refactor: rewrite from Python to Bun + TypeScript + React Ink#1707
refactor: rewrite from Python to Bun + TypeScript + React Ink#1707Yuandiaodiaodiao wants to merge 28 commits into
Conversation
751b51b to
963d58d
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 751b51bc86
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const stubProvider: LLMProvider = { | ||
| modelName: model.model, | ||
| async *chat(messages: Message[], chatOpts?: ChatOptions) { | ||
| throw new Error( | ||
| `LLM provider "${provider.type}" is not yet implemented in TypeScript. ` + |
There was a problem hiding this comment.
Replace stubbed chat provider with real provider dispatch
createLLM always returns stubProvider, and chat() immediately throws for every configured provider type, so every turn fails as soon as the model is invoked. This makes the CLI unable to generate any assistant response even with valid provider/model config, which is a full functional regression.
Useful? React with 👍 / 👎.
| React.createElement(Shell, { | ||
| modelName: app.soul.modelName, | ||
| onSubmit: (input: string) => { | ||
| app.soul.run(input); | ||
| }, |
There was a problem hiding this comment.
Wire soul events into shell so the interactive UI can update
The shell is rendered with only onSubmit, which calls app.soul.run(...) directly, but no onWireReady/callback bridge is provided to feed useWire with turn/text/tool/approval events. As a result the message list does not reflect agent output, and approval-required tool calls can stall because the UI never receives/returns approval events.
Useful? React with 👍 / 👎.
| contentParts.push({ | ||
| type: "tool_use", | ||
| id: tc.id, | ||
| name: tc.name, | ||
| input: JSON.parse(tc.arguments || "{}"), |
There was a problem hiding this comment.
Handle malformed tool-call JSON without aborting the turn
Tool call arguments are parsed with JSON.parse while assembling assistant context content, and parsing errors are not caught. If a model emits invalid JSON arguments (a common transient/tool-calling failure mode), the whole turn throws before tool execution reaches the existing argument-parse error handling in toolset.handle, causing avoidable turn termination.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1e67aa7e2f
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (options.print && prompt) { | ||
| // Non-interactive print mode | ||
| await app.runPrint(prompt); | ||
| } else { |
There was a problem hiding this comment.
Honor --print mode without requiring positional prompt
The --print flag is documented as non-interactive, but this branch only enters print mode when prompt is also present; kimi --print (or any future stdin-driven print flow) falls through to the interactive Ink shell instead. In automation/non-TTY contexts this causes unexpected hangs or UI startup failures instead of producing print output.
Useful? React with 👍 / 👎.
| const shellPath = process.env.SHELL || "/bin/bash"; | ||
|
|
||
| const proc = Bun.spawn([shellPath, "-c", params.command], { | ||
| stdout: "pipe", |
There was a problem hiding this comment.
Use a Windows-compatible shell fallback
When SHELL is unset, the tool hardcodes /bin/bash as the executable. On Windows, SHELL is commonly absent and /bin/bash usually does not exist, so Shell tool calls fail before running the requested command. Because this commit adds Windows binaries in CI, this breaks core tool functionality on that target.
Useful? React with 👍 / 👎.
| const dirFile = Bun.file(sessionsDir); | ||
| if (!(await dirFile.exists())) return []; |
There was a problem hiding this comment.
Check session directory existence with dir-aware APIs
This uses Bun.file(sessionsDir).exists() to test whether the sessions directory exists, but Bun.file(...).exists() returns false for directories, so Session.list() returns [] even when sessions are present. That makes session discovery/listing logic silently fail for normal repositories.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3732f9cdd7
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| switch (provider.type) { | ||
| case "kimi": | ||
| case "openai_legacy": | ||
| case "openai_responses": | ||
| llmProvider = new OpenAICompatibleProvider({ |
There was a problem hiding this comment.
Normalize OpenAI provider type in LLM dispatch
The provider switch only handles "openai_legacy"/"openai_responses", but config parsing allows "openai" and app.ts passes that value through. In that common configuration, createLLM falls into the default branch and returns a provider that throws on the first chat() call, so OpenAI-backed sessions fail at runtime despite valid config.
Useful? React with 👍 / 👎.
| async function registerBuiltinTools(toolset: KimiToolset): Promise<void> { | ||
| // Import and register all built-in tools | ||
| const toolModules = [ | ||
| () => import("../tools/file/read.ts"), | ||
| () => import("../tools/file/write.ts"), |
There was a problem hiding this comment.
Load tools from agent spec instead of hardcoded list
Agent construction always registers a fixed module list here, so the tool set defined in agents/*/agent.yaml (including allow/exclude controls and declared tools like background/task/plan/Agent) is never applied. This disables configured agent behaviors and subagent/background workflows even though the specs exist in the repository.
Useful? React with 👍 / 👎.
| if (!dirPath.startsWith("/")) { | ||
| return ToolError( | ||
| `\`${params.directory}\` is not an absolute path. You must provide an absolute path to search.`, |
There was a problem hiding this comment.
Accept Windows absolute paths in Glob validation
The absolute-path check only accepts paths starting with /, so Windows drive-letter paths (for example C:\repo) are rejected as non-absolute. On Windows, ctx.workingDir itself uses that format, which makes normal Glob calls fail with the absolute-path error and effectively breaks the tool on that platform.
Useful? React with 👍 / 👎.
| const filePath = configFile ?? getConfigFile(); | ||
| const dir = filePath.substring(0, filePath.lastIndexOf("/")); | ||
| await Bun.$`mkdir -p ${dir}`.quiet(); |
There was a problem hiding this comment.
Use path-aware parent dir handling in saveConfig
Parent directory extraction uses lastIndexOf("/"), which returns -1 for Windows paths with backslashes. When config is first created, this can produce an empty directory string and cause the mkdir -p step to fail before config.toml is written, breaking first-run setup on Windows.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 38cf997b7c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| }); | ||
|
|
||
| try { | ||
| const [response, feedback] = await this._runtime.waitForResponse(requestId); |
There was a problem hiding this comment.
Resolve approval requests before waiting for response
This call can block forever because there is no wiring in this commit that resolves user approvals back into ApprovalRuntime for normal tool flows. I checked the tree for ApprovalRuntime.resolve usage and only found the internal auto-approve branch in approval.ts, so with default yolo=false any tool that requests approval can stall the turn indefinitely while awaiting waitForResponse.
Useful? React with 👍 / 👎.
| // ── WorkDir Metadata ──────────────────────────────────── | ||
|
|
||
| function getSessionsBaseDir(workDir: string): string { | ||
| return join(getShareDir(), "sessions", workDir.replace(/\//g, "_").replace(/^_/, "")); |
There was a problem hiding this comment.
Normalize Windows paths when building sessions base dir
getSessionsBaseDir only rewrites /, so Windows workdirs like C:\repo\proj retain backslashes and the drive-colon in the path segment. That produces invalid/unstable session directory paths on Windows, which breaks Session.create/find/list for those workdirs.
Useful? React with 👍 / 👎.
| // Background mode - stub for now | ||
| return builder.error( | ||
| "Background tasks are not yet implemented in this version.", | ||
| ); |
There was a problem hiding this comment.
Implement or disable run_in_background in Shell tool
The tool schema and description explicitly promote run_in_background=true for long-running commands, but this branch returns an immediate error for every such call. That creates deterministic failures for long tasks the model is guided to run in background, instead of executing or scheduling them.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6e7cae1919
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| yolo: options.yolo, | ||
| sessionId: options.session, | ||
| maxStepsPerTurn: options.maxStepsPerTurn, | ||
| callbacks, | ||
| }); |
There was a problem hiding this comment.
Propagate
--continue into interactive app creation
In interactive mode, KimiCLI.create(...) is called without continueSession, so kimi -C is silently ignored unless --print is also used. This breaks session resume behavior for the default shell workflow and causes users to start a fresh session unexpectedly even when they requested continuation.
Useful? React with 👍 / 👎.
| onSubmit: (input: string) => { | ||
| app.soul.run(input); | ||
| }, |
There was a problem hiding this comment.
Route streaming input to steer instead of dropping it
The shell prompt explicitly allows input while streaming, but onSubmit always calls app.soul.run(input). KimiSoul.run returns early when a turn is already running, so any mid-turn user guidance is discarded instead of being queued as steer input. In practice this makes in-flight correction/steering impossible during long tool runs.
Useful? React with 👍 / 👎.
| const approvalState = new ApprovalState({ | ||
| yolo: | ||
| opts.config.default_yolo || opts.session.state.approval.yolo, | ||
| autoApproveActions: new Set( | ||
| opts.session.state.approval.auto_approve_actions, |
There was a problem hiding this comment.
Persist approval state changes back into session state
Approval settings are loaded from session.state.approval into a separate ApprovalState, but no change hook writes updates back to session.state. As a result, /yolo toggles and approve_for_session decisions are lost on shutdown/resume because Session.saveState() persists stale approval data.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 302656023c
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const callbacks: SoulCallbacks = { | ||
| onTextDelta: (text) => process.stdout.write(text), | ||
| onThinkDelta: (text) => process.stderr.write(chalk.dim(text)), | ||
| onError: (err) => | ||
| process.stderr.write(chalk.red(`[ERROR] ${err.message}\n`)), |
There was a problem hiding this comment.
Honor print mode formatting flags
In the --print branch, output is hardwired to raw text streaming callbacks, so --output-format stream-json and --final-message-only are never applied. This causes automation that relies on JSON lines or final-only output to receive the wrong format even when those flags are explicitly set.
Useful? React with 👍 / 👎.
| // Determine config source: --config-file takes precedence over legacy --config as path | ||
| const configFile = options.configFile ?? undefined; | ||
|
|
There was a problem hiding this comment.
Parse inline --config payload instead of ignoring it
The CLI accepts --config <string>, but this code only forwards options.configFile and never consumes options.config (nor calls loadConfigFromString). As a result, users providing inline TOML/JSON config get silently ignored settings.
Useful? React with 👍 / 👎.
| if (opts.maxStepsPerTurn) { | ||
| config.loop_control.max_steps_per_turn = opts.maxStepsPerTurn; | ||
| } |
There was a problem hiding this comment.
Apply max-retries override in app creation
CLI call sites pass maxRetriesPerStep, but KimiCLI.create only applies max_steps_per_turn and never updates loop_control.max_retries_per_step. This makes --max-retries-per-step a no-op, so retry behavior stays at config defaults regardless of user input.
Useful? React with 👍 / 👎.
| const file = Bun.file(path); | ||
| // Synchronous existence check is not available — use a simple approach | ||
| return file.size > 0 ? null : null; // Will be refined when plan tools exist |
There was a problem hiding this comment.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 94b7b71d07
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const planTarget = inspectPlanEditTarget(resolvedPath, { | ||
| planModeChecker: this._planModeChecker ?? ctx.getPlanMode, | ||
| planFilePathGetter: this._planFilePathGetter, | ||
| }); |
There was a problem hiding this comment.
Pass current plan path into WriteFile plan-mode guard
When plan mode is active, inspectPlanEditTarget requires a plan file path, but this call only passes this._planFilePathGetter and does not fall back to runtime context. Because tool registration never calls bindPlanMode, that getter stays unset, so WriteFile returns the “plan file is unavailable” error for every plan-mode write and blocks the core plan workflow from progressing.
Useful? React with 👍 / 👎.
| }); | ||
|
|
||
| // 6. Load agent | ||
| const agent = await loadAgent({ runtime }); |
There was a problem hiding this comment.
Honor CLI agent selection when constructing the runtime agent
Agent loading is hardcoded to loadAgent({ runtime }), so runtime always uses the default agent spec. Since --agent and --agent-file are parsed in cli/index.ts but KimiCLI.create has no corresponding inputs to forward, those user-facing flags are effectively no-ops across print/wire/interactive modes.
Useful? React with 👍 / 👎.
| const gitFile = Bun.file(`${current}/.git`); | ||
| if (await gitFile.exists()) return current; | ||
| const parent = dirname(current); |
There was a problem hiding this comment.
Detect git roots with directory-aware filesystem checks
This root-detection logic relies on Bun.file(...).exists() for .git, but Bun reports false for directories, so standard repos with a .git/ directory are not recognized as project roots. The walk then falls back to workDir as root, which skips parent-scope AGENTS.md files and breaks the intended root-to-leaf instruction merge.
Useful? React with 👍 / 👎.
|
???真的还有语言之真啊 ? ? ? There really is truth in language |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac1d84c76e
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| wireToolContext(): void { | ||
| const ctx = this.agent.toolset.context; | ||
| ctx.setPlanMode = (on: boolean) => this.setPlanMode(on); | ||
| ctx.getPlanMode = () => this._planMode; | ||
| ctx.getPlanFilePath = () => this.getPlanFilePath() ?? undefined; | ||
| ctx.togglePlanMode = () => this.togglePlanMode(); |
There was a problem hiding this comment.
Expose askUser bridge when wiring tool context
wireToolContext() only assigns plan-mode helpers and never assigns ctx.askUser. I checked this commit for other ctx.askUser = call sites and found none, so AskUserQuestion and plan-review paths always fall back to auto/default answers even in interactive shell mode. This bypasses intended user confirmations and can make the agent proceed with an unintended plan or option.
Useful? React with 👍 / 👎.
| const file = Bun.file(sessionDir); | ||
| if (await file.exists()) { | ||
| await Bun.$`rm -rf ${sessionDir}`.quiet(); |
There was a problem hiding this comment.
Delete sessions with directory-aware existence checks
Session.delete() checks a session directory using Bun.file(sessionDir).exists(), which is a file-oriented API and can return false for directories in this Bun migration. When that happens, empty sessions are never removed during shutdown, so stale session folders accumulate and session housekeeping no longer matches expected behavior.
Useful? React with 👍 / 👎.
| inputData: { | ||
| session_id: _currentSessionId, | ||
| tool_name: name, |
There was a problem hiding this comment.
Populate hook session_id before running tool hooks
Tool hook payloads read session_id from _currentSessionId, but this module initializes it to an empty string and this commit has no setSessionId(...) call site. As a result, Pre/Post tool hooks always receive an empty session id, which breaks per-session policy checks and audit correlation for hook consumers.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ae8f85b6fc
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const paths = [ | ||
| `src/kimi_cli/agents/${agentName}/system.md`, | ||
| `agents/${agentName}/system.md`, |
There was a problem hiding this comment.
Resolve system prompt path from package directory
loadSystemPrompt looks for src/kimi_cli/agents/... and agents/... using process-relative paths, so when the CLI is launched from a normal project directory (not the repo root) those files are not found and the code silently falls back to the minimal hardcoded prompt. That drops the intended bundled agent instructions for most real runs (including packaged/binary installs), which can materially change tool-use behavior and response quality.
Useful? React with 👍 / 👎.
| .option("--agent-file <path>", "Custom agent specification file") | ||
| .option("--mcp-config-file <path...>", "MCP config file(s) to load") | ||
| .option("--mcp-config <json...>", "MCP config JSON to load") | ||
| .option("--command <cmd>", "Run a single shell command and exit") |
There was a problem hiding this comment.
Implement --command behavior instead of advertising a no-op
The CLI exposes --command <cmd> as “Run a single shell command and exit”, but this option is never consumed in the action handler (only declared in options typing), so invocations like kimi --command "..." still follow normal print/wire/interactive flows. That is a functional mismatch that can break automation expecting one-shot execution.
Useful? React with 👍 / 👎.
| if (!searchPath.startsWith("/")) { | ||
| searchPath = `${ctx.workingDir}/${searchPath}`; | ||
| } |
There was a problem hiding this comment.
Treat Windows absolute paths as absolute in Grep
This absolute-path check only accepts /..., so Windows paths like C:\repo are treated as relative and rewritten to ${ctx.workingDir}/C:\repo, which causes ripgrep to search an invalid location. In Windows environments (which this migration targets), that breaks Grep for common absolute-path inputs.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0b25be06cb
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| planMode: options.plan, | ||
| sessionId: resolvedSessionId, | ||
| continueSession: options.continue, | ||
| maxStepsPerTurn: options.maxStepsPerTurn ?? options.maxRetriesPerStep, |
There was a problem hiding this comment.
Stop mapping retry flag into max-steps override
This wiring makes --max-retries-per-step change max_steps_per_turn instead of retry behavior: when users pass retries (for example --max-retries-per-step 1) and do not set --max-steps-per-turn, turns are capped to one step and frequently terminate before tool-result follow-up can happen. Because KimiCLI.create has no separate retry override input, this also means the retry flag cannot work as intended at all.
Useful? React with 👍 / 👎.
| const app = await KimiCLI.create({ | ||
| workDir: options.workDir, | ||
| additionalDirs: options.addDir, | ||
| configFile, | ||
| modelName: options.model, |
There was a problem hiding this comment.
Forward --plan when creating interactive sessions
In the interactive/shell branch, the app is created without passing planMode: options.plan, so kimi --plan starts with plan mode disabled in the default UI path and only print/wire branches honor the flag. This creates mode-dependent behavior for the same CLI option and breaks expected startup state for interactive users.
Useful? React with 👍 / 👎.
| } | ||
| return filePath; | ||
| } | ||
| return `${workingDir}/${filePath}`; |
There was a problem hiding this comment.
Resolve Windows absolute paths correctly in ReadFile
The fallback path join treats any non-/ path as relative, so on Windows a drive-letter absolute path like C:\\repo\\a.txt is rewritten to ${workingDir}/C:\\repo\\a.txt and reported missing. This breaks core file-reading for Windows users whenever the model or user supplies absolute paths.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ee821c9595
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| ): Promise<ToolResult> { | ||
| const timeout = params.timeout ?? undefined; | ||
| try { | ||
| const runner = new ForegroundSubagentRunner(runtime); |
There was a problem hiding this comment.
Initialize subagent runtime services before Agent execution
AgentTool._runForeground always constructs ForegroundSubagentRunner here, but Runtime.create never populates runtime.subagentStore/runtime.laborMarket/runtime.approvalRuntime (see src/kimi_cli/soul/agent.ts), and the runner constructor hard-fails when subagentStore is missing (src/kimi_cli/subagents/runner.ts). I searched the repo for any later assignment to these runtime fields and only found reads, so foreground Agent calls deterministically return an error instead of launching a subagent.
Useful? React with 👍 / 👎.
| const controller = new AbortController(); | ||
| const timer = setTimeout(() => controller.abort(), timeout * 1000); | ||
| try { | ||
| return await runner.run(req); |
There was a problem hiding this comment.
Apply Agent timeout to the foreground subagent run
The timeout branch creates an AbortController and timer, but the signal is never passed into runner.run(req) (or any callee), so aborting the controller has no effect and timeout is silently ignored. Any long-running foreground subagent task can exceed the user-specified timeout indefinitely, despite the tool schema and docs promising enforced limits.
Useful? React with 👍 / 👎.
| // Check abort before each tool, but don't interrupt mid-append | ||
| if (this.abortController?.signal.aborted) break; | ||
|
|
||
| const result = await this.agent.toolset.handle(tc); |
There was a problem hiding this comment.
Emit tool_result callbacks after tool execution
After toolset.handle(tc) returns, this path appends the tool result to context but never calls callbacks.onToolResult; meanwhile KimiToolset's own onToolResult hook is not wired when constructed in loadAgent. In interactive shell mode, useWire relies on tool_result events to attach completion/error payloads to tool calls, so users never see per-tool results even though execution finished.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fe44b011b0
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const proc = Bun.spawn([shellPath, "-c", wrappedCommand], { | ||
| stdout: "pipe", | ||
| stderr: "pipe", // stderr still piped for safety (but most goes to stdout via 2>&1) | ||
| stdin: "pipe", | ||
| cwd: ctx.workingDir, |
There was a problem hiding this comment.
Use OS-appropriate shell invocation for command execution
This spawn call hardcodes -c, which only works for POSIX shells; on Windows the default shell is typically PowerShell/cmd and does not accept this invocation, so Shell tool calls fail before running the command. In Windows environments this makes core command execution unavailable for normal tool usage.
Useful? React with 👍 / 👎.
| function resolvePath(filePath: string, workingDir: string): string { | ||
| if (filePath.startsWith("/") || filePath.startsWith("~")) { |
There was a problem hiding this comment.
Treat Windows drive-letter paths as absolute in WriteFile
resolvePath only recognizes /... and ~... as absolute, so a Windows absolute path like C:\repo\file.txt is treated as relative and rewritten under workingDir. That produces invalid targets and causes WriteFile to fail for common Windows absolute-path inputs.
Useful? React with 👍 / 👎.
| this._tokenCount = usage.inputTokens + (usage.cacheReadTokens ?? 0); | ||
| this._pendingTokenEstimate = 0; | ||
| const record: UsageRecord = { | ||
| _usage: { | ||
| input_tokens: usage.inputTokens, |
There was a problem hiding this comment.
Persist cache-read tokens in saved context usage records
The in-memory token counter includes cacheReadTokens, but the persisted _usage record only writes input_tokens/output_tokens. After restore, token count is rebuilt from input_tokens only, so sessions that use prompt caching undercount context usage and can delay compaction until requests exceed model context limits.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f4ab7da272
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| callbacks, | ||
| }); | ||
|
|
||
| if (prompt) await app.runPrint(prompt); |
There was a problem hiding this comment.
Read print-mode input from stdin when prompt is absent
This branch only runs app.runPrint(...) when prompt is present, so kimi --print with stdin input exits without processing any user command. That breaks non-interactive workflows that rely on piped input (including --input-format stream-json, which is advertised as print-mode input) because the CLI creates/shuts down a session but never executes a turn.
Useful? React with 👍 / 👎.
| output = ""; | ||
| stderrStr = ""; |
There was a problem hiding this comment.
Preserve ripgrep output on timeout before returning
On timeout, this code clears output and stderrStr immediately after killing the process. That makes the later timeout handler always take the "no output" error path, so partial matches are never returned even when ripgrep already produced results before timing out. In long searches, users lose usable partial data despite the code path intending to support partial results.
Useful? React with 👍 / 👎.
|
LGTM😂 |
f4ab7da to
75e849d
Compare
| this._tokenCount = usage.inputTokens + (usage.cacheReadTokens ?? 0); | ||
| this._pendingTokenEstimate = 0; | ||
| const record: UsageRecord = { | ||
| _usage: { | ||
| input_tokens: usage.inputTokens, | ||
| output_tokens: usage.outputTokens, | ||
| }, | ||
| }; | ||
| await this._appendToFile(record); |
There was a problem hiding this comment.
🔴 Context token count double-counts cache tokens, inflating context window estimate
updateTokenCount sets _tokenCount = usage.inputTokens + (usage.cacheReadTokens ?? 0). For OpenAI-compatible APIs (including Kimi), inputTokens (from prompt_tokens) already includes cached tokens — cacheReadTokens is a subset of inputTokens, not additional tokens. Adding them double-counts, inflating the context estimate by up to 2×. This causes premature compaction via shouldCompact() at src/kimi_cli_ts/soul/compaction.ts:220-223. The restore method at src/kimi_cli_ts/soul/context.ts:92 correctly uses just record._usage.input_tokens without adding cache tokens, confirming this is inconsistent.
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 75e849d6bd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const dirFile = Bun.file(sessionsDir); | ||
| if (!(await dirFile.exists())) return []; |
There was a problem hiding this comment.
Check session directories with a directory-aware API
Session.list bails out early whenever Bun.file(sessionsDir).exists() is false, but Bun reports false for directories, so this path returns an empty list even when session folders are present. In practice this breaks session discovery flows that depend on Session.list (e.g. picker mode and continue_ fallback), making existing sessions appear missing.
Useful? React with 👍 / 👎.
| const stat = await Bun.$`stat -f %m ${this.contextFile} 2>/dev/null || stat -c %Y ${this.contextFile} 2>/dev/null`.quiet().text(); | ||
| this.updatedAt = Number.parseFloat(stat.trim()) || 0; |
There was a problem hiding this comment.
Parse mtime without mixing incompatible stat outputs
This cross-platform command emits non-numeric stdout on GNU/Linux from the stat -f %m branch before falling back to stat -c %Y, so parseFloat(stat.trim()) resolves to NaN and updatedAt collapses to 0. As a result, session recency sorting becomes unreliable and resume/continue paths that expect “most recent” ordering can pick the wrong session.
Useful? React with 👍 / 👎.
|
无敌了,不知道会不会火,先合个影 Invincible. I don’t know if it will be popular. Let’s take a photo together |
75e849d to
08b03ed
Compare
Complete rewrite of the CLI from Python to TypeScript/Bun with React Ink UI, including OpenAI-compatible LLM provider, slash commands, tool system, subagent support, and all upstream Python fixes synced from main. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
08b03ed to
6d69435
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 08b03ed067
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| type: "tool_use", | ||
| id: tc.id, | ||
| name: tc.name, | ||
| input: JSON.parse(tc.arguments || "{}"), |
There was a problem hiding this comment.
Guard tool-call argument parsing before appending assistant msg
This JSON.parse runs on model-provided tool arguments before tool execution, so a single malformed argument payload causes _step() to throw and abort the turn. In that case the tool never reaches toolset.handle(...), which already has graceful argument-parse error handling, so recoverable tool-call formatting issues become hard failures that stop execution.
Useful? React with 👍 / 👎.
| typeof value.prototype.toDefinition === "function" | ||
| ) { | ||
| try { | ||
| const instance = new (value as new () => any)(); |
There was a problem hiding this comment.
Initialize plan tools with the active session id
Tools are instantiated generically with a no-arg constructor here, but EnterPlanMode/ExitPlanMode default their internal session ID to "default" when no ID is provided (src/kimi_cli_ts/tools/plan/plan.ts). That makes plan files effectively global instead of per-session, so plan approval/review can read or overwrite another session’s plan content.
Useful? React with 👍 / 👎.
| ): Promise<HookResult> { | ||
| const timeout = (opts?.timeout ?? 30) * 1000; | ||
| try { | ||
| const proc = Bun.spawn(["sh", "-c", command], { |
There was a problem hiding this comment.
Use platform-appropriate shell to run hook commands
Hook execution is hardcoded to sh -c, which fails on Windows environments that do not provide a POSIX sh. In those setups, configured hook policies (including PreToolUse) never actually execute, so approval/policy enforcement via hooks is silently bypassed.
Useful? React with 👍 / 👎.
|
震撼首发👍 Shocking first release👍 |
| } finally { | ||
| reader.releaseLock(); | ||
| } |
There was a problem hiding this comment.
🔴 Pending tool calls silently dropped when SSE stream ends without finish_reason
In OpenAICompatibleProvider.chat, tool call deltas are accumulated in pendingToolCalls (line 517-520) and only yielded when a chunk with finish_reason is received (line 598-608). After the stream reading loop exits (line 611), the code proceeds directly to emitting usage and done chunks (line 616-628) without checking for remaining entries in pendingToolCalls. If the stream ends without a finish_reason chunk (e.g. connection drop, API quirk, or the [DONE] sentinel arrives before finish_reason), all accumulated tool calls are silently lost. The caller (KimiSoul._step) would see zero tool calls, end the agent turn prematurely, and the LLM's tool call intent would be invisible to the user.
Missing flush after stream loop
Add after line 613 (reader.releaseLock()):
// Flush any remaining pending tool calls
for (const [, tc] of pendingToolCalls) {
yield { type: "tool_call", id: tc.id, name: tc.name, arguments: tc.arguments };
}
pendingToolCalls.clear();Was this helpful? React with 👍 or 👎 to provide feedback.
…fy UI panels Refactor the main agent loop in kimisoul.ts for clarity, enhance grep tool with advanced options and ask_user with multi-question support, simplify QuestionPanel/SelectionPanel UI, and improve subagent runner and CLI option handling.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 090fa31e52
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| if (result === "cancelled") { | ||
| logger.debug("Cancelling the run"); | ||
| // The soul should check the abort signal and stop | ||
| // We need to wait for it to actually stop | ||
| throw new RunCancelled(); |
There was a problem hiding this comment.
Abort active soul run before returning RunCancelled
When cancellation wins the race, this branch throws RunCancelled without signaling the soul to stop, so KimiSoul.run() can continue executing LLM/tool work in the background after the UI path exits. In interactive Ctrl+C scenarios this leaves the prior turn running until natural completion (or timeout), which can block subsequent turns and produce late writes/errors against a shutting-down wire. Triggering soul.abort() before exiting this path is needed to make cancellation effective.
Useful? React with 👍 / 👎.
| contentParts.push({ | ||
| type: "tool_use", | ||
| id: tc.id, | ||
| name: tc.name, | ||
| input: JSON.parse(tc.arguments || "{}"), |
There was a problem hiding this comment.
Guard tool-use argument parsing against malformed JSON
This eagerly parses streamed tool-call arguments with JSON.parse(...) while building the assistant message. If the model emits malformed/incomplete arguments (a common transient in streamed tool calls), _step() throws before tool execution, so the turn fails instead of returning a normal tool-parse error. Since tool argument parsing is already handled defensively in toolset.handle(), this unchecked parse introduces a hard crash path for otherwise recoverable tool calls.
Useful? React with 👍 / 👎.
| } | ||
| return filePath; | ||
| } | ||
| return resolve(workingDir, filePath); |
There was a problem hiding this comment.
Block parent traversal in StrReplaceFile relative paths
Relative paths are normalized with resolve(workingDir, filePath) but never constrained back to the workspace root, so inputs like ../../.ssh/config are accepted as valid edit targets. The tool contract says absolute paths are required outside the working directory, yet this allows escaping the workspace via relative traversal and modifying unintended files.
Useful? React with 👍 / 👎.
| const stat = await Bun.$`stat -f %m ${this.contextFile} 2>/dev/null || stat -c %Y ${this.contextFile} 2>/dev/null`.quiet().text(); | ||
| this.updatedAt = Number.parseFloat(stat.trim()) || 0; |
There was a problem hiding this comment.
🔴 stat -f %m returns wrong data on Linux, breaking session timestamps
On macOS, stat -f %m <file> returns the file modification time in epoch seconds. On Linux (GNU coreutils), -f means "display file system status" and %m means "maximum filename length" (typically 255). Since this command succeeds on Linux (exit code 0), the || fallback to stat -c %Y never executes. The result is that this.updatedAt gets a filesystem metadata value (e.g., 255) instead of the actual modification timestamp.
This causes incorrect session ordering in Session.list() (which sorts by updatedAt), the /sessions panel, and the session picker (--session flag).
| const stat = await Bun.$`stat -f %m ${this.contextFile} 2>/dev/null || stat -c %Y ${this.contextFile} 2>/dev/null`.quiet().text(); | |
| this.updatedAt = Number.parseFloat(stat.trim()) || 0; | |
| const stat = await Bun.$`stat -c %Y ${this.contextFile} 2>/dev/null || stat -f %m ${this.contextFile} 2>/dev/null`.quiet().text(); | |
| this.updatedAt = Number.parseFloat(stat.trim()) || 0; |
Was this helpful? React with 👍 or 👎 to provide feedback.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 947ab80e6b
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| const flowCommand: SlashCommand = { | ||
| name: commandName, | ||
| description: skill.description || "", | ||
| handler: this._makeSkillRunner(skill), | ||
| aliases: [], |
There was a problem hiding this comment.
Execute flow-skill commands with FlowRunner
For flow skills, /flow:<name> is wired to this._makeSkillRunner(skill), but that runner only reads SKILL.md and calls _turn(...) once, so decision edges/loop control in skill.flow are never executed. In practice, flow skills behave like plain prompt injections instead of graph-driven workflows, which breaks any skill relying on branch selection or repeated iterations.
Useful? React with 👍 / 👎.
| // Execute tools and collect results — shielded from abort | ||
| await this._executeToolsShielded(toolCalls); |
There was a problem hiding this comment.
Re-check abort before dispatching tool calls
Cancellation is only checked before _step(), but not after it returns. If the abort signal is raised while _step() is in progress (e.g. during model streaming) and tool calls are produced, this branch still executes those tools, so a cancelled turn can still run side-effecting commands/filesystem edits. Add an abort guard before calling _executeToolsShielded(...) to prevent post-cancel tool execution.
Useful? React with 👍 / 👎.
| let planMode = opts.planMode ?? false; | ||
| if (!opts.resumed) { | ||
| planMode = planMode || config.default_plan_mode; | ||
| } |
There was a problem hiding this comment.
Apply default plan mode after session restore status
The plan-mode default is computed from opts.resumed before session lookup sets the local resumed flag, so resumed sessions are treated as “new” during this decision. With default_plan_mode=true, resuming an existing session that previously had plan mode off will be forced back into plan mode on startup, overriding persisted session behavior.
Useful? React with 👍 / 👎.
| // Read the embedded binary content | ||
| const file = Bun.file(embeddedRgPath); | ||
| const buffer = new Uint8Array(file.size); | ||
| const reader = file.stream().getReader(); | ||
| let offset = 0; |
There was a problem hiding this comment.
🟡 Stream reader resource leak and dead code in extractEmbeddedRg
At lines 61-64, Bun.file(embeddedRgPath).stream().getReader() acquires a read lock on the stream, but the reader is never released via releaseLock() or consumed. The function immediately falls through to fs.readFileSync at line 69 instead. The buffer (line 62), reader (line 63), and offset (line 64) variables are allocated but never used — this is leftover dead code from an incomplete refactor. The unreleased reader holds a lock on the underlying stream, constituting a resource leak (though mitigated by the function only being called once at first grep invocation).
| // Read the embedded binary content | |
| const file = Bun.file(embeddedRgPath); | |
| const buffer = new Uint8Array(file.size); | |
| const reader = file.stream().getReader(); | |
| let offset = 0; | |
| // Synchronous-ish extraction: we use a blocking pattern since this | |
| // only runs once on first grep invocation. | |
| // Use writeFileSync with the embedded path directly via fs. | |
| const content = fs.readFileSync(embeddedRgPath); |
Was this helpful? React with 👍 or 👎 to provide feedback.
947ab80 to
9f9b2dc
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9f9b2dcccd
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } | ||
| return filePath; | ||
| } | ||
| return resolve(workingDir, filePath); |
There was a problem hiding this comment.
Block parent traversal in WriteFile relative paths
The relative-path resolver currently does resolve(workingDir, filePath) without checking that the result stays under workingDir, so inputs like ../../.ssh/config are accepted and can overwrite files outside the workspace while still using a relative path. This breaks the tool contract that absolute paths are required for out-of-workdir writes and creates an unintended arbitrary file-write path.
Useful? React with 👍 / 👎.
| const file = Bun.file(sessionDir); | ||
| if (await file.exists()) { | ||
| await Bun.$`rm -rf ${sessionDir}`.quiet(); |
There was a problem hiding this comment.
Use directory-aware existence check before deleting session
Session.delete() gates removal on Bun.file(sessionDir).exists(), but Bun.file(...).exists() returns false for directories, so the rm -rf branch is skipped for normal session directories. In practice, empty sessions that shutdown tries to clean up are never deleted, causing stale session accumulation and breaking expected cleanup behavior.
Useful? React with 👍 / 👎.
…mports, fix print mode Structural alignment: - Add missing barrel index.ts for all modules (approval_runtime, background, hooks, plugin, subagents, soul/dynamic_injections, tools/*, utils) - Extract share.ts, session_state.ts from config.ts/session.ts - Rename tool files to index.ts for symmetry (agent.ts→index.ts, etc.) - Add new modules: acp/, vis/, web/, cli/toad.ts, tools/plan/enter.ts, tools/test.ts - Add 13 missing utils (datetime, editor, envvar, frontmatter, io, etc.) Bug fixes: - Fix circular import cli→app→soul→cli by extracting cli/errors.ts - Lazy-load heavy modules (react, ink, app, soul) in CLI handler - Fix print mode to match Python's rich.pretty_repr output format Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…entation Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…I and LLM support Major changes across the TS codebase: expanded toolset with MCP integration, enhanced wire server/serde/jsonrpc, improved LLM multi-provider support, new TaskPanel UI component, CLI command improvements (init/model/info), and various soul layer refinements (context, kimisoul, agent). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tion Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…baseline build target Remove 6 unused cell-level diffing files (ansi-parser, screen, types, diff, patch-writer, csi) from ui/renderer/ and clean up their re-exports from index.ts. Add build:linux-x64-baseline script to package.json. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0a60a91fee
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| // Accept and register external tool | ||
| accepted.push(tool.name); | ||
| } |
There was a problem hiding this comment.
Register accepted external tools with the toolset
This initialize path reports external tools as accepted but never actually registers them (accepted.push(...) is the only action). Because KimiToolset.registerExternalTool(...) is never called, the accepted tools are not added to the model-visible tool list, so wire clients that send external_tools get a successful handshake but those tools cannot be invoked in later turns.
Useful? React with 👍 / 👎.
| return runWithWireContext(wire, async () => { | ||
| const streamPromise = streamCallback(wire); | ||
| try { | ||
| await app.soul.run(userInput as string | ContentPart[]); | ||
| return "finished"; |
There was a problem hiding this comment.
Respect cancel events in wire-mode prompt execution
Wire cancellation is effectively ignored here: the server passes a cancelEvent into onPrompt, but this implementation immediately awaits app.soul.run(...) without observing that signal. In wire clients, a cancel RPC can be acknowledged while the turn keeps running (including tool calls), which breaks expected interrupt semantics for long-running prompts.
Useful? React with 👍 / 👎.
| const app = await KimiCLI.create({ | ||
| workDir: options.workDir, | ||
| additionalDirs: options.addDir, | ||
| configFile, | ||
| modelName: options.model, |
There was a problem hiding this comment.
Pass --config text into print-mode app creation
The CLI advertises --config as inline TOML/JSON config, but print mode only forwards configFile and drops options.config entirely. As a result, kimi --print --config '...' silently uses default/file config instead of the provided override, which can select the wrong model/provider/settings in automation.
Useful? React with 👍 / 👎.
| if (prompt) { | ||
| // Echo user input to stdout (matches Python Print.run() line 89) |
There was a problem hiding this comment.
Read stdin in print mode when no prompt is provided
Print mode only executes a run inside if (prompt), and prompt is derived from positional args/--prompt earlier, so piped stdin input is never consumed. This breaks non-interactive usage like echo "..." | kimi --print and also prevents --input-format stream-json from processing line-delimited user messages when no explicit prompt argument is passed.
Useful? React with 👍 / 👎.
…ython version Add structured error classes (APIConnectionError, APITimeoutError, APIStatusError, APIEmptyResponseError), RetryableChatProvider interface, stream timeout mechanisms, and connection recovery logic to match the Python kosong/kimisoul implementation. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6cc2f7ea53
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
| } | ||
| return filePath; | ||
| } | ||
| return resolve(workingDir, filePath); |
There was a problem hiding this comment.
Block parent traversal in ReadMediaFile relative paths
The path resolver accepts any non-absolute input via resolve(workingDir, filePath) without checking that the result stays under the workspace. That means inputs like ../../private/image.png are treated as valid relative paths and can read files outside workingDir, even though the tool contract says out-of-workdir access must use absolute paths. In environments where this tool is exposed to model-generated arguments, this becomes an unintended data-exfiltration path.
Useful? React with 👍 / 👎.
|
? |
|
already have https://github.com/MoonshotAI/kimi-code |
kimicli用python是彻底的失败 立刻重构为ts
Summary
Complete rewrite of kimi-cli from Python to Bun + TypeScript + React Ink, delivering a fully functional terminal-native AI agent CLI.
SWE-bench Verified Results
kimi-k2.5 on SWE-bench Verified (500 tasks), comparing Python (kimi-cli) vs TypeScript (kimi-cli-ts):
Resolved tasks — Python kimi-cli (325)
astropy__astropy-12907astropy__astropy-13453astropy__astropy-13579astropy__astropy-14096astropy__astropy-14182astropy__astropy-14309astropy__astropy-14508astropy__astropy-14539astropy__astropy-14995astropy__astropy-7166astropy__astropy-7336astropy__astropy-7671django__django-10914django__django-10973django__django-11066django__django-11095django__django-11099django__django-11119django__django-11133django__django-11138django__django-11149django__django-11179django__django-11206django__django-11211django__django-11265django__django-11276django__django-11292django__django-11299django__django-11451django__django-11490django__django-11532django__django-11551django__django-11555django__django-11603django__django-11728django__django-11740django__django-11749django__django-11790django__django-11815django__django-11848django__django-11880django__django-11999django__django-12039django__django-12050django__django-12125django__django-12155django__django-12193django__django-12209django__django-12273django__django-12276django__django-12304django__django-12419django__django-12663django__django-12708django__django-12713django__django-12774django__django-12858django__django-12965django__django-13012django__django-13023django__django-13028django__django-13033django__django-13089django__django-13109django__django-13112django__django-13121django__django-13128django__django-13158django__django-13279django__django-13297django__django-13315django__django-13343django__django-13344django__django-13346django__django-13363django__django-13410django__django-13417django__django-13449django__django-13516django__django-13551django__django-13568django__django-13569django__django-13590django__django-13670django__django-13741django__django-13786django__django-13809django__django-13810django__django-13820django__django-13821django__django-13837django__django-13925django__django-13933django__django-13964django__django-14007django__django-14089django__django-14122django__django-14238django__django-14311django__django-14349django__django-14351django__django-14373django__django-14434django__django-14493django__django-14500django__django-14534django__django-14539django__django-14559django__django-14580django__django-14608django__django-14631django__django-14672django__django-14752django__django-14765django__django-14771django__django-14787django__django-14792django__django-14855django__django-14915django__django-14999django__django-15022django__django-15037django__django-15104django__django-15127django__django-15128django__django-15161django__django-15268django__django-15277django__django-15278django__django-15315django__django-15375django__django-15380django__django-15382django__django-15467django__django-15499django__django-15525django__django-15554django__django-15561django__django-15569django__django-15572django__django-15731django__django-15732django__django-15741django__django-15814django__django-15851django__django-15863django__django-15916django__django-15930django__django-15987django__django-16032django__django-16082django__django-16136django__django-16139django__django-16255django__django-16333django__django-16429django__django-16485django__django-16493django__django-16527django__django-16560django__django-16569django__django-16595django__django-16612django__django-16661django__django-16662django__django-16801django__django-16819django__django-16877django__django-16899django__django-16901django__django-16938django__django-16950django__django-17029django__django-17084django__django-17087django__django-7530django__django-9296matplotlib__matplotlib-13989matplotlib__matplotlib-14623matplotlib__matplotlib-20859matplotlib__matplotlib-21568matplotlib__matplotlib-22719matplotlib__matplotlib-23412matplotlib__matplotlib-24570matplotlib__matplotlib-24637matplotlib__matplotlib-25122matplotlib__matplotlib-25311matplotlib__matplotlib-25775matplotlib__matplotlib-25960matplotlib__matplotlib-26291matplotlib__matplotlib-26342mwaskom__seaborn-3069pallets__flask-5014psf__requests-1142psf__requests-1724psf__requests-1766pydata__xarray-2905pydata__xarray-3151pydata__xarray-3305pydata__xarray-3677pydata__xarray-3993pydata__xarray-4075pydata__xarray-4094pydata__xarray-4356pydata__xarray-4695pydata__xarray-4966pydata__xarray-6461pydata__xarray-6599pydata__xarray-7233pydata__xarray-7393pylint-dev__pylint-8898pytest-dev__pytest-10051pytest-dev__pytest-10081pytest-dev__pytest-5262pytest-dev__pytest-5631pytest-dev__pytest-5809pytest-dev__pytest-6197pytest-dev__pytest-6202pytest-dev__pytest-7205pytest-dev__pytest-7236pytest-dev__pytest-7324pytest-dev__pytest-7432pytest-dev__pytest-7521pytest-dev__pytest-7571pytest-dev__pytest-7982pytest-dev__pytest-8399scikit-learn__scikit-learn-10297scikit-learn__scikit-learn-11578scikit-learn__scikit-learn-12585scikit-learn__scikit-learn-12682scikit-learn__scikit-learn-12973scikit-learn__scikit-learn-13135scikit-learn__scikit-learn-13142scikit-learn__scikit-learn-13328scikit-learn__scikit-learn-13439scikit-learn__scikit-learn-13496scikit-learn__scikit-learn-13779scikit-learn__scikit-learn-14053scikit-learn__scikit-learn-14087scikit-learn__scikit-learn-14141scikit-learn__scikit-learn-14629scikit-learn__scikit-learn-14710scikit-learn__scikit-learn-14894scikit-learn__scikit-learn-14983scikit-learn__scikit-learn-15100scikit-learn__scikit-learn-25102scikit-learn__scikit-learn-25931scikit-learn__scikit-learn-25973scikit-learn__scikit-learn-26323scikit-learn__scikit-learn-9288sphinx-doc__sphinx-10449sphinx-doc__sphinx-10466sphinx-doc__sphinx-10673sphinx-doc__sphinx-7757sphinx-doc__sphinx-7889sphinx-doc__sphinx-7910sphinx-doc__sphinx-8035sphinx-doc__sphinx-8120sphinx-doc__sphinx-8269sphinx-doc__sphinx-8475sphinx-doc__sphinx-8548sphinx-doc__sphinx-8551sphinx-doc__sphinx-8593sphinx-doc__sphinx-8595sphinx-doc__sphinx-8621sphinx-doc__sphinx-8721sphinx-doc__sphinx-9230sphinx-doc__sphinx-9258sphinx-doc__sphinx-9281sphinx-doc__sphinx-9591sphinx-doc__sphinx-9658sphinx-doc__sphinx-9673sphinx-doc__sphinx-9698sphinx-doc__sphinx-9711sympy__sympy-11618sympy__sympy-12096sympy__sympy-12419sympy__sympy-12481sympy__sympy-13031sympy__sympy-13480sympy__sympy-13551sympy__sympy-13615sympy__sympy-13647sympy__sympy-13757sympy__sympy-13877sympy__sympy-13878sympy__sympy-14531sympy__sympy-14711sympy__sympy-14976sympy__sympy-15345sympy__sympy-15349sympy__sympy-15599sympy__sympy-15809sympy__sympy-15976sympy__sympy-16766sympy__sympy-16792sympy__sympy-16886sympy__sympy-17139sympy__sympy-17655sympy__sympy-18189sympy__sympy-18211sympy__sympy-19346sympy__sympy-19495sympy__sympy-19637sympy__sympy-19783sympy__sympy-19954sympy__sympy-20154sympy__sympy-20590sympy__sympy-20801sympy__sympy-21379sympy__sympy-21612sympy__sympy-21847sympy__sympy-22714sympy__sympy-22914sympy__sympy-23262sympy__sympy-23413sympy__sympy-23534sympy__sympy-23824sympy__sympy-23950sympy__sympy-24066sympy__sympy-24213sympy__sympy-24443sympy__sympy-24539sympy__sympy-24562sympy__sympy-24661Resolved tasks — TypeScript kimi-cli-ts (317)
astropy__astropy-12907astropy__astropy-13453astropy__astropy-13579astropy__astropy-14096astropy__astropy-14309astropy__astropy-14365astropy__astropy-14508astropy__astropy-14539astropy__astropy-14995astropy__astropy-7336astropy__astropy-7671django__django-10880django__django-10914django__django-11066django__django-11095django__django-11099django__django-11119django__django-11133django__django-11138django__django-11149django__django-11163django__django-11206django__django-11211django__django-11265django__django-11276django__django-11292django__django-11299django__django-11333django__django-11451django__django-11490django__django-11532django__django-11551django__django-11555django__django-11603django__django-11728django__django-11740django__django-11749django__django-11815django__django-11880django__django-11951django__django-11999django__django-12039django__django-12050django__django-12125django__django-12143django__django-12155django__django-12209django__django-12262django__django-12273django__django-12276django__django-12304django__django-12419django__django-12663django__django-12713django__django-12741django__django-12754django__django-12774django__django-12858django__django-13012django__django-13023django__django-13028django__django-13033django__django-13089django__django-13109django__django-13112django__django-13121django__django-13128django__django-13158django__django-13279django__django-13297django__django-13343django__django-13344django__django-13346django__django-13363django__django-13401django__django-13410django__django-13417django__django-13516django__django-13551django__django-13568django__django-13569django__django-13590django__django-13658django__django-13670django__django-13741django__django-13786django__django-13807django__django-13809django__django-13810django__django-13820django__django-13821django__django-13837django__django-13933django__django-13964django__django-14007django__django-14017django__django-14053django__django-14089django__django-14122django__django-14238django__django-14311django__django-14349django__django-14373django__django-14434django__django-14493django__django-14500django__django-14534django__django-14539django__django-14559django__django-14580django__django-14608django__django-14631django__django-14672django__django-14725django__django-14752django__django-14765django__django-14771django__django-14787django__django-14792django__django-14855django__django-14915django__django-14999django__django-15037django__django-15104django__django-15127django__django-15161django__django-15268django__django-15277django__django-15278django__django-15315django__django-15368django__django-15380django__django-15382django__django-15467django__django-15499django__django-15503django__django-15525django__django-15561django__django-15569django__django-15572django__django-15731django__django-15732django__django-15741django__django-15814django__django-15851django__django-15863django__django-15930django__django-15957django__django-15987django__django-16032django__django-16082django__django-16116django__django-16136django__django-16139django__django-16145django__django-16255django__django-16315django__django-16333django__django-16429django__django-16485django__django-16493django__django-16527django__django-16569django__django-16612django__django-16642django__django-16661django__django-16662django__django-16801django__django-16819django__django-16899django__django-16901django__django-16938django__django-16950django__django-17029django__django-17087django__django-7530django__django-9296matplotlib__matplotlib-13989matplotlib__matplotlib-14623matplotlib__matplotlib-21568matplotlib__matplotlib-22719matplotlib__matplotlib-24570matplotlib__matplotlib-24637matplotlib__matplotlib-25122matplotlib__matplotlib-25332matplotlib__matplotlib-26291matplotlib__matplotlib-26342matplotlib__matplotlib-26466mwaskom__seaborn-3069pallets__flask-5014psf__requests-1142psf__requests-1921pydata__xarray-3095pydata__xarray-3151pydata__xarray-3677pydata__xarray-4075pydata__xarray-4094pydata__xarray-4356pydata__xarray-4629pydata__xarray-4695pydata__xarray-7233pydata__xarray-7393pylint-dev__pylint-8898pytest-dev__pytest-10051pytest-dev__pytest-10081pytest-dev__pytest-5262pytest-dev__pytest-5631pytest-dev__pytest-5809pytest-dev__pytest-6202pytest-dev__pytest-7205pytest-dev__pytest-7432pytest-dev__pytest-7490pytest-dev__pytest-7521pytest-dev__pytest-7571pytest-dev__pytest-7982pytest-dev__pytest-8399scikit-learn__scikit-learn-10908scikit-learn__scikit-learn-11578scikit-learn__scikit-learn-12585scikit-learn__scikit-learn-12973scikit-learn__scikit-learn-13142scikit-learn__scikit-learn-13328scikit-learn__scikit-learn-13779scikit-learn__scikit-learn-14053scikit-learn__scikit-learn-14141scikit-learn__scikit-learn-14496scikit-learn__scikit-learn-14629scikit-learn__scikit-learn-14710scikit-learn__scikit-learn-14894scikit-learn__scikit-learn-14983scikit-learn__scikit-learn-25102scikit-learn__scikit-learn-25232scikit-learn__scikit-learn-25747scikit-learn__scikit-learn-25931scikit-learn__scikit-learn-25973scikit-learn__scikit-learn-9288sphinx-doc__sphinx-10449sphinx-doc__sphinx-10466sphinx-doc__sphinx-10673sphinx-doc__sphinx-7440sphinx-doc__sphinx-7454sphinx-doc__sphinx-7757sphinx-doc__sphinx-7889sphinx-doc__sphinx-7910sphinx-doc__sphinx-8035sphinx-doc__sphinx-8120sphinx-doc__sphinx-8269sphinx-doc__sphinx-8459sphinx-doc__sphinx-8475sphinx-doc__sphinx-8551sphinx-doc__sphinx-8593sphinx-doc__sphinx-8595sphinx-doc__sphinx-8621sphinx-doc__sphinx-8638sphinx-doc__sphinx-8721sphinx-doc__sphinx-9230sphinx-doc__sphinx-9258sphinx-doc__sphinx-9281sphinx-doc__sphinx-9320sphinx-doc__sphinx-9591sphinx-doc__sphinx-9658sphinx-doc__sphinx-9673sphinx-doc__sphinx-9698sphinx-doc__sphinx-9711sympy__sympy-12096sympy__sympy-12419sympy__sympy-12481sympy__sympy-13031sympy__sympy-13091sympy__sympy-13372sympy__sympy-13480sympy__sympy-13551sympy__sympy-13615sympy__sympy-13647sympy__sympy-13757sympy__sympy-13877sympy__sympy-13878sympy__sympy-13974sympy__sympy-14976sympy__sympy-15017sympy__sympy-15345sympy__sympy-15349sympy__sympy-15599sympy__sympy-15809sympy__sympy-15875sympy__sympy-16450sympy__sympy-16766sympy__sympy-16792sympy__sympy-16886sympy__sympy-17318sympy__sympy-17655sympy__sympy-18189sympy__sympy-18211sympy__sympy-19040sympy__sympy-19346sympy__sympy-19495sympy__sympy-19637sympy__sympy-19783sympy__sympy-19954sympy__sympy-20154sympy__sympy-20590sympy__sympy-20801sympy__sympy-21379sympy__sympy-21612sympy__sympy-21847sympy__sympy-21930sympy__sympy-22456sympy__sympy-22714sympy__sympy-22914sympy__sympy-23262sympy__sympy-23534sympy__sympy-23824sympy__sympy-23950sympy__sympy-24066sympy__sympy-24213sympy__sympy-24443sympy__sympy-24661Tech Stack Migration
What's Implemented
Core Agent Loop (
soul/)Tools (12 categories)
Subagent System (
subagents/)UI - React Ink TUI (
ui/)@file mention with fuzzy searchInfrastructure
~/.kimi/config.toml~/.kimi/sessions/(no console pollution)Architecture
Quick Start