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,186 @@
//! Layer 2a: Screen state tracking via `alacritty_terminal` (ptyctl).
//!
//! Parses raw PTY output through a headless terminal emulator and provides
//! queries for what the user would see on screen.
use ptyctl::styled::StyledLine;
use ptyctl::term::{ScreenOpts, ScreenOutput, SessionListener, Terminal};
/// Tracks the virtual terminal screen state by feeding raw PTY output
/// through an `alacritty_terminal`-based headless terminal (via ptyctl).
pub struct ScreenTracker {
terminal: Terminal,
/// Receives terminal-generated replies (cursor-position reports, device
/// attributes, color queries, …) the emulator emits while parsing input.
/// Drained by [`ScreenTracker::drain_responses`] so the harness can forward
/// them back to the child (real terminals answer these automatically).
pty_write_rx: tokio::sync::mpsc::UnboundedReceiver<Vec<u8>>,
}
impl ScreenTracker {
/// Create a new tracker for a terminal with the given dimensions.
pub fn new(rows: u16, cols: u16) -> Self {
let (tx, rx) = tokio::sync::mpsc::unbounded_channel();
let listener = SessionListener::new(tx);
Self {
terminal: Terminal::new(cols, rows, listener),
pty_write_rx: rx,
}
}
/// Feed raw PTY output bytes into the terminal emulator.
pub fn feed(&mut self, bytes: &[u8]) {
self.terminal.feed(bytes);
}
/// Drain any terminal-generated replies queued while parsing fed input
/// (cursor-position reports answering `ESC[6n`, device attributes, color
/// queries, …), concatenated in order. Empty when nothing was queued.
///
/// These MUST be written back to the PTY or programs that probe the
/// terminal will hang or time out — most relevant here, the inline
/// viewport's startup cursor-position query that minimal mode depends on
/// (a timeout there downgrades `--minimal` to full-screen inline). A real
/// terminal answers automatically; the harness forwards these in
/// [`crate::PtyHarness::update`] when response forwarding is enabled.
pub fn drain_responses(&mut self) -> Vec<u8> {
let mut out = Vec::new();
while let Ok(bytes) = self.pty_write_rx.try_recv() {
out.extend_from_slice(&bytes);
}
out
}
/// Return structured screen contents (no escape codes).
pub fn output(&self) -> ScreenOutput {
self.terminal.screen_content(&ScreenOpts::default())
}
/// Return the full text contents of the screen (no escape codes).
pub fn contents(&self) -> String {
self.output().lines.join("\n")
}
/// Check whether the screen contains the given text substring.
pub fn contains(&self, text: &str) -> bool {
self.contents().contains(text)
}
/// Return the current cursor position as `(row, col)` (0-indexed, matching
/// the original vt100 convention used by existing tests).
pub fn cursor_position(&self) -> (u16, u16) {
let pos = self.terminal.cursor_position();
// ptyctl cursor is 1-indexed; the harness API is 0-indexed.
(
(pos.row as u16).saturating_sub(1),
(pos.col as u16).saturating_sub(1),
)
}
/// Resize the virtual terminal to new dimensions.
pub fn resize(&mut self, rows: u16, cols: u16) {
self.terminal.resize(cols, rows);
}
/// Return the full screen with style information for visual artifacts.
pub fn styled(&self) -> Vec<StyledLine> {
self.terminal.screen_styled(&ScreenOpts::default())
}
/// Render the current screen as an HTML document.
pub fn html(&self) -> String {
self.terminal.screen_html(&ScreenOpts::default())
}
/// Access the underlying ptyctl `Terminal` for advanced queries
/// (styled output, scrollback, terminal modes, etc.).
pub fn terminal(&self) -> &Terminal {
&self.terminal
}
/// Number of lines in the terminal's scrollback history — content that has
/// scrolled *above* the visible screen. This is where minimal mode's
/// committed conversation blocks land (printed via `insert_before`).
pub fn scrollback_count(&self) -> usize {
self.terminal.scrollback_count()
}
/// The full scrollback history as text, oldest line first.
pub fn scrollback_text(&self) -> String {
let n = self.terminal.scrollback_count();
self.terminal
.scrollback_lines(n)
.into_iter()
.map(|l| l.text)
.collect::<Vec<_>>()
.join("\n")
}
/// Scrollback history plus the visible screen, joined oldest→newest:
/// everything a user could see by scrolling up. Minimal-mode committed
/// content may be in either region depending on how much has accumulated,
/// so assertions on committed output should use this.
pub fn full_text(&self) -> String {
let sb = self.scrollback_text();
let screen = self.contents();
if sb.is_empty() {
screen
} else {
format!("{sb}\n{screen}")
}
}
/// Whether scrollback + visible screen contains `text`.
pub fn full_contains(&self, text: &str) -> bool {
self.full_text().contains(text)
}
}
#[cfg(test)]
mod tests {
use super::*;
/// Lines pushed above a small screen must be readable via the scrollback
/// helpers — the property minimal-mode e2e tests rely on to assert that a
/// committed block reached native scrollback.
#[test]
fn scrolled_off_lines_are_captured_by_scrollback_helpers() {
// 3-row screen; print 8 numbered lines so the first ones scroll off.
let mut s = ScreenTracker::new(3, 20);
for i in 1..=8 {
s.feed(format!("line{i}\r\n").as_bytes());
}
// The earliest lines are no longer on the visible screen…
assert!(
!s.contains("line1"),
"line1 should have scrolled off-screen"
);
// …but they are in scrollback, and full_text sees everything.
assert!(s.scrollback_count() >= 5, "expected scrolled-off history");
assert!(s.scrollback_text().contains("line1"));
assert!(s.full_contains("line1"));
assert!(s.full_contains("line8"));
}
/// A DSR cursor-position query (`ESC[6n`) must produce a forwardable reply
/// (a CPR `ESC[<row>;<col>R`) — the mechanism minimal-mode tests rely on so
/// the inline viewport's startup cursor query completes. Without forwarding,
/// `--minimal` silently downgrades to full-screen inline.
#[test]
fn drain_responses_answers_cursor_position_query() {
let mut s = ScreenTracker::new(24, 80);
// Nothing queued before any query is fed.
assert!(s.drain_responses().is_empty());
s.feed(b"\x1b[6n");
let reply = s.drain_responses();
assert!(
reply.starts_with(b"\x1b[") && reply.ends_with(b"R"),
"expected a cursor-position report, got {:?}",
String::from_utf8_lossy(&reply)
);
// Drained exactly once — no duplicate delivery on the next call.
assert!(s.drain_responses().is_empty());
}
}