Feedback about an unofficial community build belongs on its own issue
tracker, not Moonshot's feedback endpoint — and this mirrors the
official kimi-cli, whose /feedback opens its repo's issues page
(ISSUE_URL in ui/shell/slash.py). /feedback now returns
Action::OpenUrl(https://github.com/ZacharyZhang-NY/Kigi-CLI/issues),
the same battle-tested browser path /docs web uses.
The now-dead TUI text-feedback pipeline is excised: PromptInputMode::
Feedback (~ prefix composer mode), Action::{EnterFeedbackMode,
SendFeedback}, Effect::SendFeedback (the kigi/feedback ACP POST),
TaskResult::{FeedbackComplete,FeedbackFailed}, and their dispatchers.
The shell-side kigi/feedback ACP extension stays: it is protocol
surface for editor embeddings, OAuth-gated, and shared with kigi/btw.
Gates: workspace check/clippy --all-targets 0/0, fmt, kigi-tui lib
6621 passed / 0 failed.
The unauthenticated welcome screen previously offered only 'Login with
Kimi Code'. It now lists every interactive platform the shell
advertises, matching the official kimi-cli picker:
Kimi Code (OAuth)
Moonshot Open Platform (API key · moonshot.cn)
Moonshot Open Platform (API key · moonshot.ai)
Quit
- Shell: new ACP auth methods moonshot-cn / moonshot-ai (advertised
after kimi-code; the BYOK first-position invariant holds).
authenticate(moonshot-*) reloads keys from env>config, fails with an
actionable message when none is configured, validates the key against
GET {base}/models (401 → 'invalid API key' naming the console), then
swaps the fresh config in, triggers the model sync, and reports
auth_mode api_key so the pager treats it like other API-key logins.
Never session-based; keys never logged.
- Config: save_platform_api_key persists [platforms.<id>].api_key via an
atomic mode-preserving write under the config lock; refuses OAuth
platforms, blank keys, and unparseable files.
- TUI: the Pending welcome renders the picker rows from the advertised
methods (arrows/Enter/mouse; 'l' keeps selecting the OAuth row).
Choosing a Moonshot row opens a masked paste box ('Paste your Moonshot
API key (from platform.moonshot.cn)'); Esc returns to the picker,
Enter persists the key and authenticates in one sequential effect;
failures return to the picker with the error line, success lands on
the normal welcome. Startup eager-auth is unchanged: a key already in
the environment authenticates exactly as before, and single-method
shells keep the historical auto-device-flow.
Gates: workspace check/clippy 0/0; shell 4870 + tui 6620 lib tests
green; headless probe advertises [kimi-code, moonshot-cn, moonshot-ai]
for a fresh user and xai.api_key-first with a key configured.
The PRD's first acceptance gate now holds: grep -RinE '\bx\.ai\b|grok'
crates/ --include='*.rs' → 0 matches (exempt: NOTICE and third-party
license archives, README provenance, and the required 'Based on Grok
Build Open Source' attribution, now sourced from version_attribution.txt).
Wire-visible renames (both sides in this repo, changed in lockstep):
- Auth method id 'grok.com' → 'kimi-code' (AuthMethodKind::KimiCode).
- Every x.ai/* and _x.ai/* ACP ext method and meta key → kigi/* /
_kigi/* (~200 names; grokShell → kigiShell). Session-file replay keeps
a read-side alias for the legacy '_x.ai/session/update' method so
existing updates.jsonl histories load; writes emit only the new name
(both directions test-pinned).
- Agent types grok-build* → kigi* with a documented legacy-prefix alias
at resolution time so persisted sessions keep resolving.
- ToolNamespace/BuiltinAgentName GrokBuild* → Kigi* (wire snake_case
kigi/kigi_concise/kigi_hashline; schema regenerated); grok_build
implementation dirs renamed to kigi*.
- x-grok-* headers → x-kigi-*, __GROK_* sentinels → __KIGI_*, themes
grokday/groknight → kigiday/kiginight (old persisted values fall back
to the default theme), web_fetch allowlist xAI hosts → kimi.com +
moonshot platforms, changelog CDN → this repo, grok-build changelog
archives deleted.
- BYOK default endpoint removed: [endpoints] api_base_url is now truly
optional with NO default — consumers fail fast with the flag name when
unset (no silent x.ai egress). Mock harnesses inject it explicitly.
- System-prompt identity fixed: 'released by xAI' → 'an unofficial
community CLI for Kimi' (template + regenerated encrypted form).
Also repaired pre-existing grok-era test debt found by the sweep: the
stale trace_classify default-model pin, the grok-pager UA label test,
pty-harness stale-binary reuse and non-hermetic moonshot routing (a PTY
test could previously reach the real api.moonshot.cn), and the outdated
oauth fixture scope key.
Gates: §9 grep 0; fmt clean; workspace check/clippy 0/0 (-D warnings);
FULL cargo test --workspace: 234 suites, 21,961 passed, 0 failed;
deny advisories ok.
- .github/workflows/release.yml: on tag v* build all 5 targets (macOS
arm64/x86_64, Linux arm64/x86_64 incl. free arm runners, Windows
x86_64) with the release-dist profile, archive kigi-<version>-<triple>
with LICENSE/NOTICE/THIRD-PARTY-NOTICES, generate SHA256SUMS, publish
the release (prerelease for tags containing '-'), with a tag↔workspace
version guard.
- install.sh / install.ps1 (repo root): platform detection, latest or
--version download from GitHub Releases, SHA-256 verification against
SHA256SUMS, install into the kigi home's downloads/ + bin/kigi symlink
(the same layout the self-updater manages), smoke test, PATH guidance.
- kigi-update rewritten onto the GitHub Releases API (documented wire
shape; stable=/latest, alpha=semver-max across the list, pinned=/tags):
SHA-256 gate before any binary swap, tar.gz/zip extraction per
platform, atomic bin/kigi symlink swap, channel/rollback semantics and
the KIGI_AUTO_UPDATE gate preserved verbatim; every x.ai/GCS/npm
endpoint deleted, npm/gh-release installers removed, legacy grok/agent
links retired on install. kigi-env owns the update base URL with a
KIGI_UPDATE_BASE_URL override (this is what the test artifact server
injects).
- .cargo/config.toml: removed the non-portable neoverse-v2 CPU pin on
Linux arm64 (fleet-specific); RELRO/NX hardening link-args now apply
to the gnu targets too, matching the release-dist profile's contract.
- THIRD-PARTY-NOTICES regenerated via cargo-about (about.toml +
template); the M0 hand-built file is dropped and README points at the
generated one. docs/RELEASE.md carries the release checklist.
- Deleted xAI-era leftovers: kigi-tui/scripts/install*.{sh,ps1} (x.ai
CDN) and the @xai-official/grok npm skeleton (PRD F8: no npm).
Gates: fmt clean; workspace check/clippy 0/0 (--locked, -D warnings);
kigi-update 58 lib + 86 integration tests green; deny ok;
release-dist build of kigi-bin succeeds and reports 'kigi 0.1.0'.
Removed root-and-branch for the zero-egress guarantee (the hub was xAI's
remote-workspace/cloud-sandbox service):
- Crates deleted: kigi-computer-hub-core, kigi-computer-hub-sdk,
kigi-computer-hub-mcp-adapter, kigi-workspace-client (hub-proxied
workspace RPC client), and kigi-tracing (its sole network path was the
OTLP gRPC exporter; zero consumers remained). kigi-tracing-macros
(purely local) stays.
- kigi-workspace: every hub surface deleted — hub server/channel/auth,
HITL-over-hub permissions, donation/metrics pumps, file upload RPCs,
hub tool-snapshot merge (resolve pipeline is MCP-only now),
WorkspaceOps::Proxy. Local worktrees, sessions, leader IPC, MCP, and
the ACP permission prompt path are untouched; LocalRegistry re-homed
into kigi-tool-runtime on the existing ToolDyn types so in-process
tool dispatch is unchanged.
- kigi-shell: leader workspace-exposure control surface (incl. the
wss://computer-hub... URL), [hub] config, ObservabilityBridge, hub
WebSocket proxy, dead OTLP config knobs. ClientMode::Headless (never
constructed) removed.
- kigi-tui/bin: hidden `kigi workspace` command removed (`kigi
worktree` stays).
- Renames: --xai-api-base-url → --api-base-url / KIGI_API_BASE_URL /
[endpoints] api_base_url (serde alias keeps old configs working; the
flag feeds BYOK/custom-endpoint routing, not main inference);
grok_version → kigi_version in inspect/models-cache/trace metadata
(old caches self-heal via version-mismatch refetch).
- Dependency tree: dropped fastrace*, opentelemetry-otlp/http/proto,
tokio-tungstenite from the workspace; fixed the 4 real useless_format
violations the fastrace lint allowance was masking and removed the
allowance.
- marketplaceAllowlist kept: it gates the LOCAL plugin-marketplace
feature, not an xAI service.
Known §9 leftover (deliberate, for the M3 sweep): the BYOK default base
URL string. Gates: workspace check/clippy 0/0, fmt, deny ok; suites
green (workspace 1042, shell 4918, tui 6634, tools 2608, tool-runtime
47, mcp 154).
The welcome and prompt model labels (and /effort's 'current' hint)
rendered the canonical internal level name, so K3 at its default effort
showed 'K3 (xhigh)' even though the server's vocabulary for that level is
'max' (live /models think_efforts: low/high/max).
New ModelState::reasoning_effort_display() resolves the current effort
through the model's own effort menu (option id whose value matches),
falling back to the canonical name only when the model has no entry for
the level. All three display sites route through it; test pins the K3
mapping (Xhigh → 'max', Low → 'low', no-menu-entry → canonical).
- docs/SMOKE.md: the F9 capability checklist — every carried-over harness
capability mapped to at least one automated case (test target) or a
manual probe with its observable, per the PRD acceptance rule.
- scripts/bench.sh: enforces both PRD performance budgets and fails CI on
a miss — `kigi --version` p95 <= 50ms via hyperfine, and TUI first
frame <= 300ms measured through the pty harness (real vt100 emulator
answering terminal queries) via the new scripts/first-frame.scenario.json.
Local run: p95 10.1ms, first frame 134ms.
- pty harness: StepOutcome gains elapsed_ms (stamped per step by the
scripted runner) — a leading wait_for_text step's elapsed IS the
spawn-to-first-paint latency the budget reads.
- CI: new `perf` job (macOS + Linux) building the release binaries and
running scripts/bench.sh.
New `kigi import-kimi` (with --dry-run) plus a one-time welcome-screen
hint when ~/.kimi/config.toml is present and unimported.
- Sources: ~/.kimi/config.toml (default_model, [models.*], [providers.*])
and ~/.kimi/mcp.json ({"mcpServers": ...} — parsed by the existing
McpConfig machinery). Shapes ported from kimi-cli 1.49.0 config.py with
citations.
- Strictly read-only over ~/.kimi: plain reads only; the test suite pins
byte-identical contents AND unchanged mtimes across scan+apply. Keyring
credentials are never imported; KIMI_SHARE_DIR / any KIMI_* env var is
never consulted (official dir hardcoded to ~/.kimi).
- Mapping: MCP servers merge into kigi [mcp_servers.*] without clobbering
existing names; models on non-built-in providers become kigi [model.*]
custom entries (base_url + api_key + context_window); providers that
duplicate kigi's built-in Kimi/Moonshot platforms are skipped with a
note; default_model maps to the imported alias or the managed catalog
key and never overwrites an existing default.
- One-time marker ~/.kigi/kimi_import_done (claude-import convention);
the startup hint and re-runs no-op once set. api_key values flow only
into the user's own config.toml and are redacted in every summary.
Verified end-to-end with the real binary in a sandboxed home: dry-run,
apply, `kigi mcp list` shows both imported servers, second run no-ops,
~/.kimi mtimes unchanged. 8 unit tests + CLI parse test.
- `kigi acp`: top-level alias for the stdio ACP server (kimi-cli `acp`).
AgentArgs.mode is now optional — bare `kigi agent` and `kigi acp` both
default to stdio at dispatch; `kigi acp <mode>` is rejected.
- `kigi mcp auth <name>`: authorize an OAuth-enabled remote MCP server
(kimi-cli `mcp auth`). Reuses the doctor's interactive connection path:
starts the named server with OauthInteractivity::Interactive (browser
flow when required), completes the handshake, and reports the tool
count. Stdio servers and unknown names fail with actionable errors.
- `--mcp-config-file <PATH>` (repeatable, global): extra MCP config files
in the .mcp.json shape ({"mcpServers": {...}}), kimi-cli semantics.
Files are validated at parse time (fail fast on unreadable/invalid
JSON), carried across the TUI -> shell boundary via
KIGI_MCP_CONFIG_FILES, and merged at HIGHEST priority — an explicitly
passed file overrides every config scope. Covered by loader unit tests
and verified live: an injected server surfaces in the session's
x.ai/mcp/servers_updated notification via both the flag and the env.
The stale bare-agent-requires-mode CLI test is re-contracted to the new
default-to-stdio behavior.
web_search now speaks the Kimi search service (kimi-cli tools/web/search.py,
wire-verified against api.kimi.com):
- POST {coding_base}/search with {text_query, limit 1-20 (default 5),
enable_page_crawling, timeout_seconds: 30}, OAuth bearer +
X-Msh-Tool-Call-Id; results render in kimi-cli's Title/Date/URL/Summary
schema with result URLs as citations.
- The old implementation called the xAI Responses API with a search model;
that client is fully replaced and the entire model-based config surface
is excised root-and-branch: web_search_model config keys/env/CLI plumbing,
resolve_web_search_sampling_config, toolset web_search SamplerConfig,
RemoteSettings.web_search_model, default_web_search_model.
- Enablement is now purely structural: the service exists only on the Kimi
Code subscription channel, so OAuth sessions get Enabled and API-key-only
sessions get Disabled (tool absent) — per PRD F5.
web_fetch gains the Kimi fetch service as its primary path (kimi-cli
tools/web/fetch.py): POST {coding_base}/fetch with {url}, Accept:
text/markdown, OAuth bearer + X-Msh-Tool-Call-Id; the 200 body is the
extracted markdown (still overflow-budgeted). Any service failure falls
back to the existing local pipeline (SSRF guards, cache, extraction).
The tool gate defaults ON now (kimi-cli always offers FetchURL) and the
egress User-Agent no longer claims grok-agent/x.ai.
Verified end-to-end against the scripted mock service: a headless session
drove web_search (limit/crawling/call-id observed on the wire) then
web_fetch (Accept + call-id observed) to completion.
The headless not-signed-in message still pointed at `grok login` and
XAI_API_KEY; error copy across the shell (re-auth prompts, managed-config
rejection, mcp doctor, trace classifier) and kigi-bin did too. All now
reference `kigi login` / `kigi logout`, and the headless fallback points
at the Moonshot open-platform key (KIGI_MOONSHOT_API_KEY).
Root cause of today's repeated logouts: the keyring entry (service kigi /
oauth/kimi-code) is global per OS user, but the enable gate keyed off ENV
VARS while an AuthManager's identity is its constructor path. Integration
test binaries (compiled without cfg(test), no KIGI_SHARE_DIR in env)
constructed managers on tempdirs whose remove_scope() then deleted the
developer's REAL keychain credential — the unified log shows 13 such
wipes in one day, one per test run.
Structural fix: keyring participation is now a property of the manager's
own path. AuthManager captures keyring_path_scoped at construction
(path == default ~/.kigi/auth.json) and every keyring touch — the
constructor read, update()'s write, remove_scope()'s delete — requires it,
with the dynamic keyring_enabled() gate (env kill-switch, cfg(test) mock
toggle) layered on top. A tempdir-rooted manager can no longer read,
write, or delete the global entry no matter what process type it runs in.
Regression test tempdir_manager_never_touches_global_keyring pins the
incident: a foreign manager's logout must leave the (mock) keyring entry
intact. Keyring behavior tests keep constructor-read coverage via a
thread-local path-scope test seam.
The live /models wire (verified against api.kimi.com) marks every Kimi
Code model supports_thinking_type: "only" and gives K3 a think_efforts
block {support, valid_efforts: [low, high, max], default_effort: max} —
both of which the F4 sync discarded, hardcoding
supports_reasoning_effort: false. Every effort selection was therefore
rejected with 'current model does not support reasoning effort'.
- kigi-models: WireModel gains supports_thinking_type + WireThinkEfforts;
"only" forces the always_thinking capability.
- models_fetch: think_efforts maps into the catalog entry — wire tokens
stay the option ids/labels (max/Max), canonical values map via the
ReasoningEffort parser (max → Xhigh), default_effort marks the default.
This lights up the existing /model <model> [effort] two-phase completion
and the /effort menu with the server's own vocabulary.
- kimi_compat: the effort level rides the wire as thinking.effort
({"type": "enabled", "effort": "low"} is accepted live; invalid
levels are a 400). Only the canonical-vs-wire spelling divergence
(xhigh → max) is renamed; levels pass through verbatim so a contract
violation surfaces instead of being clamped away.
Live acceptance: kigi -m kimi-code/k3 --reasoning-effort max -p ... round
trips against api.kimi.com, and the refreshed models_cache.json carries
low/high/max with the max default.
The welcome logo (hero box, stacked layout, and minimal's welcome card) is
now a braille moon that waxes and wanes through a full 8s lunation,
echoing the Kimi CLI's moon-phase spinner. The disc is rasterized into the
2x4 braille dot grid at render time — round at any size, no art assets —
with the terminator at x = cos(2*pi*p)*sqrt(1-y^2) and a faint outline
ring so the silhouette survives new moon. The full moon grows to 20x10
cells (from the 14x7 slashed-circle art) and the small tier to 10x5; the
old logo*.txt assets are deleted. Geometry is unit-tested (wax/wane
monotonicity, quarter-phase symmetry, right-limb-first waxing, silhouette
at new moon, round raster).
The welcome hero subtitle still read "Thanks for trying Grok Build";
theme display names, the NO_COLOR/COLORTERM diagnostics notes, the
permission notification title, the self-update restart hints, and the
clipboard-wrap protocol marker all still said Grok. Canonical theme keys
(groknight/grokday) are unchanged — only display strings move.
macOS ld cannot encode >16MB of __eh_frame in its compact unwind table and
says so on every debug link of the large binary. All profiles build with
panic=abort, so Rust never unwinds and the note's exception-handling caveat
does not apply. Allow rust.linker_messages at the workspace level with that
rationale, and give kigi-bin the missing [lints] workspace = true (it was
not inheriting workspace lints at all). Also drop the stale xAI authors
field and Grok wording from the kigi-bin manifest.
Sampler / inference (PRD F3):
- kimi_compat.rs: single adaptation point for the Kimi chat/completions
dialect (thinking-field mapping, model_id stripping, empty-content
tool-call message fix, stream_options.include_usage), with kimi-cli
source citations
- Rate-limit handling reworked for Kimi/Moonshot semantics; UA kigi/{version}
- /models replaces the xAI models-v2 endpoint everywhere; idle model
refresh carries X-Msh-* device headers only (X-XAI-Token-Auth and
x-grok-client-mode/CLIENT_MODE_HEADER machinery deleted)
Cloud-surface excision (PRD §5, zero-egress):
- remote/ conversations lane, cli-chat-proxy-types crate, prod/ dir,
share command, credit bar: deleted (single local session lane;
paginate() replaces merge_and_paginate)
- Subscription/tier gate stack deleted end-to-end: AppView
gate/tier/team/ZDR fields, app/subscription.rs watch loop,
dispatch/billing.rs paywall + SuperGrok upsell, free-usage-exhausted
chain, tier-restricted commands, GateInfo, RemoteSettings gate fields,
SettingsUpdateNotification gate fields
- /privacy + coding-data-sharing setting deleted (backed by a dead xAI
RPC; Kigi is zero-egress — nothing to share or retain remotely)
Auth UX correctness (user-reported):
- Device-flow fixtures now mirror the live Kimi payload shape
(https://www.kimi.com/code/authorize_device?user_code=..., verified
against auth.kimi.com); the fabricated auth.kimi.com/device?code=...
URLs are gone
- open_browser_detached is a no-op under cfg(test): unit tests drove
wiremock fixture URLs into the real browser (root cause of the
"garbage mock link" ABCD-1234 tabs)
- Welcome/pager-minimal rebrand: Grok Build -> Kigi, grok.com ->
kimi.com, "Sign in to Grok" -> "Sign in to Kimi"
F2 — fixed three-platform registry in kigi-models: kimi-code
(subscription, OAuth bearer, base kigi_env::coding_api_base_url()),
moonshot-cn (https://api.moonshot.cn/v1), moonshot-ai
(https://api.moonshot.ai/v1) with kimi-k model-prefix filtering.
Moonshot API keys via KIGI_MOONSHOT_CN_API_KEY / KIGI_MOONSHOT_AI_API_KEY
(+ KIGI_MOONSHOT_API_KEY shared fallback) or ~/.kigi/config.toml;
values redacted from logs/display.
F4 — model catalog now syncs from GET {base}/models (Bearer auth,
wire shape per official kimi-cli: id/context_length/supports_reasoning/
supports_image_in/supports_video_in/display_name) with the official
capability-derivation rules (thinking / always_thinking-in-name /
kimi-k2 implicit set). Managed keys {platform_id}/{model_id}; default
model = first list entry; default thinking iff capabilities contain
thinking/always_thinking. Sync failure → last cache; no cache →
built-in fallback table seeded from ids sourced in official kimi-cli
(kimi-for-coding, kimi-k2-turbo-preview, kimi-k2-thinking-turbo).
401 during sync forces one token refresh and retries.
Model resolution priority preserved: CLI > env > config > server >
fallback. Grok model artifacts (grok-4*/grok-build catalog, tier
gating remnants) removed from non-test code.
All first-party endpoints re-verified live: device_authorization mints
real codes; /models on all three platforms answers with real API auth
errors when unauthenticated.
Gates: check/clippy --all-targets 0/0, fmt clean, deny ok,
kigi-shell lib 5136 green, kigi-tui lib 6819 green, kigi-models 8.