§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:
@@ -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
@@ -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());
|
||||
|
||||
@@ -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
@@ -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,4 +1,4 @@
|
||||
const TARGET: &str = "xai_grok_instrumentation";
|
||||
const TARGET: &str = "xai_kigi_instrumentation";
|
||||
|
||||
pub struct TimingGuard {
|
||||
name: &'static str,
|
||||
|
||||
Reference in New Issue
Block a user