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,207 @@
|
||||
use crate::agent::subagent::SubagentSpawnContext;
|
||||
use crate::session::SessionCommand;
|
||||
use agent_client_protocol as acp;
|
||||
use kigi_acp_lib::AcpAgentGatewaySender as GatewaySender;
|
||||
use kigi_tools::implementations::grok_build::task::types::{SubagentRequest, SubagentResult};
|
||||
use std::collections::HashMap;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use tokio::sync::{mpsc, oneshot};
|
||||
pub(crate) type GatewayOut = <acp::AgentSide as kigi_acp_lib::AcpSide>::OutMessage;
|
||||
pub(crate) fn test_gateway() -> GatewaySender {
|
||||
let (tx, _rx) = mpsc::unbounded_channel();
|
||||
GatewaySender::new(tx)
|
||||
}
|
||||
/// Like `test_gateway` but returns the receiver; keep it alive for the test.
|
||||
pub(crate) fn test_gateway_with_receiver() -> (GatewaySender, mpsc::UnboundedReceiver<GatewayOut>) {
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
(GatewaySender::new(tx), rx)
|
||||
}
|
||||
/// `ctx_with_toggle` with a wired `parent_cmd_tx`.
|
||||
pub(crate) fn ctx_with_toggle_and_cmd_tx(
|
||||
toggle: HashMap<String, bool>,
|
||||
) -> (
|
||||
SubagentSpawnContext,
|
||||
mpsc::UnboundedReceiver<SessionCommand>,
|
||||
) {
|
||||
let mut ctx = ctx_with_toggle(toggle);
|
||||
let (tx, rx) = mpsc::unbounded_channel();
|
||||
ctx.parent_cmd_tx = Some(tx);
|
||||
(ctx, rx)
|
||||
}
|
||||
pub(crate) fn ctx_with_toggle(toggle: HashMap<String, bool>) -> SubagentSpawnContext {
|
||||
let (tx, _rx) = mpsc::unbounded_channel();
|
||||
SubagentSpawnContext {
|
||||
lsp: None,
|
||||
parent_max_turns: None,
|
||||
gateway: test_gateway(),
|
||||
client_hooks: Default::default(),
|
||||
sampling_config: kigi_sampler::SamplerConfig {
|
||||
api_key: None,
|
||||
base_url: String::new(),
|
||||
model: String::new(),
|
||||
max_completion_tokens: None,
|
||||
temperature: None,
|
||||
top_p: None,
|
||||
api_backend: Default::default(),
|
||||
auth_scheme: Default::default(),
|
||||
extra_headers: Default::default(),
|
||||
context_window: 256_000,
|
||||
client_version: None,
|
||||
force_http1: false,
|
||||
max_retries: None,
|
||||
stream_tool_calls: false,
|
||||
idle_timeout_secs: None,
|
||||
client_identifier: None,
|
||||
reasoning_effort: None,
|
||||
deployment_id: None,
|
||||
user_id: None,
|
||||
origin_client: None,
|
||||
attribution_callback: None,
|
||||
bearer_resolver: None,
|
||||
supports_backend_search: false,
|
||||
compactions_remaining: None,
|
||||
compaction_at_tokens: None,
|
||||
doom_loop_recovery: None,
|
||||
header_injector: None,
|
||||
},
|
||||
alpha_test_key: None,
|
||||
auth_method_id: acp::AuthMethodId::new("test"),
|
||||
model_id: acp::ModelId::new("test"),
|
||||
storage_mode: crate::config::StorageMode::Local,
|
||||
auth: None,
|
||||
parent_cwd: PathBuf::from("/tmp"),
|
||||
parent_session_id: "test-parent".into(),
|
||||
yolo_mode: false,
|
||||
subagent_event_tx: tx,
|
||||
hunk_tracker_handle: kigi_hunk_tracker::HunkTrackerHandle::noop(),
|
||||
hunk_tracking_enabled: false,
|
||||
fs: Arc::new(kigi_workspace::file_system::LocalFs::new(PathBuf::from(
|
||||
"/tmp",
|
||||
))),
|
||||
terminal: Arc::new(crate::terminal::TerminalRunner::new(
|
||||
Arc::new(test_gateway()),
|
||||
acp::SessionId::new("test"),
|
||||
)),
|
||||
session_env: Arc::new(HashMap::new()),
|
||||
memory_config: None,
|
||||
web_search_sampling_config: None,
|
||||
web_fetch_config: Default::default(),
|
||||
image_gen_config: Default::default(),
|
||||
video_gen_config: Default::default(),
|
||||
app_builder_deployer_config: Default::default(),
|
||||
write_file_enabled: true,
|
||||
goal_enabled: false,
|
||||
ask_user_question_enabled: true,
|
||||
parent_cmd_tx: None,
|
||||
parent_session_info: None,
|
||||
subagent_roles: HashMap::new(),
|
||||
subagent_personas: HashMap::new(),
|
||||
persona_io_summaries: Vec::new(),
|
||||
parent_chat_state: None,
|
||||
available_models: indexmap::IndexMap::new(),
|
||||
subagent_model_overrides: HashMap::new(),
|
||||
subagent_toggle: toggle,
|
||||
disable_web_search: false,
|
||||
todo_gate: false,
|
||||
remote_settings: None,
|
||||
laziness_debug_log: None,
|
||||
backend_tools_enabled: true,
|
||||
respect_gitignore: false,
|
||||
path_not_found_hints: false,
|
||||
plugin_registry: None,
|
||||
models_manager: Default::default(),
|
||||
file_tool_overrides: None,
|
||||
agent_config: None,
|
||||
hook_registry: None,
|
||||
hook_workspace_root: String::new(),
|
||||
parent_depth: 0,
|
||||
inference_idle_timeout_secs: 600,
|
||||
auto_compact_threshold_tiers: crate::agent::subagent::AutoCompactThresholdTiers::default(),
|
||||
permission_handle: None,
|
||||
worktree_type: crate::util::config::WorktreeType::Linked,
|
||||
api_key_provider: None,
|
||||
image_description_model: crate::test_support::TEST_MODEL.to_owned(),
|
||||
workspace_ops: kigi_workspace::WorkspaceOps::for_test(),
|
||||
auth_manager: Arc::new(crate::auth::AuthManager::new(
|
||||
std::path::Path::new("/tmp/nonexistent-grok-test"),
|
||||
crate::auth::GrokComConfig::default(),
|
||||
)),
|
||||
attribution_callback: None,
|
||||
parent_agent_name: None,
|
||||
parent_model_agent_type: None,
|
||||
allowed_subagent_types: None,
|
||||
parent_mcp_configs: vec![],
|
||||
managed_mcp_state: crate::session::managed_mcp::ManagedMcpStateHandle::default(),
|
||||
managed_mcp_proxy_base_url: String::new(),
|
||||
parent_mcp_pool: None,
|
||||
parent_tool_snapshot: None,
|
||||
parent_skills: None,
|
||||
parent_skills_config: kigi_agent::prompt::skills::SkillsConfig::default(),
|
||||
parent_compat: kigi_tools::types::compat::CompatConfig::default(),
|
||||
auto_wake_delivered: None,
|
||||
task_output_tool_name: kigi_tools::reminders::task_completion::DEFAULT_TASK_OUTPUT_TOOL
|
||||
.to_string(),
|
||||
auto_wake_enabled: true,
|
||||
goal_loop_active: std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)),
|
||||
parent_blocking_wait_depth: std::sync::Arc::new(std::sync::atomic::AtomicUsize::new(0)),
|
||||
parent_terminal_backend: None,
|
||||
parent_notification_handle: None,
|
||||
parent_scheduler_handle: None,
|
||||
}
|
||||
}
|
||||
pub(crate) fn make_request(
|
||||
subagent_type: &str,
|
||||
) -> (SubagentRequest, oneshot::Receiver<SubagentResult>) {
|
||||
let (tx, rx) = oneshot::channel();
|
||||
let req = SubagentRequest {
|
||||
id: uuid::Uuid::now_v7().to_string(),
|
||||
prompt: "do something".into(),
|
||||
description: "test task".into(),
|
||||
subagent_type: subagent_type.into(),
|
||||
parent_session_id: "test-parent".into(),
|
||||
parent_prompt_id: Some("parent-prompt".into()),
|
||||
resume_from: None,
|
||||
cwd: None,
|
||||
runtime_overrides: Default::default(),
|
||||
run_in_background: false,
|
||||
surface_completion: true,
|
||||
fork_context: false,
|
||||
result_tx: tx,
|
||||
};
|
||||
(req, rx)
|
||||
}
|
||||
#[derive(Default)]
|
||||
pub(crate) struct DummyLspDispatch;
|
||||
#[async_trait::async_trait]
|
||||
impl kigi_tools::implementations::lsp::LspBackend for DummyLspDispatch {
|
||||
fn ensure_started_background(&self) {}
|
||||
async fn ensure_ready(&self) -> Result<(), String> {
|
||||
Ok(())
|
||||
}
|
||||
fn is_ready(&self) -> bool {
|
||||
true
|
||||
}
|
||||
async fn dispatch(
|
||||
&self,
|
||||
_input: &kigi_tools::implementations::lsp::LspToolInput,
|
||||
) -> kigi_tools::implementations::lsp::LspToolResult {
|
||||
kigi_tools::implementations::lsp::LspToolResult {
|
||||
text: String::new(),
|
||||
is_error: false,
|
||||
}
|
||||
}
|
||||
async fn drain_diagnostics(
|
||||
&self,
|
||||
_timeout: std::time::Duration,
|
||||
) -> Option<kigi_tools::implementations::lsp::DiagnosticsSummary> {
|
||||
None
|
||||
}
|
||||
async fn notify_file_changed(&self, _path: &std::path::Path, _content: &str) {}
|
||||
async fn read_diagnostics(
|
||||
&self,
|
||||
_paths: &[std::path::PathBuf],
|
||||
) -> Vec<kigi_tools::implementations::lsp::FileDiagnosticEntry> {
|
||||
vec![]
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
pub(crate) mod lsp_runtime;
|
||||
|
||||
pub(crate) const TEST_MODEL: &str = "test-model";
|
||||
|
||||
/// Prepend the hermetic git binary (via `GIT_BIN_PATH`) to `PATH` so that
|
||||
/// `Command::new("git")` in test helpers resolves to the Bazel-provided
|
||||
/// static binary instead of relying on system-installed git.
|
||||
///
|
||||
/// Safe to call multiple times — only the first call mutates `PATH`.
|
||||
pub(crate) fn ensure_hermetic_git_on_path() {
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Once;
|
||||
static INIT: Once = Once::new();
|
||||
INIT.call_once(|| {
|
||||
if let Ok(git_bin) = std::env::var("GIT_BIN_PATH") {
|
||||
let p = PathBuf::from(&git_bin);
|
||||
let p = if p.is_relative() {
|
||||
std::env::current_dir().unwrap().join(&p)
|
||||
} else {
|
||||
p
|
||||
};
|
||||
if let Some(dir) = p.parent() {
|
||||
let cur = std::env::var("PATH").unwrap_or_default();
|
||||
unsafe {
|
||||
std::env::set_var("PATH", format!("{}:{}", dir.display(), cur));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user