§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,7 +15,7 @@ use crate::matcher::HookMatcher;
|
||||
#[derive(Debug)]
|
||||
pub struct HooksMap {
|
||||
pub events: HashMap<HookEventName, Vec<MatcherGroup>>,
|
||||
/// Event names present in the JSON but not recognized by Grok.
|
||||
/// Event names present in the JSON but not recognized by Kigi.
|
||||
pub skipped_events: Vec<String>,
|
||||
}
|
||||
|
||||
|
||||
@@ -650,7 +650,7 @@ mod tests {
|
||||
fn load_from_settings_file_no_hooks_key() {
|
||||
let dir = tempfile::tempdir().unwrap();
|
||||
let settings = dir.path().join("settings.json");
|
||||
std::fs::write(&settings, r#"{"theme": "dark", "model": "grok-3"}"#).unwrap();
|
||||
std::fs::write(&settings, r#"{"theme": "dark", "model": "kigi-3"}"#).unwrap();
|
||||
|
||||
let (registry, errors) =
|
||||
load_hooks_from_sources(&[HookSource::SettingsFile(&settings)], &[]);
|
||||
|
||||
@@ -20,7 +20,7 @@ pub struct PreToolUseResult {
|
||||
/// Hook failures (timeouts, crashes, command-not-found, env-var
|
||||
/// pre-spawn refusals, malformed output) are **fail-open**: the failure
|
||||
/// is logged and surfaced in the per-hook results for the UI scrollback,
|
||||
/// but the tool call continues as if the hook had allowed it. Grok
|
||||
/// but the tool call continues as if the hook had allowed it. Kigi
|
||||
/// runs in protected environments where induced-failure bypass of
|
||||
/// security hooks is not part of the threat model; the previous
|
||||
/// fail-closed posture over-blocked innocent tool calls when
|
||||
|
||||
@@ -63,7 +63,7 @@ use std::collections::HashMap;
|
||||
/// "Apple logo" PUA char) plus a long magic ASCII prefix. The full
|
||||
/// sentinel string adds 128 bits of per-call entropy as a hex suffix
|
||||
/// followed by another `U+F8FF` char.
|
||||
const SENTINEL_PREFIX: &str = "\u{f8ff}__GROK_HOOKS_MASK_";
|
||||
const SENTINEL_PREFIX: &str = "\u{f8ff}__KIGI_HOOKS_MASK_";
|
||||
const SENTINEL_SUFFIX: &str = "__\u{f8ff}";
|
||||
|
||||
/// Build a per-call sentinel string used to hide modifier-form
|
||||
@@ -704,7 +704,7 @@ mod tests {
|
||||
}
|
||||
|
||||
/// An earlier sentinel was a fixed string
|
||||
/// `"\u{f8ff}__GROK_HOOKS_MASK__\u{f8ff}"`. A user-supplied
|
||||
/// `"\u{f8ff}__KIGI_HOOKS_MASK__\u{f8ff}"`. A user-supplied
|
||||
/// `extra_env` value containing that exact byte sequence would
|
||||
/// have been silently rewritten to `${` by the unmask step. The
|
||||
/// per-call randomized sentinel removes this hazard. This
|
||||
@@ -713,7 +713,7 @@ mod tests {
|
||||
/// though the input also references that variable through `${VAL}`.
|
||||
#[test]
|
||||
fn expand_preserves_pre_existing_legacy_fixed_sentinel_in_extra() {
|
||||
let legacy_sentinel = "\u{f8ff}__GROK_HOOKS_MASK__\u{f8ff}";
|
||||
let legacy_sentinel = "\u{f8ff}__KIGI_HOOKS_MASK__\u{f8ff}";
|
||||
let mut extra = HashMap::new();
|
||||
// Value embeds the legacy sentinel followed by what would
|
||||
// have been parsed as an identifier+brace if the unmask
|
||||
|
||||
@@ -529,7 +529,7 @@ mod tests {
|
||||
prompt_id: None,
|
||||
payload: HookPayload::SessionStart {
|
||||
source: "new".into(),
|
||||
model_id: Some("grok-3".into()),
|
||||
model_id: Some("kigi-3".into()),
|
||||
agent_type: None,
|
||||
},
|
||||
};
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
//! # kigi-hooks
|
||||
//!
|
||||
//! Runtime hook system for Grok — file-based discovery, command execution,
|
||||
//! Runtime hook system for Kigi — file-based discovery, command execution,
|
||||
//! and policy enforcement.
|
||||
//!
|
||||
//! ## Overview
|
||||
//!
|
||||
//! This crate provides a minimal hooks system for Grok. Hooks are discovered
|
||||
//! This crate provides a minimal hooks system for Kigi. Hooks are discovered
|
||||
//! from dedicated directories (`~/.kigi/hooks/` and `<git-worktree-root>/.kigi/hooks/`),
|
||||
//! defined in JSON files (compatible settings format), and executed as child processes.
|
||||
//!
|
||||
|
||||
@@ -6,9 +6,9 @@ use regex::Regex;
|
||||
///
|
||||
/// - an empty pattern or `"*"` matches every tool;
|
||||
/// - a "simple" pattern (only `[A-Za-z0-9_|]`, i.e. a plain name or `|`-list) is an
|
||||
/// **exact** match against each name (after external→Grok alias expansion), NOT a regex;
|
||||
/// **exact** match against each name (after external→Kigi alias expansion), NOT a regex;
|
||||
/// - anything else is an **unanchored** regex (also tested against the tool's external
|
||||
/// alias names, so e.g. `^Bash$` matches the Grok tool `run_terminal_command`).
|
||||
/// alias names, so e.g. `^Bash$` matches the Kigi tool `run_terminal_command`).
|
||||
///
|
||||
/// The simple-vs-regex split is deliberate: it avoids anchoring a `|`-alternation (a
|
||||
/// naive `^a|b|c$` anchors only the first/last term and silently over-matches). Whitespace
|
||||
@@ -61,8 +61,8 @@ fn is_simple_form(pattern: &str) -> bool {
|
||||
}
|
||||
|
||||
/// Expand a simple-form pattern into the exact set of names it matches: each `|`-term
|
||||
/// plus any Grok tool names that term aliases (so `"Bash"` also matches
|
||||
/// `run_terminal_command`), per the shared external-name to Grok registry in
|
||||
/// plus any Kigi tool names that term aliases (so `"Bash"` also matches
|
||||
/// `run_terminal_command`), per the shared external-name to Kigi registry in
|
||||
/// `kigi-tools`. Empty terms and duplicates are dropped.
|
||||
fn exact_names(pattern: &str) -> Vec<String> {
|
||||
let mut names: Vec<String> = Vec::new();
|
||||
@@ -155,22 +155,22 @@ mod tests {
|
||||
// ── External tool-name aliases ────────────────────────────────
|
||||
|
||||
#[test]
|
||||
fn claude_bash_matches_grok_tool() {
|
||||
fn claude_bash_matches_kigi_tool() {
|
||||
let m = HookMatcher::new("Bash").unwrap();
|
||||
assert!(m.is_match("Bash")); // external alias name
|
||||
assert!(m.is_match("run_terminal_command")); // Grok name
|
||||
assert!(m.is_match("run_terminal_command")); // Kigi name
|
||||
assert!(!m.is_match("read_file"));
|
||||
// Bug-fix regression: exact, not prefix.
|
||||
assert!(!m.is_match("run_terminal_command_v2"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claude_edit_write_matches_grok_tool_exactly() {
|
||||
fn claude_edit_write_matches_kigi_tool_exactly() {
|
||||
let m = HookMatcher::new("Edit|Write").unwrap();
|
||||
assert!(m.is_match("Edit"));
|
||||
assert!(m.is_match("Write"));
|
||||
assert!(m.is_match("search_replace")); // Grok equivalent
|
||||
assert!(m.is_match("hashline_edit")); // second Grok alias
|
||||
assert!(m.is_match("search_replace")); // Kigi equivalent
|
||||
assert!(m.is_match("hashline_edit")); // second Kigi alias
|
||||
assert!(!m.is_match("read_file"));
|
||||
// The old anchoring bug matched these; the exact-list mode must not.
|
||||
assert!(!m.is_match("Editorial"));
|
||||
@@ -178,7 +178,7 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn claude_read_matches_grok_tool() {
|
||||
fn claude_read_matches_kigi_tool() {
|
||||
let m = HookMatcher::new("Read").unwrap();
|
||||
assert!(m.is_match("Read"));
|
||||
assert!(m.is_match("read_file"));
|
||||
@@ -186,8 +186,8 @@ mod tests {
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn regex_against_claude_alias_matches_grok_tool() {
|
||||
// A regex written against an external alias still matches the Grok tool
|
||||
fn regex_against_claude_alias_matches_kigi_tool() {
|
||||
// A regex written against an external alias still matches the Kigi tool
|
||||
// (legacy alias-name expansion).
|
||||
let m = HookMatcher::new("^Bash$").unwrap();
|
||||
assert!(m.is_match("run_terminal_command"));
|
||||
|
||||
@@ -87,7 +87,7 @@ pub async fn run_command_hook(
|
||||
let mut cmd = if is_shell_command {
|
||||
// Refuse to spawn when the command interpolates an env var that
|
||||
// we can't resolve from any of: the runner's always-set vars, the
|
||||
// per-hook extra_env (plugin vars), or Grok's own process env. The
|
||||
// per-hook extra_env (plugin vars), or Kigi's own process env. The
|
||||
// alternative is letting sh expand the var to empty -- which then
|
||||
// produces a broken command, exits 127, and (for PreToolUse hooks)
|
||||
// fails closed with an opaque "exit code 127" reason. Catching it
|
||||
@@ -287,7 +287,7 @@ pub(crate) const RUNNER_ALWAYS_SET_ENV: &[&str] = &[
|
||||
/// * the runner's always-set env vars (see [`RUNNER_ALWAYS_SET_ENV`]),
|
||||
/// * the per-hook `extra_env` map (set by the plugin adapter for plugin
|
||||
/// hooks),
|
||||
/// * the Grok process's own environment (which is inherited by the child),
|
||||
/// * the Kigi process's own environment (which is inherited by the child),
|
||||
/// * local shell assignments inside the command itself (e.g. an
|
||||
/// `INPUT=$(cat)` earlier in the string defines `INPUT` for the rest of
|
||||
/// the command).
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::path::{Path, PathBuf};
|
||||
// exist only to migrate prior grants out of the legacy file.
|
||||
|
||||
/// Path to the legacy project-hook trust file
|
||||
/// (`<user_kigi_home>/trusted-hook-projects`), or `None` when no user grok home
|
||||
/// (`<user_kigi_home>/trusted-hook-projects`), or `None` when no user kigi home
|
||||
/// resolves. Retained only for the one-time migration into folder-trust.
|
||||
pub fn legacy_trust_file_path() -> Option<PathBuf> {
|
||||
Some(kigi_config::user_kigi_home()?.join("trusted-hook-projects"))
|
||||
@@ -59,7 +59,7 @@ fn is_hook_disabled_with_file(hook_name: &str, file: &Path) -> bool {
|
||||
/// Disable a hook by name. Adds to .
|
||||
pub fn disable_hook(hook_name: &str) -> Result<(), String> {
|
||||
let file = disabled_hooks_file_path()
|
||||
.ok_or_else(|| "no user grok home (set $KIGI_SHARE_DIR or $HOME)".to_string())?;
|
||||
.ok_or_else(|| "no user kigi home (set $KIGI_SHARE_DIR or $HOME)".to_string())?;
|
||||
disable_hook_with_file(hook_name, &file)
|
||||
}
|
||||
|
||||
@@ -122,7 +122,7 @@ fn enable_hook_with_file(hook_name: &str, file: &Path) -> Result<bool, String> {
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// Returns the path to `$KIGI_SHARE_DIR/disabled-hooks`, or `None` when no user grok
|
||||
/// Returns the path to `$KIGI_SHARE_DIR/disabled-hooks`, or `None` when no user kigi
|
||||
/// home resolves.
|
||||
fn disabled_hooks_file_path() -> Option<PathBuf> {
|
||||
Some(kigi_config::user_kigi_home()?.join("disabled-hooks"))
|
||||
@@ -134,9 +134,9 @@ mod tests {
|
||||
|
||||
/// Each test creates its own legacy file in its own temp dir -- no shared state.
|
||||
fn trust_file_in(dir: &Path) -> PathBuf {
|
||||
let grok_dir = dir.join(".kigi");
|
||||
std::fs::create_dir_all(&grok_dir).unwrap();
|
||||
grok_dir.join("trusted-hook-projects")
|
||||
let kigi_dir = dir.join(".kigi");
|
||||
std::fs::create_dir_all(&kigi_dir).unwrap();
|
||||
kigi_dir.join("trusted-hook-projects")
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user