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:
2026-07-17 05:31:01 -04:00
commit d6c20fc13f
2612 changed files with 1353757 additions and 0 deletions
@@ -0,0 +1,206 @@
use std::hint::black_box;
use colored_json::ToColoredJson;
use criterion::{BatchSize, Criterion, criterion_group, criterion_main};
use ratatui::backend::CrosstermBackend;
use ratatui::layout::Rect;
use ratatui::style::Style;
use ratatui::{TerminalOptions, Viewport};
use serde_json::json;
use kigi_ratatui_inline::{LinkSpan, Terminal, split_into_line_segments};
fn generate_colored_json_content() -> String {
// Create a complex JSON structure with 50+ lines when pretty printed
let data = json!({
"users": (0..10).map(|i| json!({
"id": i,
"name": format!("User {}", i),
"email": format!("user{}@example.com", i),
"active": i % 2 == 0,
"roles": ["admin", "user", "moderator"],
"metadata": {
"created_at": "2024-01-01T00:00:00Z",
"updated_at": "2024-12-01T00:00:00Z",
"last_login": "2024-12-15T12:00:00Z",
"preferences": {
"theme": if i % 2 == 0 { "dark" } else { "light" },
"language": "en",
"notifications": true
}
}
})).collect::<Vec<_>>(),
"settings": {
"app_name": "Test Application",
"version": "1.2.3",
"features": {
"feature_a": true,
"feature_b": false,
"feature_c": true,
"feature_d": {
"enabled": true,
"config": {
"param1": 100,
"param2": "value",
"param3": [1, 2, 3, 4, 5]
}
}
}
},
"logs": (0..5).map(|i| json!({
"timestamp": format!("2024-12-01T{:02}:00:00Z", i),
"level": if i % 3 == 0 { "ERROR" } else if i % 2 == 0 { "WARN" } else { "INFO" },
"message": format!("Log entry number {}", i),
"context": {
"request_id": format!("req-{:04}", i * 100),
"user_id": i % 10,
"action": "process_request"
}
})).collect::<Vec<_>>()
});
// Convert to colored JSON string - this will have LOTS of ANSI color codes
serde_json::to_string_pretty(&data)
.unwrap()
.to_colored_json_auto()
.unwrap()
}
fn bench_split_into_line_segments(c: &mut Criterion) {
let colored_json = generate_colored_json_content();
// Also create a plain text version for comparison
let plain_text = colored_json
.chars()
.filter(|c| *c != '\x1b')
.collect::<String>()
.replace("[0m", "")
.replace("[31m", "")
.replace("[32m", "")
.replace("[33m", "")
.replace("[34m", "")
.replace("[35m", "")
.replace("[36m", "")
.replace("[37m", "")
.replace("[90m", "")
.replace("[1m", "")
.replace("[22m", "");
let mut group = c.benchmark_group("text_splitting");
// Benchmark colored JSON
group.bench_function("colored_json_80_cols", |b| {
b.iter(|| split_into_line_segments(black_box(&colored_json), black_box(80)));
});
// Benchmark plain text
group.bench_function("plain_text_80_cols", |b| {
b.iter(|| split_into_line_segments(black_box(&plain_text), black_box(80)));
});
group.finish();
}
/// Fill rows `[rows]` of the current frame with `ch`.
fn fill_rows(
t: &mut Terminal<CrosstermBackend<Vec<u8>>>,
ch: char,
rows: std::ops::Range<u16>,
width: u16,
) {
let line: String = ch.to_string().repeat(width as usize);
let mut frame = t.get_frame();
let buf = frame.buffer_mut();
for y in rows {
buf.set_string(0, y, &line, Style::default());
}
}
/// Build a terminal in a "ready to flush" state: a previous full-screen frame,
/// then a partial-redraw current frame (a few changed rows, like streaming
/// output) with `num_links` hyperlinks set. The returned terminal is cloned per
/// benchmark iteration so the measured call is just the flush.
fn dirty_terminal(
width: u16,
height: u16,
num_links: usize,
) -> Terminal<CrosstermBackend<Vec<u8>>> {
let area = Rect::new(0, 0, width, height);
let mut t = Terminal::with_options(
CrosstermBackend::new(Vec::<u8>::new()),
TerminalOptions {
viewport: Viewport::Fixed(area),
},
)
.unwrap();
// Previous frame: full screen of 'a'.
fill_rows(&mut t, 'a', 0..height, width);
t.set_frame_links(&[]);
let _ = t.flush_with_links();
t.swap_buffers();
// Current frame: mostly unchanged ('a'), a few changed rows ('b') — a
// realistic partial redraw. The diff still visits every cell, which is where
// the per-cell link resolution cost lives.
fill_rows(&mut t, 'a', 0..height, width);
fill_rows(&mut t, 'b', 0..height.min(3), width);
let spans: Vec<LinkSpan> = (0..num_links)
.map(|i| {
let row = (i as u16) % height;
LinkSpan {
row,
col_start: 0,
col_end: width.min(24),
url: "https://example.com/some/path".into(),
id: Some(i as u32),
}
})
.collect();
t.set_frame_links(&spans);
t
}
/// Benchmarks the OSC 8 hyperlink render path on a 256x100 viewport: the plain
/// `flush` baseline, `flush_with_links` with no links (early-exit fast path),
/// and `flush_with_links` with 50 links (the link-aware diff + emit).
fn bench_flush_with_links(c: &mut Criterion) {
const W: u16 = 256;
const H: u16 = 100;
let mut group = c.benchmark_group("hyperlink_flush");
group.bench_function("flush_baseline_no_links", |b| {
b.iter_batched(
|| dirty_terminal(W, H, 0),
|mut t| black_box(t.flush()),
BatchSize::SmallInput,
);
});
group.bench_function("flush_with_links_no_links", |b| {
b.iter_batched(
|| dirty_terminal(W, H, 0),
|mut t| black_box(t.flush_with_links()),
BatchSize::SmallInput,
);
});
group.bench_function("flush_with_links_50_links", |b| {
b.iter_batched(
|| dirty_terminal(W, H, 50),
|mut t| black_box(t.flush_with_links()),
BatchSize::SmallInput,
);
});
group.finish();
}
criterion_group!(
benches,
bench_split_into_line_segments,
bench_flush_with_links
);
criterion_main!(benches);