* fix(web): activate slash skills on the new-session screen The composer now lists workspace skills before a session exists, but activating one from the empty-session composer silently did nothing: activateSkill() short-circuits when there is no active session id, so the command was cleared with no turn started. Mirror the first-prompt path: when a slash skill is activated with no active session (but a workspace is active), create the session first and activate on the new session id. Extract the shared session-creation block out of startSessionAndSendPrompt into createDraftSession and add a startSessionAndActivateSkill counterpart; activateSkill now also accepts an explicit session id so a concurrent session switch can't redirect it. Unrecognized '/cmd' text still falls through to a plain-text message as before. * fix(web): persist draft modes when a new session is opened via skill The skill-activate request carries only `args`, so for a skill launched from the new-session composer createDraftSession's local plan/swarm maps never reached the daemon, and the first skill turn ran at default modes while the UI showed them enabled. Persist the draft plan/swarm modes to the new session's profile inside createDraftSession (by the new session id), and teach persistSessionProfile to take an explicit session id so a concurrent session switch during the snapshot load can't write the patch to the wrong session. Plain prompts already send planMode/swarmMode on the prompt request itself, so this is redundant but harmless on the first-prompt path. Goal mode is a one-shot flag consumed per send, not a profile field, so there is nothing to persist for it. Add coverage that the profile is persisted before activation when draft plan/swarm modes are enabled. * fix(web): await draft-mode profile POST before activating a skill The previous fix wrote the draft plan/swarm modes to the new session's profile but fire-and-forget: persistSessionProfile returned immediately after starting POST /profile, so startSessionAndActivateSkill sent :activate without waiting. Since skill activation carries only args, the daemon could process :activate (and start the turn) before applyAgentState from /profile finished, running the first skill turn at default modes while the UI showed them enabled. Make persistSessionProfile return the update promise and await it inside createDraftSession, so any origin that follows (the skill activation, or a plain prompt) only starts after the profile is applied. Existing callers still fire-and-forget via `void persistSessionProfile(...)`. Test coverage now blocks activation behind a deferred profile POST and asserts it is issued only after the profile resolves. * refactor(web): persist draft modes on the skill path only Move the awaited plan/swarm profile write out of createDraftSession and into startSessionAndActivateSkill. The create path now only builds the session and mirrors draft modes into the per-session maps, matching the old first-prompt behavior: plain prompts already send planMode/swarmMode on the prompt request itself, so a /profile write there was redundant. The profile write stays on the one path that needs it: skill activation carries only args, so the draft modes must be stored on the new session and applied before :activate is sent. * fix(web): persist draft permission and thinking for new-session skill /auto, /yolo, and /thinking on the new-session composer only update rawState (there is no session to persist to yet). A plain first prompt still honors them because submitPromptInternal sends permissionMode and thinking on the request, but skill activation carries only args, so the first skill turn was running at daemon defaults. Include permissionMode and thinking in the awaited profile patch alongside planMode/swarmMode before activating, so the skill turn matches the controls the UI shows. * fix(web): start /goal from the new-session composer createGoal() short-circuited when there was no active session, so `/goal <objective>` from the empty-session composer silently cleared and ran nothing — the same bug class as the slash-skill activation fixed earlier in this PR. Mirror startSessionAndSendPrompt: when no session exists but a workspace is active, create one first then target it with the goal profile update and the objective prompt. Send via submitPromptInternal with the explicit sid so a concurrent session switch during creation can't redirect it. Plain prompts already carry their own permissionMode / thinking / plan / swarm, so no profile fallback is needed here. * fix(web): use active-workspace fallback for empty-composer /goal On a fresh-booted empty workspace, load() never writes rawState.activeWorkspaceId (no most-recent session to anchor it). The UI still has a usable workspace via the client-wide activeWorkspaceId computed, which falls back to the first sidebar-visible workspace — but createGoal read the raw value directly and silently no-op'd when it was null. Normal first prompts and skill activations didn't hit this because App.vue passes the computed activeWorkspaceId in. Make createGoal use the same fallback so a first-session `/goal <objective>` works in empty workspaces too. * fix(web): start /btw from the new-session composer openSideChat() reads rawState.activeSessionId directly, so `/btw [<question>]` from the empty-session composer silently no-oped — it still set detailTarget to 'btw', leaving the side chat panel open but empty. Add startSessionAndOpenSideChat(workspaceId, prompt?) that creates the parent session first, then calls a new sideChat.openSideChatOn(sid, prompt) which targets the explicit parent session id (race-safe against a concurrent session switch, like the skill activation case). Route through it from the empty-composer branch in openSideChatTab. Side-chat prompts now also carry model / thinking / permissionMode / plan / swarm (via a shared sendSideChatPromptOn), so a BTW first turn matches the UI even when the parent /profile is still in flight. Unlike skill activation, this means the BTW path needs no profile fallback. Tests: startSessionAndOpenSideChat creates a session then opens BTW on the new id, works without an initial question, and is a no-op for an unknown workspace; sendSideChatPromptOn carries the runtime controls on the submitted prompt. * fix(web): preserve send queue when creating goals Switching createGoal from sendPrompt to submitPromptInternal avoided the activeSessionId race during the empty-composer create window, but it also bypassed sendPrompt's queue guard: when a goal is created against an already-active session that is running another turn, the prompt posted immediately instead of being locally queued. Restore the guard for the overwhelmingly common case (the goal still targets the active session): route through sendPrompt when activeSessionId still matches the resolved sid, which enqueues when the session is running or a prompt is already in flight. Only fall back to the explicit- session submitPromptInternal(sid) when activeSessionId moved during the create window, so a concurrent session switch can't redirect the goal prompt. The newly-created session is idle+not-in-flight in that branch, so the explicit submit does not race another turn. Add a regression test: createGoal against a running existing session enqueues instead of submits. * fix(web): coerce thinking for skill/BTW and handle goal creation failures Three follow-ups from review: - createGoal now wraps createDraftSession in a try/catch: App.vue invokes it fire-and-forget, so a rejection from session creation previously leaked as an unhandled rejection with no operation failure surfaced. Mirrors the skill / BTW / first-prompt paths that already wrap it. - startSessionAndActivateSkill coerces the draft thinking level against the new session's model before persisting the profile (via coercePromptThinking), matching what the first-prompt path submits. A value carried over from another/default model (e.g. 'max' from an effort model) would otherwise be persisted verbatim and the first skill turn would run at a level the UI wouldn't send for this model. - sendSideChatPromptOn coerces thinking against the parent session's model the same way normal prompts do (coerceThinkingForModel against the model catalog). Model catalog threaded in via a new 'models' dep on useSideChat. Same reasoning: stale rawState.thinking must not be submitted raw into a BTW first turn. * fix(web): clear staged goal mode when submitting explicit /goal When the empty composer has goal mode staged (e.g. the user runs bare `/goal`, then `/goal <objective>`) and an explicit objective is then submitted, createDraftSession copies draftModes.goalMode into goalModeBySession[sid]. createGoal then updateSession(goalObjective) for the explicit goal, and sendPrompt(trimmed) re-enters submitPromptInternal which sees goalModeBySession[sid] still set and POSTs a second goalObjective. The daemon rejects that as an existing goal, the catch in submitPromptInternal rolls back the optimistic message, and the user's objective prompt never lands. Clear the staged goalModeBySession[sid] flag right after the explicit update, since `/goal <objective>` has exactly the same effect as the flag's consumption. |
||
|---|---|---|
| .agents/skills | ||
| .changeset | ||
| .github | ||
| apps | ||
| build | ||
| docs | ||
| packages | ||
| plugins | ||
| scripts | ||
| .editorconfig | ||
| .gitattributes | ||
| .gitignore | ||
| .npmrc | ||
| .nvmrc | ||
| .oxfmtrc.json | ||
| .oxlintrc.json | ||
| AGENTS.md | ||
| CLAUDE.md | ||
| CONTRIBUTING.md | ||
| flake.lock | ||
| flake.nix | ||
| LICENSE | ||
| Makefile | ||
| package.json | ||
| pnpm-lock.yaml | ||
| pnpm-workspace.yaml | ||
| README.md | ||
| README.zh-CN.md | ||
| SECURITY.md | ||
| tsconfig.json | ||
| vitest.config.ts | ||
Kimi Code CLI
Documentation · Issues · 中文
What is Kimi Code CLI
Kimi Code CLI is an AI coding agent that runs in your terminal — it can read and edit code, run shell commands, search files, fetch web pages, and choose the next step based on the feedback it receives. It works out of the box with Moonshot AI’s Kimi models and can also be configured to use other compatible providers.
Install
Install with the official script. No Node.js required.
- macOS or Linux:
curl -fsSL https://code.kimi.com/kimi-code/install.sh | bash
- Homebrew (macOS/Linux):
brew install kimi-code
- Windows (PowerShell):
irm https://code.kimi.com/kimi-code/install.ps1 | iex
On Windows, install Git for Windows before first launch because Kimi Code CLI uses the bundled Git Bash as its shell environment. If Git Bash is installed in a custom location, set
KIMI_SHELL_PATHto the absolute path ofbash.exe.
Then, run it with a new shell session:
kimi --version
For npm install, upgrade, uninstall, see Getting Started.
Quick Start
Open a project and start the interactive UI:
cd your-project
kimi
On first launch, run /login inside Kimi Code CLI and choose either Kimi Code OAuth or a Moonshot AI Open Platform API key. After login, try your first task:
Take a look at this project and explain its main directories.
Key Features
- Single-binary distribution. Install with one command: no Node.js setup, PATH gymnastics, or global module conflicts.
- Blazing-fast startup. The TUI is ready in milliseconds, so starting a session never feels heavy.
- Purpose-built TUI. A carefully tuned interface, optimized end to end for long, focused agent sessions.
- Video input. Drop a screen recording or demo clip into the chat and let the agent watch what is hard to describe in words — turn a reference clip into a LUT, a long video into a short, a screen recording into working code, and more.
- AI-native MCP configuration. Add, edit, and authenticate Model Context Protocol servers conversationally with
/mcp-config, without hand-editing JSON. - Rich plugin ecosystem. Install skills, MCP servers, and data sources from the marketplace or any GitHub repo, with each install's trust level surfaced up front.
- Subagents for focused, parallel work. Dispatch built-in
coder,explore, andplansubagents in isolated contexts while keeping the main conversation clean. - Lifecycle hooks. Run local commands at key points to gate risky tool calls, audit decisions, trigger desktop notifications, or connect to your own automation.
- Editor & IDE integration (ACP). Drive a Kimi Code CLI session straight from Zed, JetBrains, or any Agent Client Protocol client with
kimi acp.
Use it in your editor (ACP)
Kimi Code CLI speaks the Agent Client Protocol, so ACP-compatible editors and IDEs (Zed, JetBrains, …) can drive a session over stdio. Log in once, then point your editor at the kimi acp subcommand — no extra login needed.
For Zed, add this to ~/.config/zed/settings.json:
{
"agent_servers": {
"Kimi Code CLI": {
"type": "custom",
"command": "kimi",
"args": ["acp"],
"env": {}
}
}
}
Then open a new conversation in Zed's Agent panel. See Using in IDEs for JetBrains setup and troubleshooting, and the kimi acp reference for the full capability matrix.
Docs
- Getting Started
- Interaction and approvals
- Sessions
- Using in IDEs (ACP)
- Configuration
- Command reference
Develop
Requirements: Node.js ≥ 24.15.0, pnpm 10.33.0.
git clone https://github.com/MoonshotAI/kimi-code.git
cd kimi-code
pnpm install
pnpm dev:cli # run the CLI in dev mode
pnpm test # run tests
pnpm typecheck # TypeScript check
pnpm lint # oxlint
pnpm build # build all packages
See CONTRIBUTING.md for the full contribution guide.
Community
- Issues
- For security vulnerabilities, see SECURITY.md.
Acknowledgements
Our TUI is built on top of pi-tui. We thank the authors of pi-tui for their valuable work.
License
Released under the MIT License.
