§9 acceptance: grep-zero sweep — every internal x.ai/grok identifier renamed

The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok'
crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party
license archives, README provenance, and the required 'Based on Grok
Build Open Source' attribution, now sourced from version_attribution.txt).

Wire-visible renames (both sides in this repo, changed in lockstep):
- Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode).
- Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* /
  _kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps
  a read-side alias for the legacy '_x.ai/session/update' method so
  existing updates.jsonl histories load; writes emit only the new name
  (both directions test-pinned).
- Agent types grok-build* → kigi* with a documented legacy-prefix alias
  at resolution time so persisted sessions keep resolving.
- ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case
  kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build
  implementation dirs renamed to kigi*.
- x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes
  grokday/groknight → kigiday/kiginight (old persisted values fall back
  to the default theme), web_fetch allowlist xAI hosts → kimi.com +
  moonshot platforms, changelog CDN → this repo, grok-build changelog
  archives deleted.
- BYOK default endpoint removed: [endpoints] api_base_url is now truly
  optional with NO default — consumers fail fast with the flag name when
  unset (no silent x.ai egress). Mock harnesses inject it explicitly.
- System-prompt identity fixed: 'released by xAI' → 'an unofficial
  community CLI for Kimi' (template + regenerated encrypted form).

Also repaired pre-existing grok-era test debt found by the sweep: the
stale trace_classify default-model pin, the grok-pager UA label test,
pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY
test could previously reach the real api.moonshot.cn), and the outdated
oauth fixture scope key.

Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings);
FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed;
deny advisories ok.
This commit is contained in:
2026-07-18 02:48:46 -04:00
parent 86e3724310
commit 6f31415ed6
1056 changed files with 8410 additions and 18307 deletions
+2 -2
View File
@@ -234,7 +234,7 @@ Agent definitions are discovered from multiple locations with priority:
2. **User-level**: `~/.kigi/agents/*.md`
3. **Compat paths** (lowest priority): additional vendor agent
directories under the user home (when enabled)
4. **Built-in**: `default_grok_build()`, `browser_use()`
4. **Built-in**: `default_kigi()`, `browser_use()`
Name-based dedup ensures the highest-priority definition wins. For
example, a project `.kigi/agents/code-reviewer.md` shadows a
@@ -275,7 +275,7 @@ user-level definition with the same name.
| Name | Prompt Mode | Description |
|---|---|---|
| `grok-build` | extend | Default agent for software engineering tasks |
| `kigi` | extend | Default agent for software engineering tasks |
| `browser-use` | full | Web browsing and interaction agent |
## Error Handling
+78 -92
View File
@@ -15,8 +15,8 @@ use kigi_tools::types::tool::ToolKind;
use std::collections::HashMap;
use std::path::PathBuf;
use std::sync::Arc;
/// The Grok [`ToolKind`] a vendor-compat `tools:` allowlist entry resolves to, so
/// a plugin's upstream allowlist still binds. Backed by the shared vendor-to-Grok
/// The Kigi [`ToolKind`] a vendor-compat `tools:` allowlist entry resolves to, so
/// a plugin's upstream allowlist still binds. Backed by the shared vendor-to-Kigi
/// tool registry in `kigi-tools` (also used by the hook matcher).
fn claude_tool_kind(name: &str) -> Option<ToolKind> {
kigi_tools::types::kind_for(name)
@@ -55,7 +55,7 @@ pub struct AgentBuilder {
notification_handle: ToolNotificationHandle,
owner_session_id: Option<String>,
parent_scheduler_handle:
Option<kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle>,
Option<kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle>,
/// The agent definition — set via from_definition() or built up
/// via individual with_*() calls.
definition: Option<AgentDefinition>,
@@ -91,10 +91,10 @@ pub struct AgentBuilder {
/// tools for execution by the agentic sampler, instead of being
/// registered as local Function tools.
backend_search: bool,
web_fetch_config: kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig,
web_fetch_config: kigi_tools::implementations::kigi::web_fetch::WebFetchConfig,
lsp: Option<std::sync::Arc<dyn kigi_tools::implementations::lsp::LspBackend>>,
app_builder_deployer_config:
kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig,
kigi_tools::implementations::kigi::deploy_app::AppBuilderDeployerConfig,
write_file_enabled: bool,
subagents_enabled: bool,
ask_user_question_enabled: bool,
@@ -135,27 +135,21 @@ pub struct AgentBuilder {
/// Ensure plan mode tools (`enter_plan_mode`, `exit_plan_mode`,
/// `ask_user_question`) are present in the tool config.
fn ensure_plan_mode_tools(tool_config: &mut kigi_tools::registry::types::ToolServerConfig) {
use kigi_tools::implementations::grok_build;
use kigi_tools::implementations::kigi;
let existing: std::collections::HashSet<&str> =
tool_config.tools.iter().map(|tc| tc.id.as_str()).collect();
let missing_enter = !existing.contains("GrokBuild:enter_plan_mode");
let missing_exit = !existing.contains("GrokBuild:exit_plan_mode");
let missing_ask = !existing.contains("GrokBuild:ask_user_question");
let missing_enter = !existing.contains("Kigi:enter_plan_mode");
let missing_exit = !existing.contains("Kigi:exit_plan_mode");
let missing_ask = !existing.contains("Kigi:ask_user_question");
drop(existing);
if missing_enter {
tool_config
.tools
.push((&grok_build::EnterPlanModeTool).into());
tool_config.tools.push((&kigi::EnterPlanModeTool).into());
}
if missing_exit {
tool_config
.tools
.push((&grok_build::ExitPlanModeTool).into());
tool_config.tools.push((&kigi::ExitPlanModeTool).into());
}
if missing_ask {
tool_config
.tools
.push((&grok_build::AskUserQuestionTool).into());
tool_config.tools.push((&kigi::AskUserQuestionTool).into());
}
}
/// Merge a shell-resolved params map into every matching tool's
@@ -401,7 +395,7 @@ impl AgentBuilder {
/// Share the parent's scheduler handle so scheduled tasks survive subagent exit.
pub fn with_parent_scheduler_handle(
mut self,
handle: kigi_tools::implementations::grok_build::scheduler::types::SchedulerHandle,
handle: kigi_tools::implementations::kigi::scheduler::types::SchedulerHandle,
) -> Self {
self.parent_scheduler_handle = Some(handle);
self
@@ -435,7 +429,7 @@ impl AgentBuilder {
/// `KIGI_WEB_FETCH` env var.
pub fn with_web_fetch_config(
mut self,
config: kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig,
config: kigi_tools::implementations::kigi::web_fetch::WebFetchConfig,
) -> Self {
self.web_fetch_config = config;
self
@@ -450,7 +444,7 @@ impl AgentBuilder {
/// Set the deploy service configuration.
pub fn with_app_builder_deployer_config(
mut self,
config: kigi_tools::implementations::grok_build::deploy_app::AppBuilderDeployerConfig,
config: kigi_tools::implementations::kigi::deploy_app::AppBuilderDeployerConfig,
) -> Self {
self.app_builder_deployer_config = config;
self
@@ -501,14 +495,14 @@ impl AgentBuilder {
self.subagents_enabled = enabled;
self
}
/// Set public model slugs advertised in the GrokBuild Task description.
/// Set public model slugs advertised in the Kigi Task description.
pub fn with_task_model_slugs(mut self, slugs: Vec<String>) -> Self {
self.task_model_slugs = slugs;
self
}
/// Enable or disable the `ask_user_question` tool.
///
/// When disabled, `GrokBuild:ask_user_question` is stripped from the
/// When disabled, `Kigi:ask_user_question` is stripped from the
/// agent's tool config after `ensure_plan_mode_tools` injection, so
/// the model cannot ask the user structured questions regardless of
/// which built-in profile is in use. Driven by the shell's resolved gate
@@ -537,7 +531,7 @@ impl AgentBuilder {
}
/// Set the skills config (custom paths, ignore globs) from config.toml.
/// Without this, only auto-discovered skills (cwd/.kigi/skills, ~/.kigi/skills)
/// are included — custom paths added via `x.ai/skills/add` would be ignored.
/// are included — custom paths added via `kigi/skills/add` would be ignored.
pub fn with_skills_config(mut self, config: crate::prompt::skills::SkillsConfig) -> Self {
self.skills_config = config;
self
@@ -585,7 +579,7 @@ impl AgentBuilder {
if let Some(ref def) = self.definition {
return def.clone();
}
let mut def = AgentDefinition::default_grok_build();
let mut def = AgentDefinition::default_kigi();
if let Some(ref name) = self.name {
def.name = name.clone();
}
@@ -668,17 +662,17 @@ impl AgentBuilder {
.push((&memory::get_tool::MemoryGetImpl).into());
}
if self.web_search_config.is_enabled() {
use kigi_tools::implementations::grok_build;
tool_config.tools.push((&grok_build::WebSearchTool).into());
use kigi_tools::implementations::kigi;
tool_config.tools.push((&kigi::WebSearchTool).into());
}
if self.web_fetch_config.is_enabled() {
use kigi_tools::implementations::grok_build;
tool_config.tools.push((&grok_build::WebFetchTool).into());
use kigi_tools::implementations::kigi;
tool_config.tools.push((&kigi::WebFetchTool).into());
}
if self.lsp.is_some() {
tool_config
.tools
.push((&kigi_tools::implementations::grok_build::LspTool).into());
.push((&kigi_tools::implementations::kigi::LspTool).into());
}
let has_write_tool = tool_config
.tools
@@ -692,13 +686,13 @@ impl AgentBuilder {
ensure_plan_mode_tools(&mut tool_config);
}
if self.memory_backend.is_none() {
let grok_build_ns = kigi_tools::types::tool::ToolNamespace::GrokBuild.to_string();
let kigi_ns = kigi_tools::types::tool::ToolNamespace::Kigi.to_string();
let mem_search_id = format!(
"{grok_build_ns}:{}",
"{kigi_ns}:{}",
kigi_tools::implementations::memory::MEMORY_SEARCH_TOOL_NAME
);
let mem_get_id = format!(
"{grok_build_ns}:{}",
"{kigi_ns}:{}",
kigi_tools::implementations::memory::MEMORY_GET_TOOL_NAME
);
tool_config
@@ -708,13 +702,13 @@ impl AgentBuilder {
if !self.ask_user_question_enabled {
let ask_user_id = format!(
"{}:ask_user_question",
kigi_tools::types::tool::ToolNamespace::GrokBuild,
kigi_tools::types::tool::ToolNamespace::Kigi,
);
tool_config.tools.retain(|tc| tc.id != ask_user_id);
}
let task_tool_id = format!(
"{}:{}",
kigi_tools::types::tool::ToolNamespace::GrokBuild,
kigi_tools::types::tool::ToolNamespace::Kigi,
"task"
);
let mut task_stripped = false;
@@ -762,8 +756,8 @@ impl AgentBuilder {
.unwrap_or(true))
})
};
if !has_satisfier(ToolNamespace::GrokBuild, "run_terminal_cmd", true)
&& !has_satisfier(ToolNamespace::GrokBuildConcise, "run_terminal_cmd", true)
if !has_satisfier(ToolNamespace::Kigi, "run_terminal_cmd", true)
&& !has_satisfier(ToolNamespace::KigiConcise, "run_terminal_cmd", true)
&& !has_satisfier(ToolNamespace::OpenCode, "bash", false)
{
let lifecycle = ["get_task_output", "wait_tasks", "kill_task"];
@@ -772,30 +766,22 @@ impl AgentBuilder {
.retain(|tc| !lifecycle.contains(&short_tool_name(&tc.id)));
}
}
if let kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig::Enabled {
ref params,
} = self.web_fetch_config
if let kigi_tools::implementations::kigi::web_fetch::WebFetchConfig::Enabled { ref params } =
self.web_fetch_config
&& let Ok(params_value) = serde_json::to_value(params)
&& let Some(obj) = params_value.as_object()
{
merge_tool_params(&mut tool_config, &["GrokBuild:web_fetch"], obj);
merge_tool_params(&mut tool_config, &["Kigi:web_fetch"], obj);
}
if let Some(ref bash_params) = self.bash_params_json {
merge_tool_params(
&mut tool_config,
&[
"GrokBuild:run_terminal_cmd",
"GrokBuildConcise:run_terminal_cmd",
],
&["Kigi:run_terminal_cmd", "KigiConcise:run_terminal_cmd"],
bash_params,
);
}
if let Some(ref ask_params) = self.ask_user_question_params_json {
merge_tool_params(
&mut tool_config,
&["GrokBuild:ask_user_question"],
ask_params,
);
merge_tool_params(&mut tool_config, &["Kigi:ask_user_question"], ask_params);
}
if !definition.disallowed_tools.is_empty() {
let before: std::collections::HashSet<String> =
@@ -878,7 +864,7 @@ impl AgentBuilder {
tracing::warn!(
agent = % definition.name, unresolved = ? unresolved, allowed = ?
definition.tools,
"tools allowlist had unmappable entries; keeping full grok toolset"
"tools allowlist had unmappable entries; keeping full kigi toolset"
);
}
}
@@ -1419,7 +1405,7 @@ mod tests {
.contains("If the user does not explicitly request a model, omit `${{ params.task.model }}` to inherit the parent model.")
);
assert!(!desc.contains("Available model slugs:"));
assert!(!desc.contains(concat!("grok", " models")));
assert!(!desc.contains(concat!("kigi", " models")));
}
#[test]
fn build_task_description_handles_empty_model_catalog() {
@@ -1431,7 +1417,7 @@ mod tests {
let desc = build_task_description(&subagents, &[]);
assert!(desc.contains("No explicit model slugs are currently available."));
assert!(desc.contains("Omit `${{ params.task.model }}` to inherit the parent model."));
assert!(!desc.contains(concat!("grok", " models")));
assert!(!desc.contains(concat!("kigi", " models")));
}
#[test]
fn task_model_guidance_resolves_model_param_override() {
@@ -1525,62 +1511,62 @@ mod tests {
}
let cases: &[PagerFlagCase] = &[
PagerFlagCase {
label: "grok-build / subagents+ask_user",
profile: AgentDefinition::default_grok_build,
label: "kigi / subagents+ask_user",
profile: AgentDefinition::default_kigi,
subagents: true,
ask_user: true,
},
PagerFlagCase {
label: "grok-build / subagents / no-ask-user",
profile: AgentDefinition::default_grok_build,
label: "kigi / subagents / no-ask-user",
profile: AgentDefinition::default_kigi,
subagents: true,
ask_user: false,
},
PagerFlagCase {
label: "grok-build / no-subagents / ask_user",
profile: AgentDefinition::default_grok_build,
label: "kigi / no-subagents / ask_user",
profile: AgentDefinition::default_kigi,
subagents: false,
ask_user: true,
},
PagerFlagCase {
label: "grok-build / no-subagents / no-ask-user",
profile: AgentDefinition::default_grok_build,
label: "kigi / no-subagents / no-ask-user",
profile: AgentDefinition::default_kigi,
subagents: false,
ask_user: false,
},
PagerFlagCase {
label: "grok-build-ask-user / subagents",
profile: AgentDefinition::grok_build_ask_user,
label: "kigi-ask-user / subagents",
profile: AgentDefinition::kigi_ask_user,
subagents: true,
ask_user: true,
},
PagerFlagCase {
label: "grok-build-ask-user / no-subagents",
profile: AgentDefinition::grok_build_ask_user,
label: "kigi-ask-user / no-subagents",
profile: AgentDefinition::kigi_ask_user,
subagents: false,
ask_user: true,
},
PagerFlagCase {
label: "grok-build-plan",
profile: AgentDefinition::grok_build_plan,
label: "kigi-plan",
profile: AgentDefinition::kigi_plan,
subagents: true,
ask_user: true,
},
PagerFlagCase {
label: "grok-build-plan / no-ask-user",
profile: AgentDefinition::grok_build_plan,
label: "kigi-plan / no-ask-user",
profile: AgentDefinition::kigi_plan,
subagents: true,
ask_user: false,
},
PagerFlagCase {
label: "grok-build-plan-no-subagents",
profile: AgentDefinition::grok_build_plan_no_subagents,
label: "kigi-plan-no-subagents",
profile: AgentDefinition::kigi_plan_no_subagents,
subagents: false,
ask_user: true,
},
PagerFlagCase {
label: "grok-build-plan-no-subagents / no-ask-user",
profile: AgentDefinition::grok_build_plan_no_subagents,
label: "kigi-plan-no-subagents / no-ask-user",
profile: AgentDefinition::kigi_plan_no_subagents,
subagents: false,
ask_user: false,
},
@@ -1629,7 +1615,7 @@ mod tests {
async fn curated_empty_toolset_fails_agent_build() {
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::notification::ToolNotificationHandle;
let mut profile = crate::config::AgentDefinition::default_grok_build();
let mut profile = crate::config::AgentDefinition::default_kigi();
profile.tool_config = Default::default();
profile.inject_default_tools = false;
let result = AgentBuilder::new(
@@ -1657,16 +1643,16 @@ mod tests {
#[tokio::test]
async fn plan_mode_injected_ask_user_question_receives_params() {
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::implementations::grok_build::ask_user_question::AskUserQuestionParams;
use kigi_tools::implementations::kigi::ask_user_question::AskUserQuestionParams;
use kigi_tools::notification::ToolNotificationHandle;
use kigi_tools::types::resources::Params;
let profile = crate::config::AgentDefinition::default_grok_build();
let profile = crate::config::AgentDefinition::default_kigi();
assert!(
!profile
.tool_config
.tools
.iter()
.any(|tc| tc.id == "GrokBuild:ask_user_question"),
.any(|tc| tc.id == "Kigi:ask_user_question"),
"test premise: the profile must not pre-declare ask_user_question"
);
let mut params = serde_json::Map::new();
@@ -1693,7 +1679,7 @@ mod tests {
async fn build_with_tools(tools: Vec<String>, disallowed: Vec<String>) -> crate::agent::Agent {
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::notification::ToolNotificationHandle;
let mut def = crate::config::AgentDefinition::default_grok_build();
let mut def = crate::config::AgentDefinition::default_kigi();
def.tools = tools;
def.disallowed_tools = disallowed;
AgentBuilder::new(
@@ -1714,7 +1700,7 @@ mod tests {
) -> Vec<String> {
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::notification::ToolNotificationHandle;
let mut def = crate::config::AgentDefinition::default_grok_build();
let mut def = crate::config::AgentDefinition::default_kigi();
def.tools = own_tools;
def.session_tools_allowlist = Some(session_allow);
let agent = AgentBuilder::new(
@@ -1791,7 +1777,7 @@ mod tests {
let mut def = crate::config::AgentDefinition::general_purpose();
assert!(def.session_tools_allowed("read_file"));
def.session_tools_allowlist = Some(vec!["read_file".into()]);
assert!(def.session_tools_allowed("GrokBuild:read_file"));
assert!(def.session_tools_allowed("Kigi:read_file"));
assert!(!def.session_tools_allowed("grep"));
def.session_tools_denylist = Some(vec!["read_file".into()]);
assert!(!def.session_tools_allowed("read_file"));
@@ -1848,7 +1834,7 @@ mod tests {
assert_eq!(agent.definition().allowed_subagent_types, None);
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::notification::ToolNotificationHandle;
let mut def = crate::config::AgentDefinition::default_grok_build();
let mut def = crate::config::AgentDefinition::default_kigi();
def.disallowed_tools = vec!["Agent".into()];
let agent = AgentBuilder::new(
std::env::temp_dir(),
@@ -1864,10 +1850,10 @@ mod tests {
#[tokio::test]
async fn spawning_blocked_disables_all_background_bash_modes() {
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::implementations::grok_build::bash::BashParams;
use kigi_tools::implementations::kigi::bash::BashParams;
use kigi_tools::notification::ToolNotificationHandle;
use kigi_tools::types::resources::Params;
let mut definition = crate::config::AgentDefinition::default_grok_build();
let mut definition = crate::config::AgentDefinition::default_kigi();
definition.tools = vec!["run_terminal_cmd".into()];
let bash_params = serde_json::json!(
{ "max_timeout_secs" : 36_000.0, "auto_background_on_timeout" : true,
@@ -1907,10 +1893,10 @@ mod tests {
Some(vec!["worker".into()])
);
}
/// Compat allowlist names (`Read`, `Bash`, `Grep`) map to their Grok
/// Compat allowlist names (`Read`, `Bash`, `Grep`) map to their Kigi
/// equivalents by `ToolKind` — a real restricted toolset, not zero tools.
#[tokio::test]
async fn claude_tool_names_map_to_grok_equivalents() {
async fn claude_tool_names_map_to_kigi_equivalents() {
let tools = vec!["Read".into(), "Bash".into(), "Grep".into()];
let agent = build_with_tools(tools, vec![]).await;
let names: Vec<String> = agent
@@ -1936,7 +1922,7 @@ mod tests {
"Edit must be excluded by the allowlist; got: {names:?}"
);
}
/// Shell, LSP, ask, and task-lifecycle tool names resolve to their grok
/// Shell, LSP, ask, and task-lifecycle tool names resolve to their kigi
/// `ToolKind`, so those allowlists are honored instead of failing open.
#[test]
fn shell_lsp_ask_and_task_tool_names_map() {
@@ -2045,7 +2031,7 @@ mod tests {
);
}
/// A restrictive allowlist must never strip MCP access. Compat allowlists
/// treat `mcp__*` as always-on, so grok keeps the MCP meta-tools
/// treat `mcp__*` as always-on, so kigi keeps the MCP meta-tools
/// (`search_tool` / `use_tool`) regardless of what the allowlist names.
#[tokio::test]
async fn restrictive_allowlist_keeps_mcp_access() {
@@ -2098,10 +2084,10 @@ mod tests {
#[tokio::test]
async fn requested_enabled_web_tools_survive_allowlist() {
use kigi_tools::computer::local::LocalTerminalBackend;
use kigi_tools::implementations::grok_build::web_fetch::WebFetchConfig;
use kigi_tools::implementations::kigi::web_fetch::WebFetchConfig;
use kigi_tools::implementations::web_search::WebSearchConfig;
use kigi_tools::notification::ToolNotificationHandle;
let mut definition = crate::config::AgentDefinition::default_grok_build();
let mut definition = crate::config::AgentDefinition::default_kigi();
definition.tools = vec![
"read_file".into(),
"grep".into(),
@@ -2139,7 +2125,7 @@ mod tests {
assert!(!names.contains(&excluded.to_string()), "got: {names:?}");
}
}
/// grok-build toolsets have no Skill tool — skills are read from
/// kigi toolsets have no Skill tool — skills are read from
/// `SKILL.md` via `read_file` — so a compat `Skill` allowlist entry grants
/// toolset.
#[tokio::test]
@@ -2209,7 +2195,7 @@ mod tests {
"no full-toolset fallback — unlisted tools must be excluded; got: {names:?}"
);
}
/// Compat `ToolSearch` meta-tool maps to grok's `search_tool` (MCP
/// Compat `ToolSearch` meta-tool maps to kigi's `search_tool` (MCP
/// is a filter (`retain`) over a `HashSet` of kinds, not an inserter — so the
/// falling back to the full toolset.
#[tokio::test]
@@ -2247,7 +2233,7 @@ mod tests {
} else {
WebSearchConfig::Disabled
};
let mut def = crate::config::AgentDefinition::default_grok_build();
let mut def = crate::config::AgentDefinition::default_kigi();
def.disallowed_tools = disallowed_tools.iter().map(|s| s.to_string()).collect();
AgentBuilder::new(
std::env::temp_dir(),
File diff suppressed because it is too large Load Diff
+52 -53
View File
@@ -72,8 +72,8 @@ pub enum SubagentSource {
/// `visible == callable` guarantee)
/// 4. Filter: remove agents toggled off via `[subagents.toggle]`
pub fn all_subagents(cwd: &Path, toggle: &HashMap<String, bool>) -> Vec<SubagentEntry> {
let grok = kigi_config::user_kigi_home();
all_subagents_with_home(cwd, toggle, dirs::home_dir().as_deref(), grok.as_deref())
let kigi = kigi_config::user_kigi_home();
all_subagents_with_home(cwd, toggle, dirs::home_dir().as_deref(), kigi.as_deref())
}
fn all_subagents_with_home(
@@ -189,7 +189,7 @@ fn merge_subagents(
/// 4. `~/.kigi/bundled/agents/` (bundled, lowest priority)
///
/// Deduplicates by name — higher-priority definitions win.
/// User-level agent directories in priority order: user grok agents, `.claude`
/// User-level agent directories in priority order: user kigi agents, `.claude`
/// compat agents, then bundled. `.kigi` dirs resolve from `kigi_home`
/// (KIGI_SHARE_DIR-aware) plus the legacy literal `~/.kigi` when KIGI_SHARE_DIR points
/// elsewhere; `.claude` resolves from `home`.
@@ -200,7 +200,7 @@ pub(crate) fn user_agent_dirs(
// Legacy literal ~/.kigi, included only when it differs from kigi_home
// (i.e. KIGI_SHARE_DIR points elsewhere) so agents left in the old location are
// still discovered and stay consistent with scope_from_path classification.
let legacy_grok = home
let legacy_kigi = home
.map(|h| h.join(".kigi"))
.filter(|legacy| kigi_home != Some(legacy.as_path()));
@@ -208,7 +208,7 @@ pub(crate) fn user_agent_dirs(
if let Some(g) = kigi_home {
dirs.push((g.join("agents"), AgentScope::User));
}
if let Some(l) = &legacy_grok {
if let Some(l) = &legacy_kigi {
dirs.push((l.join("agents"), AgentScope::User));
}
if let Some(h) = home {
@@ -217,15 +217,15 @@ pub(crate) fn user_agent_dirs(
if let Some(g) = kigi_home {
dirs.push((g.join("bundled").join("agents"), AgentScope::Bundled));
}
if let Some(l) = &legacy_grok {
if let Some(l) = &legacy_kigi {
dirs.push((l.join("bundled").join("agents"), AgentScope::Bundled));
}
dirs
}
pub fn discover(cwd: &Path) -> Vec<AgentDefinition> {
let grok = kigi_config::user_kigi_home();
discover_with_home(cwd, dirs::home_dir().as_deref(), grok.as_deref())
let kigi = kigi_config::user_kigi_home();
discover_with_home(cwd, dirs::home_dir().as_deref(), kigi.as_deref())
}
fn discover_with_home(
@@ -251,8 +251,8 @@ fn discover_with_home(
///
/// Checks built-ins first, then user-level dirs, then bundled.
pub fn by_name(name: &str) -> Option<AgentDefinition> {
let grok = kigi_config::user_kigi_home();
by_name_with_home(name, dirs::home_dir().as_deref(), grok.as_deref())
let kigi = kigi_config::user_kigi_home();
by_name_with_home(name, dirs::home_dir().as_deref(), kigi.as_deref())
}
fn by_name_with_home(
@@ -260,8 +260,12 @@ fn by_name_with_home(
home: Option<&Path>,
kigi_home: Option<&Path>,
) -> Option<AgentDefinition> {
// Check built-ins first — type-safe via BuiltinAgentName strum enum
if let Ok(builtin) = BuiltinAgentName::from_str(name) {
// Check built-ins first — type-safe via BuiltinAgentName strum enum.
// Legacy pre-rebrand agent types (persisted in old session files) are
// mapped onto their current names first; see `canonical_agent_type`.
if let Ok(builtin) =
BuiltinAgentName::from_str(crate::config::canonical_agent_type(name).as_ref())
{
return Some(builtin.definition());
}
@@ -287,8 +291,8 @@ fn by_name_with_home(
/// Project-level `.kigi/agents/` has highest priority, then falls back
/// to built-ins, user-level, and finally bundled definitions.
pub fn by_name_in_cwd(name: &str, cwd: &Path) -> Option<AgentDefinition> {
let grok = kigi_config::user_kigi_home();
by_name_in_cwd_with_home(name, cwd, dirs::home_dir().as_deref(), grok.as_deref())
let kigi = kigi_config::user_kigi_home();
by_name_in_cwd_with_home(name, cwd, dirs::home_dir().as_deref(), kigi.as_deref())
}
fn by_name_in_cwd_with_home(
@@ -363,13 +367,13 @@ pub fn all_subagents_with_plugins(
toggle: &HashMap<String, bool>,
plugins: Option<&crate::plugins::PluginRegistry>,
) -> Vec<SubagentEntry> {
let grok = kigi_config::user_kigi_home();
let kigi = kigi_config::user_kigi_home();
all_subagents_with_plugins_and_home(
cwd,
toggle,
plugins,
dirs::home_dir().as_deref(),
grok.as_deref(),
kigi.as_deref(),
)
}
@@ -450,13 +454,13 @@ pub fn by_name_in_cwd_with_plugins(
cwd: &Path,
plugins: Option<&crate::plugins::PluginRegistry>,
) -> Option<AgentDefinition> {
let grok = kigi_config::user_kigi_home();
let kigi = kigi_config::user_kigi_home();
by_name_in_cwd_with_plugins_and_home(
name,
cwd,
plugins,
dirs::home_dir().as_deref(),
grok.as_deref(),
kigi.as_deref(),
)
}
@@ -533,7 +537,7 @@ fn by_name_in_cwd_with_plugins_and_home(
None
}
/// Expand `${CLAUDE_PLUGIN_ROOT}` / `${CLAUDE_PLUGIN_DATA}` (and the Grok
/// Expand `${CLAUDE_PLUGIN_ROOT}` / `${CLAUDE_PLUGIN_DATA}` (and the Kigi
/// aliases) in a plugin agent's body so the model receives absolute paths,
/// matching the expected load-time resolution for these variables.
fn substitute_plugin_vars(def: &mut AgentDefinition, plugin: &crate::plugins::LoadedPlugin) {
@@ -675,8 +679,8 @@ mod tests {
use crate::plugins::PluginOrigin;
match scope {
PluginScope::CliOverride => PluginOrigin::CliOverride,
PluginScope::Project => PluginOrigin::ProjectGrok,
PluginScope::User => PluginOrigin::UserGrok,
PluginScope::Project => PluginOrigin::ProjectKigi,
PluginScope::User => PluginOrigin::UserKigi,
PluginScope::ConfigPath => PluginOrigin::ConfigPath,
}
}
@@ -763,27 +767,27 @@ mod tests {
}
#[test]
fn user_agent_dirs_includes_legacy_grok_when_kigi_home_differs() {
fn user_agent_dirs_includes_legacy_kigi_when_kigi_home_differs() {
let home = Path::new("/home/u");
let grok = Path::new("/custom/grokhome");
let paths: Vec<_> = user_agent_dirs(Some(home), Some(grok))
let kigi = Path::new("/custom/kigihome");
let paths: Vec<_> = user_agent_dirs(Some(home), Some(kigi))
.into_iter()
.map(|(p, _)| p)
.collect();
assert!(paths.contains(&grok.join("agents")));
assert!(paths.contains(&kigi.join("agents")));
assert!(paths.contains(&home.join(".kigi").join("agents")));
assert!(paths.contains(&home.join(".claude").join("agents")));
assert!(paths.contains(&grok.join("bundled").join("agents")));
assert!(paths.contains(&kigi.join("bundled").join("agents")));
assert!(paths.contains(&home.join(".kigi").join("bundled").join("agents")));
}
#[test]
fn user_agent_dirs_dedups_legacy_when_kigi_home_is_dot_grok() {
fn user_agent_dirs_dedups_legacy_when_kigi_home_is_dot_kigi() {
let home = Path::new("/home/u");
let grok = home.join(".kigi");
let count = user_agent_dirs(Some(home), Some(&grok))
let kigi = home.join(".kigi");
let count = user_agent_dirs(Some(home), Some(&kigi))
.into_iter()
.filter(|(p, _)| *p == grok.join("agents"))
.filter(|(p, _)| *p == kigi.join("agents"))
.count();
assert_eq!(
count, 1,
@@ -807,10 +811,10 @@ mod tests {
}
#[test]
fn test_by_name_builtin_grok_build() {
let def = by_name("grok-build");
fn test_by_name_builtin_kigi() {
let def = by_name("kigi");
assert!(def.is_some());
assert_eq!(def.unwrap().name, "grok-build");
assert_eq!(def.unwrap().name, "kigi");
}
#[test]
@@ -1004,19 +1008,14 @@ mod tests {
let agents_dir = tmp.path().join(".kigi").join("agents");
fs::create_dir_all(&agents_dir).unwrap();
// Create a project-level "grok-build" that shadows the built-in
write_agent_file(
&agents_dir,
"grok-build.md",
"grok-build",
"Custom grok-build",
);
// Create a project-level "kigi" that shadows the built-in
write_agent_file(&agents_dir, "kigi.md", "kigi", "Custom kigi");
let def = by_name_in_cwd("grok-build", tmp.path());
let def = by_name_in_cwd("kigi", tmp.path());
assert!(def.is_some());
let def = def.unwrap();
assert_eq!(def.name, "grok-build");
assert_eq!(def.description, "Custom grok-build");
assert_eq!(def.name, "kigi");
assert_eq!(def.description, "Custom kigi");
}
#[test]
@@ -1024,10 +1023,10 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
// No .kigi/agents/ directory — should fall back to built-in
let def = by_name_in_cwd("grok-build", tmp.path());
let def = by_name_in_cwd("kigi", tmp.path());
assert!(def.is_some());
let def = def.unwrap();
assert_eq!(def.name, "grok-build");
assert_eq!(def.name, "kigi");
// Should be the built-in, not a custom one
assert_eq!(def.scope, AgentScope::BuiltIn);
}
@@ -1048,11 +1047,11 @@ mod tests {
#[test]
fn test_orchestrator_from_str_resolves() {
use std::str::FromStr;
let variant = BuiltinAgentName::from_str("grok-build-orchestrator")
.expect("from_str must resolve grok-build-orchestrator");
assert_eq!(variant, BuiltinAgentName::GrokBuildOrchestrator);
let variant = BuiltinAgentName::from_str("kigi-orchestrator")
.expect("from_str must resolve kigi-orchestrator");
assert_eq!(variant, BuiltinAgentName::KigiOrchestrator);
let def = variant.definition();
assert_eq!(def.name, "grok-build-orchestrator");
assert_eq!(def.name, "kigi-orchestrator");
assert!(
def.prompt_body.is_some(),
"orchestrator must have prompt_body"
@@ -1067,9 +1066,9 @@ mod tests {
#[test]
fn test_orchestrator_by_name_in_cwd() {
let tmp = tempfile::tempdir().unwrap();
let def = by_name_in_cwd("grok-build-orchestrator", tmp.path())
.expect("by_name_in_cwd must find grok-build-orchestrator");
assert_eq!(def.name, "grok-build-orchestrator");
let def = by_name_in_cwd("kigi-orchestrator", tmp.path())
.expect("by_name_in_cwd must find kigi-orchestrator");
assert_eq!(def.name, "kigi-orchestrator");
assert!(def.prompt_body.is_some());
}
@@ -1461,7 +1460,7 @@ mod tests {
let registry = make_plugin_registry("plugin-one", PluginScope::User, vec![]);
let plugin = registry.get("plugin-one").unwrap();
let mut def = AgentDefinition::default_grok_build();
let mut def = AgentDefinition::default_kigi();
def.prompt_body = Some("Body ${CLAUDE_PLUGIN_ROOT}/x".to_string());
def.system_prompt =
TemplateOverride::Custom("Data at ${CLAUDE_PLUGIN_DATA}/db".to_string());
+1 -1
View File
@@ -23,7 +23,7 @@ pub use compaction::CompactionPolicy;
pub use config::AgentDefinition;
pub use config::preset_names;
pub use config::toolset_for_preset;
pub use config::workspace_grok_build_toolset;
pub use config::workspace_kigi_toolset;
pub use error::AgentBuildError;
pub use prompt::context::{DEFAULT_SYSTEM_PROMPT_LABEL, PromptContext};
pub use system_reminder::ReminderPolicy;
@@ -69,11 +69,11 @@ pub enum PluginOrigin {
/// CLI `--plugin-dir`.
CliOverride,
/// Project `.kigi/plugins/`.
ProjectGrok,
ProjectKigi,
/// Project `.claude/plugins/`.
ProjectClaude,
/// `$KIGI_SHARE_DIR/plugins/`.
UserGrok,
UserKigi,
/// `~/.claude/plugins/`.
UserClaude,
/// A compat marketplace clone (project `extraKnownMarketplaces`
@@ -87,7 +87,7 @@ pub enum PluginOrigin {
/// Marketplace name from the `name@marketplace` JSON key, when present.
marketplace: Option<String>,
},
/// Grok's install registry (`~/.kigi/installed-plugins`).
/// Kigi's install registry (`~/.kigi/installed-plugins`).
MarketplaceInstall {
/// Git URL of the installed repo (None for local installs).
git_url: Option<String>,
@@ -214,10 +214,10 @@ impl DiscoveryConfig {
/// paths all resolve under `kigi_home()`, so a plugin scanned from the legacy
/// tree would appear untrusted and lose its persisted state. Keeping plugins on
/// `kigi_home()` only avoids that half-initialized state.
fn user_plugin_dirs(home: Option<&Path>, grok: Option<&Path>) -> Vec<(PathBuf, PluginOrigin)> {
fn user_plugin_dirs(home: Option<&Path>, kigi: Option<&Path>) -> Vec<(PathBuf, PluginOrigin)> {
let mut dirs = Vec::new();
if let Some(g) = grok {
dirs.push((g.join("plugins"), PluginOrigin::UserGrok));
if let Some(g) = kigi {
dirs.push((g.join("plugins"), PluginOrigin::UserKigi));
}
if let Some(h) = home {
dirs.push((h.join(".claude").join("plugins"), PluginOrigin::UserClaude));
@@ -234,7 +234,7 @@ fn project_plugins_dir_origin(plugins_dir: &Path) -> PluginOrigin {
if is_claude {
PluginOrigin::ProjectClaude
} else {
PluginOrigin::ProjectGrok
PluginOrigin::ProjectKigi
}
}
@@ -336,10 +336,10 @@ pub fn discover_plugins(
}
// 4-5. User plugins: $KIGI_SHARE_DIR/plugins, legacy ~/.kigi/plugins, ~/.claude/plugins.
// Gate the grok plugins dir on user_kigi_home() so a project's .kigi/plugins
// Gate the kigi plugins dir on user_kigi_home() so a project's .kigi/plugins
// is never scanned as user-global when no home resolves.
let grok = kigi_config::user_kigi_home();
let plugin_dirs = user_plugin_dirs(dirs::home_dir().as_deref(), grok.as_deref());
let kigi = kigi_config::user_kigi_home();
let plugin_dirs = user_plugin_dirs(dirs::home_dir().as_deref(), kigi.as_deref());
for (plugins_dir, origin) in plugin_dirs {
if plugins_dir.is_dir() {
scan_plugin_dir(
@@ -903,11 +903,11 @@ mod tests {
}
#[test]
fn user_plugin_dirs_are_grok_and_claude_only_no_legacy() {
fn user_plugin_dirs_are_kigi_and_claude_only_no_legacy() {
let home = Path::new("/home/u");
let grok = Path::new("/custom/grokhome");
let dirs = user_plugin_dirs(Some(home), Some(grok));
assert!(dirs.contains(&(grok.join("plugins"), PluginOrigin::UserGrok)));
let kigi = Path::new("/custom/kigihome");
let dirs = user_plugin_dirs(Some(home), Some(kigi));
assert!(dirs.contains(&(kigi.join("plugins"), PluginOrigin::UserKigi)));
assert!(dirs.contains(&(
home.join(".claude").join("plugins"),
PluginOrigin::UserClaude
@@ -921,7 +921,7 @@ mod tests {
}
#[test]
fn user_plugin_dirs_empty_without_home_or_grok() {
fn user_plugin_dirs_empty_without_home_or_kigi() {
assert!(user_plugin_dirs(None, None).is_empty());
}
@@ -951,10 +951,10 @@ mod tests {
}
#[test]
fn project_plugins_dir_origin_distinguishes_grok_and_claude() {
fn project_plugins_dir_origin_distinguishes_kigi_and_claude() {
assert_eq!(
project_plugins_dir_origin(Path::new("/repo/.kigi/plugins")),
PluginOrigin::ProjectGrok
PluginOrigin::ProjectKigi
);
assert_eq!(
project_plugins_dir_origin(Path::new("/repo/.claude/plugins")),
@@ -967,18 +967,18 @@ mod tests {
let tmp = tempfile::tempdir().unwrap();
// Create ~/.kigi/plugins/ structure
let grok_plugins = tmp.path().join(".kigi").join("plugins");
std::fs::create_dir_all(&grok_plugins).unwrap();
make_manifest_plugin(&grok_plugins, "user-tool");
let kigi_plugins = tmp.path().join(".kigi").join("plugins");
std::fs::create_dir_all(&kigi_plugins).unwrap();
make_manifest_plugin(&kigi_plugins, "user-tool");
// Override home dir by directly scanning
let trust = TrustStore::load_from(tmp.path().join("trust"));
let mut seen = HashSet::new();
let mut candidates = Vec::new();
scan_plugin_dir(
&grok_plugins,
&kigi_plugins,
PluginScope::User,
PluginOrigin::UserGrok,
PluginOrigin::UserKigi,
&trust,
false,
&mut seen,
@@ -1002,7 +1002,7 @@ mod tests {
scan_plugin_dir(
&plugins_dir,
PluginScope::User,
PluginOrigin::UserGrok,
PluginOrigin::UserKigi,
&trust,
false,
&mut seen,
@@ -1325,7 +1325,7 @@ mod tests {
collect_plugin(
&user_plugin,
PluginScope::User,
PluginOrigin::UserGrok,
PluginOrigin::UserKigi,
&trust,
false,
&mut seen,
@@ -1381,7 +1381,7 @@ mod tests {
collect_plugin(
&empty_dir,
PluginScope::User,
PluginOrigin::UserGrok,
PluginOrigin::UserKigi,
&trust,
false,
&mut seen,
@@ -1404,7 +1404,7 @@ mod tests {
collect_plugin(
&plugin_dir,
PluginScope::Project,
PluginOrigin::ProjectGrok,
PluginOrigin::ProjectKigi,
&trust,
false,
&mut seen,
@@ -1427,7 +1427,7 @@ mod tests {
collect_plugin(
&plugin_dir,
PluginScope::Project,
PluginOrigin::ProjectGrok,
PluginOrigin::ProjectKigi,
&trust,
true,
&mut seen,
@@ -1458,7 +1458,7 @@ mod tests {
PluginScope::CliOverride,
PluginOrigin::CliOverride,
),
(&user_dir, PluginScope::User, PluginOrigin::UserGrok),
(&user_dir, PluginScope::User, PluginOrigin::UserKigi),
(
&config_dir,
PluginScope::ConfigPath,
@@ -1505,7 +1505,7 @@ mod tests {
.find(|p| p.manifest.name == "proj-mcp")
.expect("project plugin discovered");
assert_eq!(p.scope, PluginScope::Project);
assert_eq!(p.origin, PluginOrigin::ProjectGrok);
assert_eq!(p.origin, PluginOrigin::ProjectKigi);
assert!(!p.trusted, "untrusted folder must block the project plugin");
// Trusted folder: the same plugin is allowed.
@@ -654,8 +654,8 @@ mod tests {
scope,
origin: match scope {
PluginScope::CliOverride => PluginOrigin::CliOverride,
PluginScope::Project => PluginOrigin::ProjectGrok,
PluginScope::User => PluginOrigin::UserGrok,
PluginScope::Project => PluginOrigin::ProjectKigi,
PluginScope::User => PluginOrigin::UserKigi,
PluginScope::ConfigPath => PluginOrigin::ConfigPath,
},
trusted,
@@ -40,13 +40,13 @@ impl TrustStore {
pub fn load() -> Self {
// Gate on user_kigi_home() so a project's `.kigi/trusted-plugins` is never
// read as the user trust store when neither KIGI_SHARE_DIR nor a home dir resolves.
let Some(grok) = kigi_config::user_kigi_home() else {
let Some(kigi) = kigi_config::user_kigi_home() else {
return Self {
trusted: HashSet::new(),
file_path: PathBuf::new(),
};
};
let file_path = grok.join(TRUST_FILE_NAME);
let file_path = kigi.join(TRUST_FILE_NAME);
let trusted = Self::read_trust_file(&file_path);
Self { trusted, file_path }
}
@@ -144,13 +144,13 @@ pub struct PromptContext {
/// stdio / generic-ACP).
#[serde(default)]
pub is_non_interactive: bool,
/// Identity in the primary grok-build system prompt (`You are <label>…`).
/// Identity in the primary kigi system prompt (`You are <label>…`).
/// Not the UI picker name. Defaults to [`DEFAULT_SYSTEM_PROMPT_LABEL`].
#[serde(default = "default_system_prompt_label")]
pub system_prompt_label: String,
}
/// Default identity on trim-tool-descriptions (`You are Grok released by xAI`).
pub const DEFAULT_SYSTEM_PROMPT_LABEL: &str = "Grok";
/// Default identity on trim-tool-descriptions (`You are Kigi released by xAI`).
pub const DEFAULT_SYSTEM_PROMPT_LABEL: &str = "Kigi";
fn default_system_prompt_label() -> String {
DEFAULT_SYSTEM_PROMPT_LABEL.to_string()
}
@@ -427,9 +427,9 @@ mod tests {
#[test]
fn test_placeholders_system_prompt_label_override() {
let mut ctx = test_context();
ctx.system_prompt_label = "Grok Internal".into();
ctx.system_prompt_label = "Kigi Internal".into();
let p = ctx.placeholders();
assert_eq!(p["system_prompt_label"], "Grok Internal");
assert_eq!(p["system_prompt_label"], "Kigi Internal");
}
#[test]
fn test_missing_system_prompt_label_deserializes_to_default() {
@@ -1229,7 +1229,7 @@ mod tests {
("plan", plan),
] {
assert!(
!prompt.contains("You are a Grok Build agent"),
!prompt.contains("You are a Kigi agent"),
"{name} prompt should not duplicate base template identity"
);
}
File diff suppressed because one or more lines are too long
+27 -27
View File
@@ -832,12 +832,12 @@ mod tests {
fn find_skill_paths_flat_layout() {
// Traditional flat layout: skills/<name>/SKILL.md
let tmp = tempfile::tempdir().unwrap();
let grok_dir = tmp.path().join(".kigi");
let kigi_dir = tmp.path().join(".kigi");
write_skill_md(&grok_dir.join("skills").join("alpha"), "alpha");
write_skill_md(&grok_dir.join("skills").join("beta"), "beta");
write_skill_md(&kigi_dir.join("skills").join("alpha"), "alpha");
write_skill_md(&kigi_dir.join("skills").join("beta"), "beta");
let paths = find_skill_paths(&grok_dir);
let paths = find_skill_paths(&kigi_dir);
assert_eq!(paths.len(), 2);
assert!(paths.iter().all(|p| p.file_name().unwrap() == "SKILL.md"));
}
@@ -846,13 +846,13 @@ mod tests {
fn find_skill_paths_nested_layout() {
// Nested: skills/team/infra/SKILL.md, skills/team/training/SKILL.md
let tmp = tempfile::tempdir().unwrap();
let grok_dir = tmp.path().join(".kigi");
let skills = grok_dir.join("skills");
let kigi_dir = tmp.path().join(".kigi");
let skills = kigi_dir.join("skills");
write_skill_md(&skills.join("team").join("infra"), "infra");
write_skill_md(&skills.join("team").join("training"), "training");
let paths = find_skill_paths(&grok_dir);
let paths = find_skill_paths(&kigi_dir);
assert_eq!(paths.len(), 2);
let path_strs: Vec<String> = paths.iter().map(|p| p.display().to_string()).collect();
@@ -864,8 +864,8 @@ mod tests {
fn find_skill_paths_mixed_flat_and_nested() {
// Mix of flat and nested skills
let tmp = tempfile::tempdir().unwrap();
let grok_dir = tmp.path().join(".kigi");
let skills = grok_dir.join("skills");
let kigi_dir = tmp.path().join(".kigi");
let skills = kigi_dir.join("skills");
// Flat
write_skill_md(&skills.join("top-level"), "top-level");
@@ -874,7 +874,7 @@ mod tests {
// Nested 2 levels
write_skill_md(&skills.join("org").join("team").join("deep"), "deep");
let paths = find_skill_paths(&grok_dir);
let paths = find_skill_paths(&kigi_dir);
assert_eq!(paths.len(), 3);
}
@@ -882,8 +882,8 @@ mod tests {
fn find_skill_paths_dir_without_skill_md_is_skipped() {
// A subdirectory exists but has no SKILL.md — should not appear
let tmp = tempfile::tempdir().unwrap();
let grok_dir = tmp.path().join(".kigi");
let skills = grok_dir.join("skills");
let kigi_dir = tmp.path().join(".kigi");
let skills = kigi_dir.join("skills");
write_skill_md(&skills.join("valid"), "valid");
// Create a dir with no SKILL.md
@@ -893,7 +893,7 @@ mod tests {
fs::create_dir_all(&other).unwrap();
fs::write(other.join("README.md"), "not a skill").unwrap();
let paths = find_skill_paths(&grok_dir);
let paths = find_skill_paths(&kigi_dir);
assert_eq!(paths.len(), 1);
assert!(paths[0].display().to_string().contains("valid"));
}
@@ -902,10 +902,10 @@ mod tests {
fn find_skill_paths_no_skills_dir() {
// .kigi exists but no skills/ subdirectory
let tmp = tempfile::tempdir().unwrap();
let grok_dir = tmp.path().join(".kigi");
fs::create_dir_all(&grok_dir).unwrap();
let kigi_dir = tmp.path().join(".kigi");
fs::create_dir_all(&kigi_dir).unwrap();
let paths = find_skill_paths(&grok_dir);
let paths = find_skill_paths(&kigi_dir);
assert!(paths.is_empty());
}
@@ -944,15 +944,15 @@ mod tests {
fn find_skill_paths_parent_and_child_both_have_skill_md() {
// A directory has SKILL.md and also has subdirectories with SKILL.md
let tmp = tempfile::tempdir().unwrap();
let grok_dir = tmp.path().join(".kigi");
let skills = grok_dir.join("skills");
let kigi_dir = tmp.path().join(".kigi");
let skills = kigi_dir.join("skills");
// Parent skill
write_skill_md(&skills.join("parent"), "parent-skill");
// Child skill inside parent
write_skill_md(&skills.join("parent").join("child"), "child-skill");
let paths = find_skill_paths(&grok_dir);
let paths = find_skill_paths(&kigi_dir);
assert_eq!(paths.len(), 2);
let path_strs: Vec<String> = paths.iter().map(|p| p.display().to_string()).collect();
@@ -1108,9 +1108,9 @@ mod tests {
#[test]
fn parse_model_and_effort() {
let content = "---\nname: my-skill\ndescription: test\nmodel: grok-3\neffort: high\n---\n";
let content = "---\nname: my-skill\ndescription: test\nmodel: kigi-3\neffort: high\n---\n";
let parsed = parse_skill_frontmatter(content, None).unwrap();
assert_eq!(parsed.model.as_deref(), Some("grok-3"));
assert_eq!(parsed.model.as_deref(), Some("kigi-3"));
assert_eq!(parsed.effort.as_deref(), Some("high"));
}
@@ -1251,7 +1251,7 @@ mod tests {
#[test]
fn parse_full_spec_plus_extensions() {
// Mixed agentskills.io spec fields + our extensions — all must parse.
let content = "---\nname: my-skill\ndescription: A full skill\nlicense: MIT\ncompatibility: Python 3.12+\nmetadata:\n author: test-org\n version: \"2.0\"\nallowed-tools:\n - bash\n - read_file\nargument-hint: file path\nmodel: grok-3\neffort: high\nuser-invocable: true\ndisable-model-invocation: false\n---\nBody content.\n";
let content = "---\nname: my-skill\ndescription: A full skill\nlicense: MIT\ncompatibility: Python 3.12+\nmetadata:\n author: test-org\n version: \"2.0\"\nallowed-tools:\n - bash\n - read_file\nargument-hint: file path\nmodel: kigi-3\neffort: high\nuser-invocable: true\ndisable-model-invocation: false\n---\nBody content.\n";
let parsed = parse_skill_frontmatter(content, None).unwrap();
assert_eq!(parsed.name, "my-skill");
assert_eq!(parsed.description, "A full skill");
@@ -1263,7 +1263,7 @@ mod tests {
Some(["bash".to_string(), "read_file".to_string()].as_slice())
);
assert_eq!(parsed.argument_hint.as_deref(), Some("file path"));
assert_eq!(parsed.model.as_deref(), Some("grok-3"));
assert_eq!(parsed.model.as_deref(), Some("kigi-3"));
assert_eq!(parsed.effort.as_deref(), Some("high"));
assert!(parsed.user_invocable);
assert!(!parsed.disable_model_invocation);
@@ -1575,7 +1575,7 @@ mod tests {
root: root.clone(),
canonical_root: root.clone(),
scope: PluginScope::User,
origin: crate::plugins::PluginOrigin::UserGrok,
origin: crate::plugins::PluginOrigin::UserKigi,
trusted: true,
enabled: true,
version: Some("1.0.0".to_string()),
@@ -1653,7 +1653,7 @@ mod tests {
root: root.to_path_buf(),
canonical_root: root.to_path_buf(),
scope: PluginScope::User,
origin: crate::plugins::PluginOrigin::UserGrok,
origin: crate::plugins::PluginOrigin::UserKigi,
trusted: true,
skill_dirs,
command_dirs: vec![],
@@ -2323,7 +2323,7 @@ mod tests {
root: root.clone(),
canonical_root: root,
scope: PluginScope::Project,
origin: crate::plugins::PluginOrigin::ProjectGrok,
origin: crate::plugins::PluginOrigin::ProjectKigi,
trusted: true,
enabled: true,
version: Some("1.0.0".to_string()),
@@ -2458,7 +2458,7 @@ mod tests {
"cursor must be gated off: {dirs:?}"
);
assert!(ends_with(&dirs, ".claude"), "claude must remain: {dirs:?}");
assert!(ends_with(&dirs, ".kigi"), "grok must remain: {dirs:?}");
assert!(ends_with(&dirs, ".kigi"), "kigi must remain: {dirs:?}");
}
// ── Same-scope frontmatter-name collisions (copied skill dirs) ──────
@@ -32,7 +32,7 @@ pub(crate) fn base_template() -> Zeroizing<String> {
decrypt(BASE_PROMPT_ENC, PROMPT_SEEDS[0])
}
/// The base prompt template source, exposed for `grok prompt --section template`.
/// The base prompt template source, exposed for `kigi prompt --section template`.
pub fn base_template_source() -> Zeroizing<String> {
base_template()
}
@@ -41,7 +41,7 @@ pub(crate) fn apply_patch_template() -> Zeroizing<String> {
decrypt(CODEX_PROMPT_ENC, PROMPT_SEEDS[1])
}
/// Apply-patch prompt template source, exposed for `grok prompt --section apply-patch-template`.
/// Apply-patch prompt template source, exposed for `kigi prompt --section apply-patch-template`.
pub fn apply_patch_template_source() -> Zeroizing<String> {
apply_patch_template()
}
@@ -93,7 +93,7 @@ mod tests {
);
}
/// Build a TemplateRenderer with the standard grok-build tool kinds.
/// Build a TemplateRenderer with the standard kigi tool kinds.
fn default_renderer() -> TemplateRenderer {
let tools: HashMap<ToolKind, String> = [
(ToolKind::Read, "read_file"),
@@ -5,7 +5,7 @@
//! workspace overview, optional rules / skills / MCP listings).
//!
//! `UserMessageTemplate` selects the rendering strategy:
//! - `Default` -- the legacy Grok Build prefix (built by the shell layer).
//! - `Default` -- the legacy Kigi prefix (built by the shell layer).
//! - `Custom` -- caller-supplied template string (MiniJinja, same delimiters
//! as the system prompt templates).
//!
@@ -63,7 +63,7 @@ fn normalize_git_status(status: &str) -> Option<String> {
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Default)]
#[serde(rename_all = "snake_case")]
pub enum UserMessageTemplate {
/// Legacy Grok Build prefix: `<user_info>` + optional `<git_status>`.
/// Legacy Kigi prefix: `<user_info>` + optional `<git_status>`.
/// Built directly by the shell layer; this
/// renderer returns `None` for `Default` and the caller falls back to
/// its own legacy path.
+1 -1
View File
@@ -1,4 +1,4 @@
const TARGET: &str = "xai_grok_instrumentation";
const TARGET: &str = "xai_kigi_instrumentation";
pub struct TimingGuard {
name: &'static str,
@@ -1,4 +1,4 @@
You are ${{ system_prompt_label }} released by xAI. You are ${%- if is_non_interactive %} an autonomous agent that completes software engineering tasks.${%- else %} an interactive CLI tool that helps users with software engineering tasks.${%- endif %} Your main goal is to complete the user's request, denoted within the <user_query> tag.
You are ${{ system_prompt_label }}, an unofficial community CLI for Kimi. You are ${%- if is_non_interactive %} an autonomous agent that completes software engineering tasks.${%- else %} an interactive CLI tool that helps users with software engineering tasks.${%- endif %} Your main goal is to complete the user's request, denoted within the <user_query> tag.
<action_safety>
Weigh each action by how easily it can be undone and how far its effects reach. Local, reversible work such as editing files and running tests is fine to do freely. Before executing any actions that are hard to reverse, reach shared external systems, or are otherwise risky or destructive, check with the user first.