M0: compilable skeleton — Kigi 0.1.0 fork surgery
Hard fork of xai-org/grok-build (Apache-2.0) re-targeted as Kigi, an
unofficial Kimi Code CLI community build.
Rename & identity
- 72 xai-*/xai-grok-* crates -> kigi-* (explicit: xai-grok-pager-bin ->
kigi-bin [binary `kigi`], xai-grok-pager -> kigi-tui; rest mechanical);
ptyctl, ptyctl-cli, third_party/ unchanged; proto package
xai.grok.tools.v1 -> kigi.tools.v1
- Config home ~/.kigi (KIGI_SHARE_DIR override), env prefix GROK_* ->
KIGI_*, `kigi --version` carries the unofficial-community-build notice
- clap identity, help text, startup banner, prompt templates rebranded
(templates re-encrypted)
Deletions (PRD removal list #5/#6/#7/#9/#10)
- voice input (xai-grok-voice) and all TUI wiring
- telemetry: Mixpanel client, external OTel stream, Sentry, OTLP layers,
trace/GCS/S3 upload queues (kigi-file-utils halved), workspace upload
module & dc_log, heap-profile uploader, auth-diagnostics uploader,
session-analytics halves of feedback; local zero-egress observability
preserved in new kigi-log crate (unified log, --debug firehose,
subsystem file logs, opt-in instrumentation)
- announcements (crate, remote-settings fields, TUI surfaces)
- plugin marketplace (crate, sources/browse/CTA/extensions-modal tab);
direct plugin install/uninstall/update via kigi-agent git_install kept
- relay/gateway/assets endpoints and features (agent relay, headless
relay transport, gateway bridge, LeaderEnvUrls); leader IPC socket now
~/.kigi/leader.sock + KIGI_LEADER_SOCKET, no ws-url derivation
- functional types rehomed instead of deleted: PermissionMode ->
kigi-config-types, McpInitStrategy -> kigi-mcp, PrCreationSource ->
session signals, TerminalDiagnostics -> kigi-pager-render, agent_id ->
shell util
Endpoints
- kigi-env rewritten: single production KigiEndpoints {coding_api_base_url
https://api.kimi.com/coding/v1 (KIGI_CODE_BASE_URL), oauth_host
https://auth.kimi.com (KIGI_OAUTH_HOST), update_base_url (GitHub
Releases API), upgrade_page_url}; GrokBuildEnvironment enum deleted
Toolchain & workspace hygiene
- Rust 1.97.0 pinned; edition 2024; full cargo update; git2 hoisted to
workspace at 0.21 (Option->Result API migration), quick-xml 0.41
- Root Cargo.toml hand-maintained (PRD §8.1): version 0.1.0 inherited by
all members, members sorted, unused deps pruned
- cargo-deny advisories gate (deny.toml with documented transitive
exceptions); CI workflow (check/clippy/fmt/deny/test, macOS+Linux)
- cross-crate test seams re-gated behind `test-support` cargo feature;
insta snapshot baselines renamed to the kigi_tui prefix
- clippy --workspace --all-targets: zero warnings; fmt clean
Fixes surfaced by the port
- updater probe/installer divergence (bin/kigi vs bin/grok symlink set)
- idle model-metadata refresh dead under KIGI_CODE_BASE_URL override
(new is_effective_coding_endpoint_url, loopback+override aware)
- macOS symlinked-TMPDIR fixture canonicalization (foreign_sessions,
fast-worktree); RSS measurement tests serialized via serial_test
Docs & legal (Apache §4)
- NOTICE added (upstream attribution + change statement); THIRD-PARTY
notices sustained; kigi-tools ported-code notices extended; README,
CONTRIBUTING, SECURITY, AGENTS.md rewritten
Out of scope for M0 (tracked): Kimi auth/inference (M1), search/fetch,
command parity, config import (M2), Computer Hub excision & final
brand-token sweep (M2), distribution & self-update rewrite (M3).
This commit is contained in:
@@ -0,0 +1,122 @@
|
||||
//! Test-only helpers shared across `kigi-hooks` unit + integration tests.
|
||||
//!
|
||||
//! This module is gated on `#[cfg(test)]` and is exported as `pub(crate)`
|
||||
//! so any in-crate `#[cfg(test)] mod tests` can use it. Integration tests
|
||||
//! under `tests/` cannot reach it; for those, copy or re-implement the
|
||||
//! handful of functions here that they need (the only one currently used
|
||||
//! by integration tests is unrelated).
|
||||
|
||||
use std::panic::{AssertUnwindSafe, catch_unwind, resume_unwind};
|
||||
|
||||
/// Run `f` with the env var `name` set to `value` (or unset if `value`
|
||||
/// is `None`), restoring the previous value on return.
|
||||
///
|
||||
/// Uses `catch_unwind` so a panic inside `f` does not leak the env var
|
||||
/// into the rest of the test process.
|
||||
///
|
||||
/// `cargo test` runs tests in parallel by default. Process env vars are
|
||||
/// process-global, so callers should pick uniquely-named vars to avoid
|
||||
/// inter-test races. The lifecycle here (save -> set -> run -> restore)
|
||||
/// is panic-safe but not race-safe.
|
||||
///
|
||||
/// **FOLLOW-UP**: the helper does not
|
||||
/// enforce the unique-name discipline -- a future contributor passing
|
||||
/// a common name like `HOME` could trigger flaky tests. The standard
|
||||
/// fix is to add `serial_test` as a dev-dep and decorate every
|
||||
/// env-touching test with `#[serial(env_var)]` so the test runner
|
||||
/// serialises them. For now the unique-name
|
||||
/// convention plus `catch_unwind` restoration is sufficient for the
|
||||
/// tests that ship today.
|
||||
pub(crate) fn with_env_var<R>(name: &str, value: Option<&str>, f: impl FnOnce() -> R) -> R {
|
||||
let previous = std::env::var_os(name);
|
||||
// SAFETY: env-var writes are not thread-safe. Callers use uniquely
|
||||
// named vars so no concurrent test races on the same name.
|
||||
unsafe {
|
||||
match value {
|
||||
Some(v) => std::env::set_var(name, v),
|
||||
None => std::env::remove_var(name),
|
||||
}
|
||||
}
|
||||
|
||||
let result = catch_unwind(AssertUnwindSafe(f));
|
||||
|
||||
// SAFETY: see above. Restore unconditionally so a panic doesn't
|
||||
// leak env state to subsequent tests.
|
||||
unsafe {
|
||||
match previous {
|
||||
Some(prev) => std::env::set_var(name, prev),
|
||||
None => std::env::remove_var(name),
|
||||
}
|
||||
}
|
||||
|
||||
match result {
|
||||
Ok(value) => value,
|
||||
Err(payload) => resume_unwind(payload),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn restores_previous_value_on_normal_return() {
|
||||
let key = "KIGI_HOOKS_TEST_SUPPORT_RESTORE";
|
||||
with_env_var(key, Some("first"), || {
|
||||
with_env_var(key, Some("second"), || {
|
||||
assert_eq!(std::env::var(key).unwrap(), "second");
|
||||
});
|
||||
assert_eq!(std::env::var(key).unwrap(), "first");
|
||||
});
|
||||
assert!(std::env::var(key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restores_previous_unset_state_on_normal_return() {
|
||||
let key = "KIGI_HOOKS_TEST_SUPPORT_UNSET_RESTORE";
|
||||
// SAFETY: see module-level note.
|
||||
unsafe {
|
||||
std::env::remove_var(key);
|
||||
}
|
||||
with_env_var(key, Some("temporary"), || {
|
||||
assert_eq!(std::env::var(key).unwrap(), "temporary");
|
||||
});
|
||||
assert!(std::env::var(key).is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn restores_after_panic() {
|
||||
let key = "KIGI_HOOKS_TEST_SUPPORT_PANIC_RESTORE";
|
||||
// SAFETY: see module-level note.
|
||||
unsafe {
|
||||
std::env::remove_var(key);
|
||||
}
|
||||
let panicked = catch_unwind(AssertUnwindSafe(|| {
|
||||
with_env_var(key, Some("during-panic"), || {
|
||||
panic!("intentional");
|
||||
});
|
||||
}));
|
||||
assert!(panicked.is_err(), "expected panic to propagate");
|
||||
assert!(
|
||||
std::env::var(key).is_err(),
|
||||
"env var must be restored after panic"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn allows_explicit_unset() {
|
||||
let key = "KIGI_HOOKS_TEST_SUPPORT_EXPLICIT_UNSET";
|
||||
// SAFETY: see module-level note.
|
||||
unsafe {
|
||||
std::env::set_var(key, "before");
|
||||
}
|
||||
with_env_var(key, None, || {
|
||||
assert!(std::env::var(key).is_err());
|
||||
});
|
||||
assert_eq!(std::env::var(key).unwrap(), "before");
|
||||
// SAFETY: see module-level note.
|
||||
unsafe {
|
||||
std::env::remove_var(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user