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
+303
View File
@@ -0,0 +1,303 @@
//! Markdown styling types.
//!
//! This module provides the `MarkdownStyle` struct which defines colors and
//! effects for all markdown elements.
use anstyle::{Effects, Style};
use crate::colors::adapt_style;
/// Table border characters for rendering tables in pretty mode.
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct TableBorders {
chars: [char; 11],
}
impl TableBorders {
const H: usize = 0;
const V: usize = 1;
const TL: usize = 2;
const TR: usize = 3;
const BL: usize = 4;
const BR: usize = 5;
const T_T: usize = 6;
const T_B: usize = 7;
const T_L: usize = 8;
const T_R: usize = 9;
const X: usize = 10;
pub const BOX: Self = Self {
chars: ['─', '│', '┌', '┐', '└', '┘', '┬', '┴', '├', '┤', '┼'],
};
pub const ASCII: Self = Self {
chars: ['-', '|', '+', '+', '+', '+', '+', '+', '+', '+', '+'],
};
pub const DOUBLE: Self = Self {
chars: ['═', '║', '╔', '╗', '╚', '╝', '╦', '╩', '╠', '╣', '╬'],
};
pub const fn new(chars: [char; 11]) -> Self {
Self { chars }
}
// Short names (used in table formatting)
pub const fn h(&self) -> char {
self.chars[Self::H]
}
pub const fn v(&self) -> char {
self.chars[Self::V]
}
pub const fn c_tl(&self) -> char {
self.chars[Self::TL]
}
pub const fn c_tr(&self) -> char {
self.chars[Self::TR]
}
pub const fn c_bl(&self) -> char {
self.chars[Self::BL]
}
pub const fn c_br(&self) -> char {
self.chars[Self::BR]
}
pub const fn t_t(&self) -> char {
self.chars[Self::T_T]
}
pub const fn t_b(&self) -> char {
self.chars[Self::T_B]
}
pub const fn t_l(&self) -> char {
self.chars[Self::T_L]
}
pub const fn t_r(&self) -> char {
self.chars[Self::T_R]
}
pub const fn x(&self) -> char {
self.chars[Self::X]
}
// Long names (for readability)
pub const fn horizontal(&self) -> char {
self.chars[Self::H]
}
pub const fn vertical(&self) -> char {
self.chars[Self::V]
}
pub const fn top_left(&self) -> char {
self.chars[Self::TL]
}
pub const fn top_right(&self) -> char {
self.chars[Self::TR]
}
pub const fn bottom_left(&self) -> char {
self.chars[Self::BL]
}
pub const fn bottom_right(&self) -> char {
self.chars[Self::BR]
}
pub const fn t_top(&self) -> char {
self.chars[Self::T_T]
}
pub const fn t_bottom(&self) -> char {
self.chars[Self::T_B]
}
pub const fn t_left(&self) -> char {
self.chars[Self::T_L]
}
pub const fn t_right(&self) -> char {
self.chars[Self::T_R]
}
pub const fn cross(&self) -> char {
self.chars[Self::X]
}
}
impl Default for TableBorders {
fn default() -> Self {
Self::BOX
}
}
/// Style configuration for markdown rendering.
///
/// Each field controls the styling for a specific markdown element.
/// The `_inner` variants are applied to the content, while `_outer` variants
/// are applied to the syntax markers (which are hidden in pretty mode).
#[derive(Copy, Clone, Default, Debug, PartialEq, Eq, Hash)]
pub struct MarkdownStyle {
pub heading_inner: [Style; 6],
pub heading_outer: [Style; 6],
pub strong_inner: Style,
pub strong_outer: Style,
pub emphasis_inner: Style,
pub emphasis_outer: Style,
pub strikethrough_inner: Style,
pub strikethrough_outer: Style,
pub inline_code_inner: Style,
pub inline_code_outer: Style,
pub blockquote_outer: Style,
pub task_checked: Style,
pub task_unchecked: Style,
pub list_item: Style,
pub rule: Style,
pub link_outer: Style,
pub link_text: Style,
pub link_url: Style,
pub link_title: Style,
pub code_outer: Style,
pub code_language: Style,
pub code_untagged: Style,
pub code_background: Style,
pub table_outer: Style,
/// Default foreground for plain body text (paragraphs with no formatting).
/// When set, the renderer applies this to text spans that would otherwise
/// inherit the terminal's default foreground.
pub text: Style,
/// Style for rendered LaTeX math (inline `$...$`/`\(...\)` content after
/// Unicode conversion, and display math block lines). In raw mode the
/// style applies to the unconverted TeX source.
pub math: Style,
}
impl MarkdownStyle {
/// Adapt all styles for the terminal's color capabilities.
///
/// This downgrades RGB colors to 256-color or 16-color as needed.
pub fn adapt(self) -> Self {
Self {
heading_inner: [
adapt_style(self.heading_inner[0]),
adapt_style(self.heading_inner[1]),
adapt_style(self.heading_inner[2]),
adapt_style(self.heading_inner[3]),
adapt_style(self.heading_inner[4]),
adapt_style(self.heading_inner[5]),
],
heading_outer: [
adapt_style(self.heading_outer[0]),
adapt_style(self.heading_outer[1]),
adapt_style(self.heading_outer[2]),
adapt_style(self.heading_outer[3]),
adapt_style(self.heading_outer[4]),
adapt_style(self.heading_outer[5]),
],
strong_inner: adapt_style(self.strong_inner),
strong_outer: adapt_style(self.strong_outer),
emphasis_inner: adapt_style(self.emphasis_inner),
emphasis_outer: adapt_style(self.emphasis_outer),
strikethrough_inner: adapt_style(self.strikethrough_inner),
strikethrough_outer: adapt_style(self.strikethrough_outer),
inline_code_inner: adapt_style(self.inline_code_inner),
inline_code_outer: adapt_style(self.inline_code_outer),
blockquote_outer: adapt_style(self.blockquote_outer),
task_checked: adapt_style(self.task_checked),
task_unchecked: adapt_style(self.task_unchecked),
list_item: adapt_style(self.list_item),
rule: adapt_style(self.rule),
link_outer: adapt_style(self.link_outer),
link_text: adapt_style(self.link_text),
link_url: adapt_style(self.link_url),
link_title: adapt_style(self.link_title),
code_outer: adapt_style(self.code_outer),
code_language: adapt_style(self.code_language),
code_untagged: adapt_style(self.code_untagged),
code_background: adapt_style(self.code_background),
table_outer: adapt_style(self.table_outer),
text: adapt_style(self.text),
math: adapt_style(self.math),
}
}
}
/// Check if ALL active styles have HIDDEN effect.
/// Used in pretty mode to determine if text should be skipped.
pub(crate) fn all_hidden(styles: impl IntoIterator<Item = Option<Style>>) -> bool {
let mut has_any = false;
let mut all_are_hidden = true;
for style in styles {
has_any = true;
match style {
Some(s) if s.get_effects().contains(Effects::HIDDEN) => {}
_ => {
all_are_hidden = false;
}
}
}
has_any && all_are_hidden
}
/// Merge multiple styles into one for rendering.
/// Strips HIDDEN from final output - it's a semantic marker, not a visual style.
pub(crate) fn merge_styles(styles: impl IntoIterator<Item = Option<Style>>) -> Style {
let mut out = Style::new();
let mut prev = Style::new();
for style in styles {
if out.get_effects().contains(Effects::HIDDEN) {
out = prev;
} else {
prev = out;
}
let Some(style) = style else {
continue;
};
if !style.get_effects().is_plain() {
out = out.effects(out.get_effects() | style.get_effects());
}
if style.get_effects().contains(Effects::DIMMED) {
out = out.effects(out.get_effects().remove(Effects::BOLD));
}
if style.get_effects().contains(Effects::BOLD) {
out = out.effects(out.get_effects().remove(Effects::DIMMED));
}
if let Some(color) = style.get_fg_color() {
out = out.fg_color(Some(color));
}
if let Some(color) = style.get_bg_color() {
out = out.bg_color(Some(color));
}
if let Some(color) = style.get_underline_color() {
out = out.underline_color(Some(color));
}
}
out.effects(out.get_effects().remove(Effects::HIDDEN))
}
// Simple default style for testing (no colors, just effects)
#[cfg(any(test, fuzzing))]
pub mod test_style {
use super::MarkdownStyle;
use anstyle::Style;
/// A minimal style for testing with no colors.
pub const STYLE: MarkdownStyle = MarkdownStyle {
heading_inner: [Style::new().bold(); 6],
heading_outer: [Style::new().dimmed().hidden(); 6],
strong_inner: Style::new().bold(),
strong_outer: Style::new().dimmed().hidden(),
emphasis_inner: Style::new().italic(),
emphasis_outer: Style::new().dimmed().hidden(),
strikethrough_inner: Style::new().strikethrough(),
strikethrough_outer: Style::new().dimmed().hidden(),
inline_code_inner: Style::new().bold(),
inline_code_outer: Style::new().dimmed().hidden(),
blockquote_outer: Style::new().dimmed(),
task_checked: Style::new(),
task_unchecked: Style::new().dimmed(),
list_item: Style::new().dimmed(),
rule: Style::new(),
link_outer: Style::new(),
link_text: Style::new().bold(),
link_url: Style::new().dimmed(),
link_title: Style::new(),
code_outer: Style::new().dimmed().hidden(),
code_language: Style::new().hidden(),
code_untagged: Style::new(),
code_background: Style::new(),
table_outer: Style::new().bold(),
text: Style::new(),
math: Style::new().italic(),
};
}