§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
+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,