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).
246 lines
6.9 KiB
Rust
246 lines
6.9 KiB
Rust
//! Per-environment hyperlink route policy.
|
|
//!
|
|
//! Mirrors [`crate::clipboard::resolve_clipboard_route`]. Combines per-brand
|
|
//! [`HyperlinkCapabilities`] with multiplexer/SSH/Byobu state into a single
|
|
//! decision struct cached once per process.
|
|
|
|
use std::sync::OnceLock;
|
|
|
|
use crate::terminal::{Osc8Support, TerminalContext};
|
|
|
|
/// Describes the hyperlink strategy for the current environment.
|
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
|
pub struct HyperlinkRoute {
|
|
/// Whether to emit OSC 8 sequences around link text.
|
|
pub emit_osc8: bool,
|
|
/// Whether to include the `id=` parameter in OSC 8 sequences
|
|
/// (enables hover-grouping across wrapped lines).
|
|
pub emit_id: bool,
|
|
/// Human-readable reason why OSC 8 is disabled, or `None` if enabled.
|
|
pub skip_reason: Option<&'static str>,
|
|
}
|
|
|
|
/// Cached hyperlink route resolved at first use from the terminal context.
|
|
pub fn hyperlink_route() -> &'static HyperlinkRoute {
|
|
static ROUTE: OnceLock<HyperlinkRoute> = OnceLock::new();
|
|
ROUTE.get_or_init(|| {
|
|
let ctx = crate::terminal::terminal_context();
|
|
resolve_hyperlink_route(ctx)
|
|
})
|
|
}
|
|
|
|
/// Resolve the hyperlink route from a terminal context.
|
|
pub fn resolve_hyperlink_route(ctx: &TerminalContext) -> HyperlinkRoute {
|
|
let caps = ctx.hyperlink_capabilities();
|
|
let skip = ctx.hyperlink_skip_reason();
|
|
|
|
let emit_osc8 = caps.osc8 == Osc8Support::Native && skip.is_none();
|
|
let emit_id = emit_osc8 && caps.id_param;
|
|
|
|
HyperlinkRoute {
|
|
emit_osc8,
|
|
emit_id,
|
|
skip_reason: skip,
|
|
}
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use crate::terminal::{MultiplexerKind, TerminalContext, TerminalName, TmuxClientMeta};
|
|
|
|
fn iterm2_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::Iterm2,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn apple_terminal_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::AppleTerminal,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn warp_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::WarpTerminal,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn iterm2_tmux_34_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::Iterm2,
|
|
multiplexer: MultiplexerKind::Tmux,
|
|
tmux_version: Some("tmux 3.4".to_owned()),
|
|
tmux_meta: TmuxClientMeta {
|
|
tmux_env: Some("/tmp/tmux-501/default,12345,0".to_owned()),
|
|
tmux_pane: Some("%0".to_owned()),
|
|
},
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn iterm2_tmux_33_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::Iterm2,
|
|
multiplexer: MultiplexerKind::Tmux,
|
|
tmux_version: Some("tmux 3.3a".to_owned()),
|
|
tmux_meta: TmuxClientMeta {
|
|
tmux_env: Some("/tmp/tmux-501/default,12345,0".to_owned()),
|
|
tmux_pane: Some("%0".to_owned()),
|
|
},
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn screen_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::Iterm2,
|
|
multiplexer: MultiplexerKind::Screen,
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn vte_old_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::Vte,
|
|
vte_version: Some("4800".to_owned()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn vte_new_ctx() -> TerminalContext {
|
|
TerminalContext {
|
|
brand: TerminalName::Vte,
|
|
vte_version: Some("7402".to_owned()),
|
|
..Default::default()
|
|
}
|
|
}
|
|
|
|
fn unknown_ctx() -> TerminalContext {
|
|
TerminalContext::default()
|
|
}
|
|
|
|
// -- hyperlink_skip_reason tests --
|
|
|
|
#[test]
|
|
fn apple_terminal_skip_reason() {
|
|
assert_eq!(
|
|
apple_terminal_ctx().hyperlink_skip_reason(),
|
|
Some("apple_terminal")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn warp_skip_reason() {
|
|
assert_eq!(
|
|
warp_ctx().hyperlink_skip_reason(),
|
|
Some("unsupported_terminal")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn iterm2_no_skip() {
|
|
assert_eq!(iterm2_ctx().hyperlink_skip_reason(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn screen_skip_reason() {
|
|
assert_eq!(screen_ctx().hyperlink_skip_reason(), Some("screen"));
|
|
}
|
|
|
|
#[test]
|
|
fn tmux_33_skip_reason() {
|
|
assert_eq!(
|
|
iterm2_tmux_33_ctx().hyperlink_skip_reason(),
|
|
Some("tmux_old")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn tmux_34_no_skip() {
|
|
assert_eq!(iterm2_tmux_34_ctx().hyperlink_skip_reason(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn vte_old_skip_reason() {
|
|
assert_eq!(vte_old_ctx().hyperlink_skip_reason(), Some("vte_old"));
|
|
}
|
|
|
|
#[test]
|
|
fn vte_new_no_skip() {
|
|
assert_eq!(vte_new_ctx().hyperlink_skip_reason(), None);
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_terminal_skip_reason() {
|
|
// Unknown brand must report a skip_reason so telemetry/feedback
|
|
// doesn't log "none" alongside emit_osc8=false.
|
|
assert_eq!(
|
|
unknown_ctx().hyperlink_skip_reason(),
|
|
Some("unknown_terminal"),
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn vte_old_inside_old_tmux_blames_vte() {
|
|
// VTE 0.48 inside tmux 3.2: VTE is the deeper cause, so a tmux
|
|
// upgrade alone wouldn't fix OSC 8. Diagnostic should point at VTE.
|
|
let ctx = TerminalContext {
|
|
brand: TerminalName::Vte,
|
|
multiplexer: MultiplexerKind::Tmux,
|
|
tmux_version: Some("tmux 3.2".to_owned()),
|
|
vte_version: Some("4800".to_owned()),
|
|
..Default::default()
|
|
};
|
|
assert_eq!(ctx.hyperlink_skip_reason(), Some("vte_old"));
|
|
}
|
|
|
|
// -- resolve_hyperlink_route tests --
|
|
|
|
#[test]
|
|
fn iterm2_emits_osc8() {
|
|
let route = resolve_hyperlink_route(&iterm2_ctx());
|
|
assert!(route.emit_osc8);
|
|
assert!(route.emit_id);
|
|
}
|
|
|
|
#[test]
|
|
fn apple_terminal_fallback() {
|
|
let route = resolve_hyperlink_route(&apple_terminal_ctx());
|
|
assert!(!route.emit_osc8);
|
|
assert_eq!(route.skip_reason, Some("apple_terminal"));
|
|
}
|
|
|
|
#[test]
|
|
fn warp_fallback() {
|
|
let route = resolve_hyperlink_route(&warp_ctx());
|
|
assert!(!route.emit_osc8);
|
|
}
|
|
|
|
#[test]
|
|
fn tmux_33_fallback() {
|
|
let route = resolve_hyperlink_route(&iterm2_tmux_33_ctx());
|
|
assert!(!route.emit_osc8);
|
|
assert_eq!(route.skip_reason, Some("tmux_old"));
|
|
}
|
|
|
|
#[test]
|
|
fn tmux_34_emits() {
|
|
let route = resolve_hyperlink_route(&iterm2_tmux_34_ctx());
|
|
assert!(route.emit_osc8);
|
|
assert!(route.emit_id);
|
|
}
|
|
|
|
#[test]
|
|
fn unknown_terminal_no_emit() {
|
|
let route = resolve_hyperlink_route(&unknown_ctx());
|
|
// Unknown brand -> osc8 == Unknown -> not Native -> no emit
|
|
assert!(!route.emit_osc8);
|
|
assert_eq!(route.skip_reason, Some("unknown_terminal"));
|
|
}
|
|
}
|