Full Code of MoonshotAI/kimi-cli for AI

main 8283d785a6b1 cached
818 files
4.7 MB
1.3M tokens
5340 symbols
1 requests
Download .txt
Showing preview only (5,022K chars total). Download the full file or copy to clipboard to get everything.
Repository: MoonshotAI/kimi-cli
Branch: main
Commit: 8283d785a6b1
Files: 818
Total size: 4.7 MB

Directory structure:
gitextract_akc1nk5y/

├── .agents/
│   └── skills/
│       ├── codex-worker/
│       │   └── SKILL.md
│       ├── feature-smoke-test/
│       │   ├── SKILL.md
│       │   ├── references/
│       │   │   └── prompt-patterns.md
│       │   └── scripts/
│       │       └── inspect_session.py
│       ├── gen-changelog/
│       │   └── SKILL.md
│       ├── gen-docs/
│       │   └── SKILL.md
│       ├── gen-rust/
│       │   └── SKILL.md
│       ├── pull-request/
│       │   └── SKILL.md
│       ├── release/
│       │   └── SKILL.md
│       ├── translate-docs/
│       │   └── SKILL.md
│       └── worktree-status/
│           └── SKILL.md
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1-bug-report.yml
│   │   ├── 2-feature-request.yml
│   │   └── config.yml
│   ├── actions/
│   │   └── macos-code-sign/
│   │       └── action.yml
│   ├── dependabot.yml
│   ├── pr-title-checker-config.json
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci-docs.yml
│       ├── ci-kimi-cli.yml
│       ├── ci-kimi-sdk.yml
│       ├── ci-kosong.yml
│       ├── ci-pykaos.yml
│       ├── docs-pages.yml
│       ├── pr-title-checker.yml
│       ├── release-kimi-cli.yml
│       ├── release-kimi-sdk.yml
│       ├── release-kosong.yml
│       ├── release-pykaos.yml
│       ├── translator.yml
│       └── typos.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── AGENTS.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── SECURITY.md
├── docs/
│   ├── .gitignore
│   ├── .pre-commit-config.yaml
│   ├── .vitepress/
│   │   ├── config.ts
│   │   └── theme/
│   │       ├── index.ts
│   │       └── style.css
│   ├── AGENTS.md
│   ├── en/
│   │   ├── configuration/
│   │   │   ├── config-files.md
│   │   │   ├── data-locations.md
│   │   │   ├── env-vars.md
│   │   │   ├── overrides.md
│   │   │   └── providers.md
│   │   ├── customization/
│   │   │   ├── agents.md
│   │   │   ├── mcp.md
│   │   │   ├── print-mode.md
│   │   │   ├── skills.md
│   │   │   └── wire-mode.md
│   │   ├── faq.md
│   │   ├── guides/
│   │   │   ├── getting-started.md
│   │   │   ├── ides.md
│   │   │   ├── integrations.md
│   │   │   ├── interaction.md
│   │   │   ├── sessions.md
│   │   │   └── use-cases.md
│   │   ├── index.md
│   │   ├── reference/
│   │   │   ├── keyboard.md
│   │   │   ├── kimi-acp.md
│   │   │   ├── kimi-command.md
│   │   │   ├── kimi-info.md
│   │   │   ├── kimi-mcp.md
│   │   │   ├── kimi-term.md
│   │   │   ├── kimi-vis.md
│   │   │   ├── kimi-web.md
│   │   │   └── slash-commands.md
│   │   └── release-notes/
│   │       ├── breaking-changes.md
│   │       └── changelog.md
│   ├── index.md
│   ├── package.json
│   ├── scripts/
│   │   └── sync-changelog.mjs
│   └── zh/
│       ├── configuration/
│       │   ├── config-files.md
│       │   ├── data-locations.md
│       │   ├── env-vars.md
│       │   ├── overrides.md
│       │   └── providers.md
│       ├── customization/
│       │   ├── agents.md
│       │   ├── mcp.md
│       │   ├── print-mode.md
│       │   ├── skills.md
│       │   └── wire-mode.md
│       ├── faq.md
│       ├── guides/
│       │   ├── getting-started.md
│       │   ├── ides.md
│       │   ├── integrations.md
│       │   ├── interaction.md
│       │   ├── sessions.md
│       │   └── use-cases.md
│       ├── index.md
│       ├── reference/
│       │   ├── keyboard.md
│       │   ├── kimi-acp.md
│       │   ├── kimi-command.md
│       │   ├── kimi-info.md
│       │   ├── kimi-mcp.md
│       │   ├── kimi-term.md
│       │   ├── kimi-vis.md
│       │   ├── kimi-web.md
│       │   └── slash-commands.md
│       └── release-notes/
│           ├── breaking-changes.md
│           └── changelog.md
├── examples/
│   ├── .gitignore
│   ├── custom-echo-soul/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── custom-kimi-soul/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── custom-tools/
│   │   ├── README.md
│   │   ├── main.py
│   │   ├── my_tools/
│   │   │   ├── __init__.py
│   │   │   └── ls.py
│   │   ├── myagent.yaml
│   │   └── pyproject.toml
│   ├── kimi-cli-stream-json/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── kimi-cli-wire-messages/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── kimi-psql/
│   │   ├── README.md
│   │   ├── agent.yaml
│   │   ├── main.py
│   │   └── pyproject.toml
│   └── sample-plugin/
│       ├── SKILL.md
│       ├── plugin.json
│       └── scripts/
│           ├── calc.ts
│           └── greet.py
├── flake.nix
├── kimi.spec
├── klips/
│   ├── .pre-commit-config.yaml
│   ├── klip-0-klip.md
│   ├── klip-1-kimi-cli-monorepo.md
│   ├── klip-10-agent-flow.md
│   ├── klip-11-kimi-code-rename.md
│   ├── klip-12-wire-initialize-external-tools.md
│   ├── klip-14-kimi-code-oauth-login.md
│   ├── klip-15-kagent-sidecar-integration.md
│   ├── klip-2-acpkaos.md
│   ├── klip-3-kimi-cli-user-docs.md
│   ├── klip-6-setup-auto-refresh-models.md
│   ├── klip-7-kimi-sdk.md
│   ├── klip-8-config-and-skills-layout.md
│   └── klip-9-shell-ui-flicker-mitigation.md
├── packages/
│   ├── kaos/
│   │   ├── .pre-commit-config.yaml
│   │   ├── CHANGELOG.md
│   │   ├── LICENSE
│   │   ├── NOTICE
│   │   ├── README.md
│   │   ├── pyproject.toml
│   │   ├── src/
│   │   │   └── kaos/
│   │   │       ├── __init__.py
│   │   │       ├── _current.py
│   │   │       ├── local.py
│   │   │       ├── path.py
│   │   │       ├── py.typed
│   │   │       └── ssh.py
│   │   └── tests/
│   │       ├── test_kaos_path.py
│   │       ├── test_local_kaos.py
│   │       ├── test_local_kaos_cmd.py
│   │       ├── test_local_kaos_sh.py
│   │       └── test_ssh_kaos.py
│   ├── kimi-code/
│   │   ├── pyproject.toml
│   │   └── src/
│   │       └── kimi_code/
│   │           └── __init__.py
│   └── kosong/
│       ├── .pre-commit-config.yaml
│       ├── CHANGELOG.md
│       ├── LICENSE
│       ├── NOTICE
│       ├── README.md
│       ├── pyproject.toml
│       ├── src/
│       │   └── kosong/
│       │       ├── __init__.py
│       │       ├── __main__.py
│       │       ├── _generate.py
│       │       ├── chat_provider/
│       │       │   ├── __init__.py
│       │       │   ├── chaos.py
│       │       │   ├── echo/
│       │       │   │   ├── __init__.py
│       │       │   │   ├── dsl.py
│       │       │   │   ├── echo.py
│       │       │   │   └── scripted_echo.py
│       │       │   ├── kimi.py
│       │       │   ├── mock.py
│       │       │   └── openai_common.py
│       │       ├── contrib/
│       │       │   ├── __init__.py
│       │       │   ├── chat_provider/
│       │       │   │   ├── __init__.py
│       │       │   │   ├── anthropic.py
│       │       │   │   ├── common.py
│       │       │   │   ├── google_genai.py
│       │       │   │   ├── openai_legacy.py
│       │       │   │   └── openai_responses.py
│       │       │   └── context/
│       │       │       ├── __init__.py
│       │       │       └── linear.py
│       │       ├── message.py
│       │       ├── py.typed
│       │       ├── tooling/
│       │       │   ├── __init__.py
│       │       │   ├── empty.py
│       │       │   ├── error.py
│       │       │   ├── mcp.py
│       │       │   └── simple.py
│       │       └── utils/
│       │           ├── __init__.py
│       │           ├── aio.py
│       │           ├── jsonschema.py
│       │           └── typing.py
│       └── tests/
│           ├── api_snapshot_tests/
│           │   ├── common.py
│           │   ├── test_anthropic.py
│           │   ├── test_google_genai.py
│           │   ├── test_kimi.py
│           │   ├── test_openai_legacy.py
│           │   └── test_openai_responses.py
│           ├── test_chat_provider.py
│           ├── test_context.py
│           ├── test_echo_chat_provider.py
│           ├── test_generate.py
│           ├── test_json_schema_deref.py
│           ├── test_kimi_stream_usage.py
│           ├── test_message.py
│           ├── test_openai_common.py
│           ├── test_scripted_echo_chat_provider.py
│           ├── test_step.py
│           ├── test_tool_call.py
│           └── test_tool_result.py
├── pyproject.toml
├── pytest.ini
├── scripts/
│   ├── build_vis.py
│   ├── build_web.py
│   ├── check_kimi_dependency_versions.py
│   ├── check_version_tag.py
│   ├── cleanup_tmp_sessions.py
│   ├── install.ps1
│   └── install.sh
├── sdks/
│   └── kimi-sdk/
│       ├── CHANGELOG.md
│       ├── LICENSE
│       ├── NOTICE
│       ├── README.md
│       ├── pyproject.toml
│       ├── src/
│       │   └── kimi_sdk/
│       │       ├── __init__.py
│       │       └── py.typed
│       └── tests/
│           └── test_smoke.py
├── src/
│   └── kimi_cli/
│       ├── __init__.py
│       ├── __main__.py
│       ├── acp/
│       │   ├── AGENTS.md
│       │   ├── __init__.py
│       │   ├── convert.py
│       │   ├── kaos.py
│       │   ├── mcp.py
│       │   ├── server.py
│       │   ├── session.py
│       │   ├── tools.py
│       │   ├── types.py
│       │   └── version.py
│       ├── agents/
│       │   ├── default/
│       │   │   ├── agent.yaml
│       │   │   ├── sub.yaml
│       │   │   └── system.md
│       │   └── okabe/
│       │       └── agent.yaml
│       ├── agentspec.py
│       ├── app.py
│       ├── auth/
│       │   ├── __init__.py
│       │   ├── oauth.py
│       │   └── platforms.py
│       ├── background/
│       │   ├── __init__.py
│       │   ├── ids.py
│       │   ├── manager.py
│       │   ├── models.py
│       │   ├── store.py
│       │   ├── summary.py
│       │   └── worker.py
│       ├── cli/
│       │   ├── __init__.py
│       │   ├── __main__.py
│       │   ├── _lazy_group.py
│       │   ├── export.py
│       │   ├── info.py
│       │   ├── mcp.py
│       │   ├── plugin.py
│       │   ├── toad.py
│       │   ├── vis.py
│       │   └── web.py
│       ├── config.py
│       ├── constant.py
│       ├── deps/
│       │   └── Makefile
│       ├── exception.py
│       ├── llm.py
│       ├── metadata.py
│       ├── notifications/
│       │   ├── __init__.py
│       │   ├── llm.py
│       │   ├── manager.py
│       │   ├── models.py
│       │   ├── notifier.py
│       │   ├── store.py
│       │   └── wire.py
│       ├── plugin/
│       │   ├── __init__.py
│       │   ├── manager.py
│       │   └── tool.py
│       ├── prompts/
│       │   ├── __init__.py
│       │   ├── compact.md
│       │   └── init.md
│       ├── py.typed
│       ├── session.py
│       ├── session_state.py
│       ├── share.py
│       ├── skill/
│       │   ├── __init__.py
│       │   └── flow/
│       │       ├── __init__.py
│       │       ├── d2.py
│       │       └── mermaid.py
│       ├── skills/
│       │   ├── kimi-cli-help/
│       │   │   └── SKILL.md
│       │   └── skill-creator/
│       │       └── SKILL.md
│       ├── soul/
│       │   ├── __init__.py
│       │   ├── agent.py
│       │   ├── approval.py
│       │   ├── compaction.py
│       │   ├── context.py
│       │   ├── denwarenji.py
│       │   ├── dynamic_injection.py
│       │   ├── dynamic_injections/
│       │   │   ├── __init__.py
│       │   │   └── plan_mode.py
│       │   ├── kimisoul.py
│       │   ├── message.py
│       │   ├── slash.py
│       │   └── toolset.py
│       ├── tools/
│       │   ├── AGENTS.md
│       │   ├── __init__.py
│       │   ├── ask_user/
│       │   │   ├── __init__.py
│       │   │   └── description.md
│       │   ├── background/
│       │   │   ├── __init__.py
│       │   │   ├── list.md
│       │   │   ├── output.md
│       │   │   └── stop.md
│       │   ├── display.py
│       │   ├── dmail/
│       │   │   ├── __init__.py
│       │   │   └── dmail.md
│       │   ├── file/
│       │   │   ├── __init__.py
│       │   │   ├── glob.md
│       │   │   ├── glob.py
│       │   │   ├── grep.md
│       │   │   ├── grep_local.py
│       │   │   ├── plan_mode.py
│       │   │   ├── read.md
│       │   │   ├── read.py
│       │   │   ├── read_media.md
│       │   │   ├── read_media.py
│       │   │   ├── replace.md
│       │   │   ├── replace.py
│       │   │   ├── utils.py
│       │   │   ├── write.md
│       │   │   └── write.py
│       │   ├── multiagent/
│       │   │   ├── __init__.py
│       │   │   ├── create.md
│       │   │   ├── create.py
│       │   │   ├── task.md
│       │   │   └── task.py
│       │   ├── plan/
│       │   │   ├── __init__.py
│       │   │   ├── description.md
│       │   │   ├── enter.py
│       │   │   ├── enter_description.md
│       │   │   └── heroes.py
│       │   ├── shell/
│       │   │   ├── __init__.py
│       │   │   ├── bash.md
│       │   │   └── powershell.md
│       │   ├── test.py
│       │   ├── think/
│       │   │   ├── __init__.py
│       │   │   └── think.md
│       │   ├── todo/
│       │   │   ├── __init__.py
│       │   │   └── set_todo_list.md
│       │   ├── utils.py
│       │   └── web/
│       │       ├── __init__.py
│       │       ├── fetch.md
│       │       ├── fetch.py
│       │       ├── search.md
│       │       └── search.py
│       ├── ui/
│       │   ├── __init__.py
│       │   ├── acp/
│       │   │   └── __init__.py
│       │   ├── print/
│       │   │   ├── __init__.py
│       │   │   └── visualize.py
│       │   └── shell/
│       │       ├── __init__.py
│       │       ├── console.py
│       │       ├── debug.py
│       │       ├── echo.py
│       │       ├── export_import.py
│       │       ├── keyboard.py
│       │       ├── mcp_status.py
│       │       ├── oauth.py
│       │       ├── placeholders.py
│       │       ├── prompt.py
│       │       ├── replay.py
│       │       ├── setup.py
│       │       ├── slash.py
│       │       ├── startup.py
│       │       ├── task_browser.py
│       │       ├── update.py
│       │       ├── usage.py
│       │       └── visualize.py
│       ├── utils/
│       │   ├── __init__.py
│       │   ├── aiohttp.py
│       │   ├── aioqueue.py
│       │   ├── broadcast.py
│       │   ├── changelog.py
│       │   ├── clipboard.py
│       │   ├── datetime.py
│       │   ├── diff.py
│       │   ├── editor.py
│       │   ├── environment.py
│       │   ├── envvar.py
│       │   ├── export.py
│       │   ├── frontmatter.py
│       │   ├── io.py
│       │   ├── logging.py
│       │   ├── media_tags.py
│       │   ├── message.py
│       │   ├── path.py
│       │   ├── proctitle.py
│       │   ├── pyinstaller.py
│       │   ├── rich/
│       │   │   ├── __init__.py
│       │   │   ├── columns.py
│       │   │   ├── markdown.py
│       │   │   ├── markdown_sample.md
│       │   │   ├── markdown_sample_short.md
│       │   │   └── syntax.py
│       │   ├── signals.py
│       │   ├── slashcmd.py
│       │   ├── string.py
│       │   ├── subprocess_env.py
│       │   ├── term.py
│       │   └── typing.py
│       ├── vis/
│       │   ├── __init__.py
│       │   ├── api/
│       │   │   ├── __init__.py
│       │   │   ├── sessions.py
│       │   │   ├── statistics.py
│       │   │   └── system.py
│       │   └── app.py
│       ├── web/
│       │   ├── __init__.py
│       │   ├── api/
│       │   │   ├── __init__.py
│       │   │   ├── config.py
│       │   │   ├── open_in.py
│       │   │   └── sessions.py
│       │   ├── app.py
│       │   ├── auth.py
│       │   ├── models.py
│       │   ├── runner/
│       │   │   ├── __init__.py
│       │   │   ├── messages.py
│       │   │   ├── process.py
│       │   │   └── worker.py
│       │   └── store/
│       │       ├── __init__.py
│       │       └── sessions.py
│       └── wire/
│           ├── __init__.py
│           ├── file.py
│           ├── jsonrpc.py
│           ├── protocol.py
│           ├── serde.py
│           ├── server.py
│           └── types.py
├── tests/
│   ├── __init__.py
│   ├── acp/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── test_protocol_v1.py
│   │   ├── test_session_notifications.py
│   │   └── test_version.py
│   ├── auth/
│   │   └── test_ascii_header.py
│   ├── background/
│   │   ├── test_manager.py
│   │   ├── test_store.py
│   │   └── test_worker.py
│   ├── conftest.py
│   ├── core/
│   │   ├── test_agent_flow.py
│   │   ├── test_agent_spec.py
│   │   ├── test_ask_user_plan_mode.py
│   │   ├── test_config.py
│   │   ├── test_context.py
│   │   ├── test_create_llm.py
│   │   ├── test_default_agent.py
│   │   ├── test_exceptions.py
│   │   ├── test_inspect_plan_edit_target.py
│   │   ├── test_kimisoul_ralph_loop.py
│   │   ├── test_kimisoul_retry_recovery.py
│   │   ├── test_kimisoul_slash_commands.py
│   │   ├── test_kimisoul_steer.py
│   │   ├── test_load_agent.py
│   │   ├── test_load_agents_md.py
│   │   ├── test_normalize_history.py
│   │   ├── test_notifications.py
│   │   ├── test_plan_mode.py
│   │   ├── test_plan_mode_injection_provider.py
│   │   ├── test_plan_mode_reminder.py
│   │   ├── test_plan_slash.py
│   │   ├── test_plugin.py
│   │   ├── test_plugin_manager.py
│   │   ├── test_plugin_tool.py
│   │   ├── test_session.py
│   │   ├── test_session_state.py
│   │   ├── test_shell_mcp_status.py
│   │   ├── test_simple_compaction.py
│   │   ├── test_skill.py
│   │   ├── test_soul_import_command.py
│   │   ├── test_soul_message.py
│   │   ├── test_startup_imports.py
│   │   ├── test_startup_progress.py
│   │   ├── test_status_formatting.py
│   │   ├── test_str_replace_file_plan_mode.py
│   │   ├── test_toolset.py
│   │   ├── test_wire_message.py
│   │   ├── test_wire_plan_mode.py
│   │   ├── test_wire_server_steer.py
│   │   └── test_write_file_plan_mode.py
│   ├── e2e/
│   │   ├── __init__.py
│   │   ├── shell_pty_helpers.py
│   │   ├── test_basic_e2e.py
│   │   ├── test_cli_error_output.py
│   │   ├── test_media_e2e.py
│   │   └── test_shell_pty_e2e.py
│   ├── notifications/
│   │   └── test_notification_manager.py
│   ├── test_additional_dirs_state.py
│   ├── test_attachment_cache.py
│   ├── test_clipboard.py
│   ├── tools/
│   │   ├── test_additional_dirs.py
│   │   ├── test_ask_user.py
│   │   ├── test_background_tools.py
│   │   ├── test_create_subagent.py
│   │   ├── test_extract_key_argument.py
│   │   ├── test_fetch_url.py
│   │   ├── test_glob.py
│   │   ├── test_grep.py
│   │   ├── test_read_file.py
│   │   ├── test_read_media_file.py
│   │   ├── test_read_media_file_desc.py
│   │   ├── test_shell_bash.py
│   │   ├── test_shell_powershell.py
│   │   ├── test_str_replace_file.py
│   │   ├── test_tool_descriptions.py
│   │   ├── test_tool_schemas.py
│   │   └── test_write_file.py
│   ├── ui_and_conv/
│   │   ├── test_acp_convert.py
│   │   ├── test_acp_server_auth.py
│   │   ├── test_export_import.py
│   │   ├── test_file_completer.py
│   │   ├── test_live_view_notifications.py
│   │   ├── test_print_final_only.py
│   │   ├── test_print_notifications.py
│   │   ├── test_prompt_clipboard.py
│   │   ├── test_prompt_external_editor.py
│   │   ├── test_prompt_history.py
│   │   ├── test_prompt_placeholders.py
│   │   ├── test_prompt_tips.py
│   │   ├── test_question_panel.py
│   │   ├── test_replay.py
│   │   ├── test_sanitize_surrogates.py
│   │   ├── test_shell_editor_slash.py
│   │   ├── test_shell_export_import_commands.py
│   │   ├── test_shell_prompt_echo.py
│   │   ├── test_shell_prompt_router.py
│   │   ├── test_shell_run_placeholders.py
│   │   ├── test_shell_slash_commands.py
│   │   ├── test_shell_task_slash.py
│   │   ├── test_slash_completer.py
│   │   ├── test_status_block.py
│   │   ├── test_task_browser.py
│   │   ├── test_tool_call_block.py
│   │   └── test_visualize_running_prompt.py
│   ├── utils/
│   │   ├── test_atomic_json_write.py
│   │   ├── test_broadcast_queue.py
│   │   ├── test_changelog.py
│   │   ├── test_diff_utils.py
│   │   ├── test_editor.py
│   │   ├── test_file_utils.py
│   │   ├── test_frontmatter.py
│   │   ├── test_is_within_workspace.py
│   │   ├── test_list_directory.py
│   │   ├── test_message_utils.py
│   │   ├── test_pyinstaller_utils.py
│   │   ├── test_result_builder.py
│   │   ├── test_rich_markdown.py
│   │   ├── test_slash_command.py
│   │   ├── test_typing_utils.py
│   │   ├── test_utils_environment.py
│   │   └── test_utils_path.py
│   ├── vis/
│   │   └── test_app.py
│   └── web/
│       └── test_open_in.py
├── tests_ai/
│   ├── scripts/
│   │   ├── main.yaml
│   │   ├── run.py
│   │   └── worker.yaml
│   ├── test_cli_loading_time.md
│   ├── test_encoding_error_handling.md
│   └── test_utf8_encoding.md
├── tests_e2e/
│   ├── AGENTS.md
│   ├── __init__.py
│   ├── test_mcp_cli.py
│   ├── test_wire_approvals_tools.py
│   ├── test_wire_config.py
│   ├── test_wire_errors.py
│   ├── test_wire_prompt.py
│   ├── test_wire_protocol.py
│   ├── test_wire_question.py
│   ├── test_wire_real_llm.py
│   ├── test_wire_sessions.py
│   ├── test_wire_skills_mcp.py
│   ├── test_wire_steer.py
│   └── wire_helpers.py
├── vis/
│   ├── components.json
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── markdown.tsx
│   │   │   └── ui/
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── select.tsx
│   │   │       └── tooltip.tsx
│   │   ├── features/
│   │   │   ├── context-viewer/
│   │   │   │   ├── assistant-message.tsx
│   │   │   │   ├── context-space-map.tsx
│   │   │   │   ├── context-viewer.tsx
│   │   │   │   ├── tool-call-block.tsx
│   │   │   │   └── user-message.tsx
│   │   │   ├── dual-view/
│   │   │   │   └── dual-view.tsx
│   │   │   ├── session-picker/
│   │   │   │   └── session-picker.tsx
│   │   │   ├── sessions-explorer/
│   │   │   │   ├── explorer-toolbar.tsx
│   │   │   │   ├── project-group.tsx
│   │   │   │   ├── session-card.tsx
│   │   │   │   └── sessions-explorer.tsx
│   │   │   ├── state-viewer/
│   │   │   │   └── state-viewer.tsx
│   │   │   ├── statistics/
│   │   │   │   └── statistics-view.tsx
│   │   │   └── wire-viewer/
│   │   │       ├── decision-path.tsx
│   │   │       ├── integrity-check.tsx
│   │   │       ├── timeline-view.tsx
│   │   │       ├── tool-call-detail.tsx
│   │   │       ├── tool-stats-dashboard.tsx
│   │   │       ├── turn-efficiency.tsx
│   │   │       ├── turn-tree.tsx
│   │   │       ├── usage-chart.tsx
│   │   │       ├── wire-event-card.tsx
│   │   │       ├── wire-filters.tsx
│   │   │       └── wire-viewer.tsx
│   │   ├── hooks/
│   │   │   └── use-theme.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   ├── api.ts
│   │   │   ├── cache.ts
│   │   │   └── utils.ts
│   │   └── main.tsx
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   └── vite.config.ts
└── web/
    ├── .gitignore
    ├── biome.jsonc
    ├── components.json
    ├── index.html
    ├── openapi.json
    ├── openapitools.json
    ├── package.json
    ├── scripts/
    │   └── generate-api.sh
    ├── src/
    │   ├── App.tsx
    │   ├── bootstrap.tsx
    │   ├── components/
    │   │   ├── ai-elements/
    │   │   │   ├── chain-of-thought.tsx
    │   │   │   ├── code-block.tsx
    │   │   │   ├── confirmation.tsx
    │   │   │   ├── context.tsx
    │   │   │   ├── conversation.tsx
    │   │   │   ├── index.ts
    │   │   │   ├── loader.tsx
    │   │   │   ├── message.tsx
    │   │   │   ├── model-selector.tsx
    │   │   │   ├── prompt-input.tsx
    │   │   │   ├── reasoning.tsx
    │   │   │   ├── shimmer.tsx
    │   │   │   ├── streamdown.tsx
    │   │   │   ├── subagent-steps.tsx
    │   │   │   └── tool.tsx
    │   │   ├── error-boundary.tsx
    │   │   ├── kimi-cli-brand.tsx
    │   │   └── ui/
    │   │       ├── alert-dialog.tsx
    │   │       ├── alert.tsx
    │   │       ├── badge.tsx
    │   │       ├── button-group.tsx
    │   │       ├── button.tsx
    │   │       ├── card.tsx
    │   │       ├── carousel.tsx
    │   │       ├── checkbox.tsx
    │   │       ├── collapsible.tsx
    │   │       ├── command.tsx
    │   │       ├── context-menu.tsx
    │   │       ├── dialog.tsx
    │   │       ├── diff/
    │   │       │   ├── index.tsx
    │   │       │   ├── lazy.tsx
    │   │       │   ├── theme.css
    │   │       │   └── utils/
    │   │       │       ├── guess-lang.ts
    │   │       │       ├── index.ts
    │   │       │       └── parse.ts
    │   │       ├── dropdown-menu.tsx
    │   │       ├── hover-card.tsx
    │   │       ├── input-group.tsx
    │   │       ├── input.tsx
    │   │       ├── kbd.tsx
    │   │       ├── progress.tsx
    │   │       ├── resizable.tsx
    │   │       ├── scroll-area.tsx
    │   │       ├── select.tsx
    │   │       ├── separator.tsx
    │   │       ├── sonner.tsx
    │   │       ├── switch.tsx
    │   │       ├── textarea.tsx
    │   │       ├── theme-toggle.tsx
    │   │       ├── toggle-group.tsx
    │   │       ├── toggle.tsx
    │   │       └── tooltip.tsx
    │   ├── config/
    │   │   └── media.ts
    │   ├── features/
    │   │   ├── chat/
    │   │   │   ├── chat-workspace-container.tsx
    │   │   │   ├── chat.tsx
    │   │   │   ├── components/
    │   │   │   │   ├── activity-status-indicator.tsx
    │   │   │   │   ├── approval-dialog.tsx
    │   │   │   │   ├── assistant-message.tsx
    │   │   │   │   ├── attachment-button.tsx
    │   │   │   │   ├── chat-conversation.tsx
    │   │   │   │   ├── chat-prompt-composer.tsx
    │   │   │   │   ├── chat-workspace-header.tsx
    │   │   │   │   ├── open-in-menu.tsx
    │   │   │   │   ├── prompt-toolbar/
    │   │   │   │   │   ├── index.tsx
    │   │   │   │   │   ├── open-in-button.tsx
    │   │   │   │   │   ├── toolbar-changes.tsx
    │   │   │   │   │   ├── toolbar-context.tsx
    │   │   │   │   │   ├── toolbar-queue.tsx
    │   │   │   │   │   └── toolbar-todo.tsx
    │   │   │   │   ├── question-dialog.tsx
    │   │   │   │   ├── session-info-popover.tsx
    │   │   │   │   └── virtualized-message-list.tsx
    │   │   │   ├── file-mention-menu.tsx
    │   │   │   ├── global-config-controls.tsx
    │   │   │   ├── message-search-dialog.tsx
    │   │   │   ├── message-search-utils.ts
    │   │   │   ├── queue-store.ts
    │   │   │   ├── slash-command-menu.tsx
    │   │   │   ├── useFileMentions.ts
    │   │   │   └── useSlashCommands.ts
    │   │   ├── sessions/
    │   │   │   ├── create-session-dialog.tsx
    │   │   │   └── sessions.tsx
    │   │   └── tool/
    │   │       ├── components/
    │   │       │   └── display-content.tsx
    │   │       └── store.ts
    │   ├── hooks/
    │   │   ├── types.ts
    │   │   ├── use-theme.ts
    │   │   ├── useGitDiffStats.ts
    │   │   ├── useGlobalConfig.ts
    │   │   ├── useSessionStream.ts
    │   │   ├── useSessions.ts
    │   │   ├── useVideoThumbnail.ts
    │   │   ├── utils.ts
    │   │   └── wireTypes.ts
    │   ├── index.css
    │   ├── lib/
    │   │   ├── api/
    │   │   │   ├── .openapi-generator/
    │   │   │   │   ├── FILES
    │   │   │   │   └── VERSION
    │   │   │   ├── .openapi-generator-ignore
    │   │   │   ├── apis/
    │   │   │   │   ├── ConfigApi.ts
    │   │   │   │   ├── DefaultApi.ts
    │   │   │   │   ├── OpenInApi.ts
    │   │   │   │   ├── SessionsApi.ts
    │   │   │   │   ├── WorkDirsApi.ts
    │   │   │   │   └── index.ts
    │   │   │   ├── docs/
    │   │   │   │   ├── ConfigApi.md
    │   │   │   │   ├── ConfigModel.md
    │   │   │   │   ├── ConfigToml.md
    │   │   │   │   ├── CreateSessionRequest.md
    │   │   │   │   ├── DefaultApi.md
    │   │   │   │   ├── GenerateTitleRequest.md
    │   │   │   │   ├── GenerateTitleResponse.md
    │   │   │   │   ├── GitDiffStats.md
    │   │   │   │   ├── GitFileDiff.md
    │   │   │   │   ├── GlobalConfig.md
    │   │   │   │   ├── HTTPValidationError.md
    │   │   │   │   ├── ModelCapability.md
    │   │   │   │   ├── OpenInApi.md
    │   │   │   │   ├── OpenInRequest.md
    │   │   │   │   ├── OpenInResponse.md
    │   │   │   │   ├── ProviderType.md
    │   │   │   │   ├── Session.md
    │   │   │   │   ├── SessionStatus.md
    │   │   │   │   ├── SessionsApi.md
    │   │   │   │   ├── UpdateConfigTomlRequest.md
    │   │   │   │   ├── UpdateConfigTomlResponse.md
    │   │   │   │   ├── UpdateGlobalConfigRequest.md
    │   │   │   │   ├── UpdateGlobalConfigResponse.md
    │   │   │   │   ├── UpdateSessionRequest.md
    │   │   │   │   ├── UploadSessionFileResponse.md
    │   │   │   │   ├── ValidationError.md
    │   │   │   │   ├── ValidationErrorLocInner.md
    │   │   │   │   └── WorkDirsApi.md
    │   │   │   ├── index.ts
    │   │   │   ├── models/
    │   │   │   │   ├── ConfigModel.ts
    │   │   │   │   ├── ConfigToml.ts
    │   │   │   │   ├── CreateSessionRequest.ts
    │   │   │   │   ├── GenerateTitleRequest.ts
    │   │   │   │   ├── GenerateTitleResponse.ts
    │   │   │   │   ├── GitDiffStats.ts
    │   │   │   │   ├── GitFileDiff.ts
    │   │   │   │   ├── GlobalConfig.ts
    │   │   │   │   ├── HTTPValidationError.ts
    │   │   │   │   ├── ModelCapability.ts
    │   │   │   │   ├── OpenInRequest.ts
    │   │   │   │   ├── OpenInResponse.ts
    │   │   │   │   ├── ProviderType.ts
    │   │   │   │   ├── Session.ts
    │   │   │   │   ├── SessionStatus.ts
    │   │   │   │   ├── UpdateConfigTomlRequest.ts
    │   │   │   │   ├── UpdateConfigTomlResponse.ts
    │   │   │   │   ├── UpdateGlobalConfigRequest.ts
    │   │   │   │   ├── UpdateGlobalConfigResponse.ts
    │   │   │   │   ├── UpdateSessionRequest.ts
    │   │   │   │   ├── UploadSessionFileResponse.ts
    │   │   │   │   ├── ValidationError.ts
    │   │   │   │   ├── ValidationErrorLocInner.ts
    │   │   │   │   └── index.ts
    │   │   │   └── runtime.ts
    │   │   ├── apiClient.ts
    │   │   ├── auth.ts
    │   │   ├── utils.ts
    │   │   └── version.ts
    │   ├── main.tsx
    │   └── react-scan.d.ts
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    └── vite.config.ts

================================================
FILE CONTENTS
================================================

================================================
FILE: .agents/skills/codex-worker/SKILL.md
================================================
---
name: codex-worker
description: Spawn and manage multiple Codex CLI agents via tmux to work on tasks in parallel. Use whenever a task can be decomposed into independent subtasks (e.g. batch triage, parallel fixes, multi-file refactors). When codex and tmux are available, prefer this over the built-in Task tool for parallelism.
---

# codex-worker

Orchestrate multiple Codex CLI (`codex`) agents running in parallel, each in its
own git worktree and tmux session.

**When to use:** Whenever you identify that a task can be split into independent
subtasks — don't wait for the user to ask for parallelism. Examples:
- User says "triage all open issues updated in the last 7 days" → fetch the
  issue list, then spawn one codex worker per issue.
- User says "refactor these 5 modules" → one worker per module.
- User says "fix lint errors across packages" → one worker per package.

**Replaces Task tool:** When `codex` and `tmux` are available in the
environment, use codex workers instead of the built-in Task (subagent) tool.
Codex workers are full-featured agents with their own file system access, shell,
and isolated worktree — far more capable than subagents.

## Preflight check

```bash
command -v codex && codex login status && command -v tmux
```

All three must succeed. The project must be a git repository.

## Naming convention

Git branch and worktree directory share a **task name**:

```
<type>-<issue number (optional)>-<short description>
```

The tmux session adds a `codex-worker-` prefix so workers are easy to filter:

| | Format | Example |
|---|---|---|
| Task name | `<type>-<number>-<desc>` | `issue-836-prompt-dollar-sign` |
| Git branch | same as task name | `issue-836-prompt-dollar-sign` |
| Worktree dir | `<project>.worktrees/<task>` | `kimi-cli.worktrees/issue-836-prompt-dollar-sign` |
| tmux session | `codex-worker-<task>` | `codex-worker-issue-836-prompt-dollar-sign` |

More examples:
- `issue-518-mcp-config-isolation`
- `fix-share-dir-skills-path`
- `feat-ask-user-tool`
- `refactor-jinja-templates`

List only codex workers: `tmux ls | grep ^codex-worker-`

## Usage

Prefer tmux + interactive codex for all tasks. It supports multi-turn dialogue,
the user can `tmux attach` to inspect or intervene, and you can send follow-up
prompts from outside.

### Spawn a worker

```bash
NAME="issue-836-prompt-dollar-sign"        # task name
SESSION="codex-worker-$NAME"               # tmux session name
PROJECT_DIR="$(pwd)"
WORKTREE_DIR="$PROJECT_DIR.worktrees"

# 1. Create worktree (skip if exists)
git worktree add "$WORKTREE_DIR/$NAME" -b "$NAME" main 2>/dev/null

# 2. Launch interactive codex inside tmux
tmux new-session -d -s "$SESSION" -x 200 -y 50 \
  "cd $WORKTREE_DIR/$NAME && codex --dangerously-bypass-approvals-and-sandbox"
```

### Send a prompt

The Codex TUI needs time to initialize before it accepts input.
After launching a session, **wait at least 5 seconds** before sending
a prompt. Then send the text followed by `Enter`. If the prompt stays
in the input field without being submitted, send an additional `Enter`.

```bash
sleep 5  # wait for Codex TUI to initialize
tmux send-keys -t "$SESSION" "Your prompt here" Enter
# If it doesn't submit, send another Enter:
# tmux send-keys -t "$SESSION" Enter
```

### Peek at output

```bash
tmux capture-pane -t "$SESSION" -p | tail -30
```

### Attach for hands-on interaction

```bash
tmux attach -t "$SESSION"
```

### Parallel fan-out

```bash
TASKS=(
  "issue-518-mcp-config-isolation|Triage #518: MCP config 被子 agent 继承的隔离问题。分析根因,给出修复方案。"
  "issue-836-prompt-dollar-sign|Triage #836: prompt 包含 $ 时启动静默失败。分析根因,给出修复方案。"
)

PROJECT_DIR="$(pwd)"
WORKTREE_DIR="$PROJECT_DIR.worktrees"

for entry in "${TASKS[@]}"; do
  NAME="${entry%%|*}"
  PROMPT="${entry#*|}"
  SESSION="codex-worker-$NAME"
  git worktree add "$WORKTREE_DIR/$NAME" -b "$NAME" main 2>/dev/null
  tmux new-session -d -s "$SESSION" -x 200 -y 50 \
    "cd $WORKTREE_DIR/$NAME && codex --dangerously-bypass-approvals-and-sandbox"
  sleep 5  # wait for Codex TUI to fully initialize
  tmux send-keys -t "$SESSION" "$PROMPT" Enter
done
```

### Fallback: `codex exec`

Only use `codex exec` when you explicitly don't need follow-up (e.g. CI, pure
analysis with `-o` output). It does not support multi-turn dialogue.

```bash
codex exec --dangerously-bypass-approvals-and-sandbox \
  -o "/tmp/$NAME-result.md" \
  "Your prompt here"
```

## Lifecycle management

List active workers:

```bash
tmux ls | grep ^codex-worker-
```

Kill a finished worker:

```bash
tmux kill-session -t "codex-worker-$NAME"
```

Clean up worktree after merging:

```bash
tmux kill-session -t "codex-worker-$NAME" 2>/dev/null
git worktree remove "$WORKTREE_DIR/$NAME"
git branch -d "$NAME"
```

Batch cleanup of dead sessions:

```bash
tmux list-sessions -F '#{session_name}:#{pane_dead}' \
  | grep ':1$' \
  | cut -d: -f1 \
  | xargs -I{} tmux kill-session -t {}
```


================================================
FILE: .agents/skills/feature-smoke-test/SKILL.md
================================================
---
name: feature-smoke-test
description: 针对 Kimi Code CLI 的新增或变更功能,规划并执行可重复的端到端冒烟测试。从 git diff 推断功能边界,读取相关文档和代码,设计测试 prompt,以 --print 非交互模式运行本地 CLI,检查进程退出码和 session 产物,总结预期与实际行为之间的差异。发现问题时自动启动多路并行探查以定位根因。
---

冒烟测试是运行时验证,不是写完 prompt 或读完代码就结束。必须实际执行、实际检查产物。

## 确定测试范围

动手之前,先从代码变更推断功能边界:

```sh
git diff main --name-only
git diff main --stat
```

根据变更文件集合,明确写下:

- 被测的功能边界
- 用户可感知的行为变化
- 运行前的准备工作和运行后的清理工作
- 能够证明成功或失败的证据

如果功能涉及状态、异步、审批流或时序敏感逻辑,默认认为单条 prompt 不够。

## 先读事实来源

读取定义该功能真实行为的最小文件集合:

- 面向用户的文档、changelog 或设计笔记
- 暴露该功能的 agent prompt 或 tool prompt
- 实现层入口
- 已有测试——当测试比文档更准确地描述行为时优先看测试

不要信任过时的 prompt 示例。先从代码或当前文档重建真实的工具接口。

## 制定最小测试计划

默认覆盖三个场景:

1. 正常路径
2. 边界条件、非法输入或容量极限
3. 中断、重试、清理或恢复

每个场景记录:

- `目标`
- `前置准备`
- `prompt 策略`
- `成功信号`
- `失败信号`
- `需要检查的产物`

如果功能存在竞态条件,必须显式标注时序。用长时间运行的命令或刻意的等待来制造时序窗口,不要用"快速再跑一个"这种模糊说法。

## 优先使用多轮 prompt

默认采用多轮流程:

1. 探索轮:让 agent 在阅读事实来源后复述当前真实接口
2. 执行轮:只执行一个场景
3. 观察轮:只读取输出、工具状态和产物文件,不扩大范围
4. 清理轮:停止、回滚或关闭有状态资源

仅对无状态的简单功能使用单轮 prompt。

多轮测试时,每一轮单独调用 CLI,在两轮之间检查上一轮的输出和产物,再决定下一轮的 prompt。不要把多轮 prompt 一次性塞进 stdin——那是盲写,无法根据上一轮结果调整。

测试时,显式要求 agent 先列举当前可用的工具,不要臆造历史工具名。可复用的 prompt 模板见 `references/prompt-patterns.md`。

## 以非交互模式隔离运行 CLI

使用 `/tmp` 下的一次性目录作为 `--work-dir`,实现 session 隔离。CLI 的 session 路径由 `~/.kimi/sessions/<md5(work_dir)>/` 决定,不同的 work-dir 自动产生独立的 session 命名空间,不会污染正常项目的 session。认证状态保留在 `~/.kimi` 下,无需额外配置。

### 环境准备

```sh
SMOKE_DIR="$(mktemp -d /tmp/kimi-smoke-XXXXXX)"
```

如果功能需要仓库上下文(读取代码、git 信息等),把仓库文件复制或软链到 `SMOKE_DIR`。如果功能会编辑文件,绝不要用活跃仓库作为 work-dir。

### 默认执行方式

绝大多数场景使用这个模式:

```sh
uv run python -m kimi_cli.cli \
  --print \
  --prompt "你的测试 prompt" \
  --work-dir "$SMOKE_DIR"
echo "exit_code=$?"
```

`--print` 会自动启用 `--yolo`(自动批准所有操作),适合无人值守的冒烟测试。

执行后**必须先检查退出码**:非零表示 CLI 本身崩溃或超时,应优先排查进程级错误,再看 session 产物。

### 备选执行模式

当默认方式不满足需求时,按需选用:

- **长 prompt**:通过 stdin 传入——`cat <<'PROMPT' | uv run python -m kimi_cli.cli --print --input-format text --work-dir "$SMOKE_DIR"`
- **结构化输出**:加 `--output-format stream-json`,输出逐行 JSON,便于程序化解析
- **只看最终结果**:用 `--quiet`,等价于 `--print --output-format text --final-message-only`

### 注意事项

- 运行过程中记录这些路径:`SMOKE_DIR`、最终 session 目录(可通过 `inspect_session.py` 定位)、功能特定的输出文件。

## 刻意执行

执行过程中维护一份简短的运行日志:

- 使用的完整 prompt
- 关键的工具调用或命令
- 功能产生的 task id、输出路径或审批 id
- 时序敏感时记录时间戳

不要仅凭 assistant 的最终文本推断正确性。运行时文件和工具结果才是事实来源。

## 检查 session 产物

首先检查:

- `~/.kimi/sessions/.../context.jsonl`
- `~/.kimi/sessions/.../wire.jsonl`
- 功能创建的 session 级文件

使用 `scripts/inspect_session.py` 查找并汇总最新 session:

```sh
uv run python .agents/skills/feature-smoke-test/scripts/inspect_session.py --share-dir ~/.kimi
```

脚本退出码含义:0 = 正常汇总,1 = session 目录缺失或无法解析。

如果功能会创建后台任务、通知或附属文件,直接检查这些文件,不要只依赖模型摘要。

## 汇报结论

将结果分为三类:

- **已确认**的行为
- **与预期不符**的行为
- **仍有歧义**、需要更确定性复现的行为

对于每个 bug 或回归,记录:

- 触发它的 prompt
- 精确的 session 路径
- 证明它的产物路径
- 能推导出的最小复现步骤

## 问题探查

当发现与预期不符的行为时,不要停在报告层面。启动并行探查流程定位根因:

### 探查策略

针对每个发现的问题,**同时启动多个独立的探查方向**(使用 Agent 工具并行执行)。根据问题的具体表现自行判断最有价值的探查角度,常见方向包括但不限于:

- 从触发问题的入口沿调用链追踪实际执行路径
- 检查输入数据经过各处理阶段后的变化
- 检查持久化状态(session 文件、后台任务、通知记录等)是否一致
- 运行相关单元测试和集成测试,确认测试是否覆盖了出问题的路径

不必机械地覆盖所有方向。根据 session 产物中的具体异常信号,选择最可能命中根因的 2-3 个方向并行展开。

### 探查输出

每条探查方向独立汇报:

- 探查的具体方向和范围
- 发现的事实(附文件路径和行号)
- 该方向的结论:已定位根因 / 已排除 / 需要进一步调查

### 综合定位

汇总所有探查结果后,输出:

- **根因**:一句话总结问题的本质原因
- **证据链**:从触发 prompt → 代码路径 → 出错点 → 产物表现的完整链路
- **修复建议**:最小改动方案,附具体文件和行号
- **回归风险**:修复后需要额外验证的相关功能


================================================
FILE: .agents/skills/feature-smoke-test/references/prompt-patterns.md
================================================
# Prompt 模板

以下模板作为脚手架使用。运行前替换占位符。

## 单轮还是多轮

满足以下任一条件时使用多轮:

- 功能有状态
- 功能依赖时序或并发
- 功能需要审批、清理或恢复
- session 产物本身是证据的一部分
- 工具接口可能近期发生过变化

仅对无状态的窄范围检查使用单轮。

## 变量

起草 prompt 前填写以下字段:

- `<feature>` — 被测功能名称
- `<goal>` — 当前场景的目标
- `<source_paths>` — 需要阅读的源码路径
- `<constraints>` — 执行约束
- `<success_signals>` — 成功信号
- `<failure_signals>` — 失败信号
- `<artifact_paths>` — 需要检查的产物路径
- `<session_dir>` — session 目录路径

## 探索 prompt

```text
我要验证 <feature>。

先阅读这些文件并只总结当前真实对外接口,不要假设旧文档、旧 prompt 或旧 tool 名称仍然正确:
<source_paths>

然后给我一个最小 smoke test 计划,只包含:
1. happy path
2. 一个边界/异常场景
3. 一个清理、恢复或中断场景

每个场景都写清楚目标、预期信号和要检查的产物。
```

## 执行 prompt

```text
在当前 session 里只执行这个场景:<goal>

约束:
<constraints>

执行前先复述你将使用的工具或命令。执行时记录关键 task id、输出片段、文件路径和任何需要后续复盘的标识符。不要扩展到其他场景。
```

## 观察 prompt

```text
现在不要继续跑新的测试。

只读取并总结这次运行已经产生的状态和文件:
<artifact_paths>

请明确指出哪些证据支持了预期,哪些证据反驳了预期,哪些地方仍然不确定。
```

## 复盘 prompt

```text
请根据这个 session 目录复盘整个 smoke test:
<session_dir>

重点阅读 context.jsonl、wire.jsonl 和相关运行产物。输出:
1. 实际执行流程
2. 关键 tool 调用与结果
3. 与预期不一致的点
4. 最小复现步骤
```

## 兼容性校验 prompt

```text
在运行 smoke test 之前,先从提供的文档或代码中复述当前真实可用的工具及其准确名称。不要臆造旧版工具名。如果任务涉及状态或时序,将工作拆分为多轮而非一次性长回复。
```


================================================
FILE: .agents/skills/feature-smoke-test/scripts/inspect_session.py
================================================
#!/usr/bin/env python3
"""Locate and summarize a Kimi CLI session for smoke-test review."""

from __future__ import annotations

import argparse
import json
import sys
from collections import Counter
from pathlib import Path
from typing import Any


def parse_args() -> argparse.Namespace:
    parser = argparse.ArgumentParser(
        description="Locate and summarize a Kimi CLI session for smoke-test review."
    )
    parser.add_argument("--share-dir", type=Path, help="Share dir that contains sessions/")
    parser.add_argument("--session-dir", type=Path, help="Explicit session directory to inspect")
    parser.add_argument(
        "--tail-lines", type=int, default=12, help="How many recent records to show"
    )
    parser.add_argument(
        "--max-text",
        type=int,
        default=220,
        help="Maximum characters to show for any text preview",
    )
    return parser.parse_args()


def truncate(text: str, max_text: int) -> str:
    text = " ".join(text.split())
    if len(text) <= max_text:
        return text
    return text[: max_text - 3] + "..."


def extract_text(content: Any) -> str:
    if isinstance(content, str):
        return content
    if isinstance(content, list):
        parts: list[str] = []
        for item in content:
            if not isinstance(item, dict):
                parts.append(str(item))
                continue
            kind = item.get("type")
            if kind == "text" and isinstance(item.get("text"), str):
                parts.append(item["text"])
            elif kind == "think" and isinstance(item.get("think"), str):
                parts.append(item["think"])
            elif kind == "shell" and isinstance(item.get("command"), str):
                parts.append(item["command"])
            else:
                parts.append(json.dumps(item, ensure_ascii=False))
        return " ".join(parts)
    return json.dumps(content, ensure_ascii=False)


def load_json(path: Path) -> dict[str, Any] | None:
    if not path.exists():
        return None
    try:
        return json.loads(path.read_text())
    except Exception:
        return None


def iter_jsonl(path: Path) -> list[dict[str, Any]]:
    records: list[dict[str, Any]] = []
    if not path.exists():
        return records
    with path.open() as f:
        for line in f:
            line = line.strip()
            if not line:
                continue
            try:
                obj = json.loads(line)
            except json.JSONDecodeError:
                obj = {"_raw": line}
            records.append(obj)
    return records


def find_latest_session(share_dir: Path) -> Path:
    sessions_root = share_dir / "sessions"
    if not sessions_root.exists():
        raise FileNotFoundError(f"sessions directory not found: {sessions_root}")

    candidates: list[tuple[float, Path]] = []
    for path in sessions_root.glob("*/*"):
        if not path.is_dir():
            continue
        context_path = path / "context.jsonl"
        wire_path = path / "wire.jsonl"
        if context_path.exists():
            mtime = context_path.stat().st_mtime
        elif wire_path.exists():
            mtime = wire_path.stat().st_mtime
        else:
            mtime = path.stat().st_mtime
        candidates.append((mtime, path))

    if not candidates:
        raise FileNotFoundError(f"no session directories found under: {sessions_root}")

    candidates.sort(key=lambda item: item[0], reverse=True)
    return candidates[0][1]


def print_header(title: str) -> None:
    print()
    print(f"== {title} ==")


def summarize_context_record(record: dict[str, Any], max_text: int) -> str:
    if "_raw" in record:
        return truncate(record["_raw"], max_text)

    role = record.get("role", "<unknown>")
    if role == "_system_prompt":
        return "role=_system_prompt"
    if role == "_checkpoint":
        return f"role=_checkpoint id={record.get('id')}"
    if role == "_usage":
        return f"role=_usage token_count={record.get('token_count')}"

    text = truncate(extract_text(record.get("content")), max_text)

    if role == "assistant":
        tool_calls = record.get("tool_calls") or []
        tool_names = [
            call.get("function", {}).get("name")
            for call in tool_calls
            if isinstance(call, dict) and isinstance(call.get("function"), dict)
        ]
        parts: list[str] = [f"role={role}"]
        if tool_names:
            parts.append("tools=" + ",".join(name for name in tool_names if name))
        if text:
            parts.append(f"text={text}")
        return " | ".join(parts)

    if role == "tool":
        parts = [f"role={role}"]
        if record.get("tool_call_id"):
            parts.append(f"tool_call_id={record['tool_call_id']}")
        if text:
            parts.append(f"text={text}")
        return " | ".join(parts)

    if text:
        return f"role={role} | text={text}"
    return f"role={role}"


def summarize_wire_record(record: dict[str, Any], max_text: int) -> str:
    if "_raw" in record:
        return truncate(record["_raw"], max_text)

    message = record.get("message")
    if not isinstance(message, dict):
        return truncate(json.dumps(record, ensure_ascii=False), max_text)

    message_type = message.get("type", "<unknown>")
    payload = message.get("payload", {})
    parts = [f"type={message_type}"]

    if message_type == "StepBegin":
        parts.append(f"n={payload.get('n')}")
    elif message_type == "ContentPart":
        part_type = payload.get("type")
        parts.append(f"part={part_type}")
        if part_type in {"text", "think"}:
            raw = payload.get("text") or payload.get("think") or ""
            parts.append("text=" + truncate(raw, max_text))
    elif message_type == "ToolCall":
        function = payload.get("function", {})
        if isinstance(function, dict):
            parts.append(f"tool={function.get('name')}")
    elif message_type == "ApprovalRequest":
        parts.append(f"action={payload.get('action')}")
        if payload.get("description"):
            parts.append("desc=" + truncate(str(payload["description"]), max_text))
    elif message_type == "TurnBegin":
        user_input = payload.get("user_input") or []
        parts.append(f"user_parts={len(user_input)}")
    elif message_type == "StatusUpdate":
        parts.append(f"context_tokens={payload.get('context_tokens')}")

    return " | ".join(parts)


def print_jsonl_summary(title: str, path: Path, tail_lines: int, max_text: int) -> None:
    if not path.exists():
        print_header(title)
        print("missing")
        return

    records = iter_jsonl(path)
    print_header(title)
    print(path)
    print(f"records: {len(records)}")

    if path.name == "context.jsonl":
        counter = Counter(record.get("role", "<raw>") for record in records)
        print("roles:", ", ".join(f"{key}={value}" for key, value in sorted(counter.items())))
        tail = records[-tail_lines:]
        for idx, record in enumerate(tail, start=max(1, len(records) - len(tail) + 1)):
            print(f"[{idx}] {summarize_context_record(record, max_text)}")
    else:
        counter = Counter(
            record.get("message", {}).get("type", "<raw>")
            if isinstance(record.get("message"), dict)
            else "<raw>"
            for record in records
        )
        print("types:", ", ".join(f"{key}={value}" for key, value in sorted(counter.items())))
        tail = records[-tail_lines:]
        for idx, record in enumerate(tail, start=max(1, len(records) - len(tail) + 1)):
            print(f"[{idx}] {summarize_wire_record(record, max_text)}")


def print_file_inventory(session_dir: Path) -> None:
    print_header("Files")
    for path in sorted(session_dir.rglob("*")):
        if path.is_dir():
            continue
        relative = path.relative_to(session_dir)
        size = path.stat().st_size
        print(f"{relative} ({size} bytes)")


def tail_text_file(path: Path, tail_lines: int, max_text: int) -> list[str]:
    if not path.exists():
        return []
    lines = path.read_text(errors="replace").splitlines()
    return [truncate(line, max_text) for line in lines[-tail_lines:]]


def print_task_summary(session_dir: Path, tail_lines: int, max_text: int) -> None:
    tasks_dir = session_dir / "tasks"
    if not tasks_dir.exists():
        return

    task_dirs = sorted(path for path in tasks_dir.iterdir() if path.is_dir())
    if not task_dirs:
        return

    print_header("Background Tasks")
    for task_dir in task_dirs:
        spec = load_json(task_dir / "spec.json") or {}
        runtime = load_json(task_dir / "runtime.json") or {}
        control = load_json(task_dir / "control.json") or {}
        consumer = load_json(task_dir / "consumer.json") or {}
        print(f"task_id: {task_dir.name}")
        print(f"  description: {spec.get('description')}")
        print(f"  kind: {spec.get('kind')}")
        print(f"  status: {runtime.get('status')}")
        if runtime.get("exit_code") is not None:
            print(f"  exit_code: {runtime.get('exit_code')}")
        if spec.get("cwd"):
            print(f"  cwd: {spec.get('cwd')}")
        if spec.get("timeout_s") is not None:
            print(f"  timeout_s: {spec.get('timeout_s')}")
        for key in (
            "created_at",
            "started_at",
            "finished_at",
            "heartbeat_at",
            "failure_reason",
            "worker_pid",
            "child_pid",
        ):
            value = runtime.get(key)
            if value is not None:
                print(f"  {key}: {value}")
        for key in ("kill_requested_at", "kill_reason"):
            value = control.get(key)
            if value is not None:
                print(f"  {key}: {value}")
        for key in ("last_read_offset", "last_viewed_at"):
            value = consumer.get(key)
            if value is not None:
                print(f"  {key}: {value}")
        output_path = task_dir / "output.log"
        if output_path.exists():
            print(f"  output_log: {output_path}")
            for line in tail_text_file(output_path, tail_lines, max_text):
                print(f"    {line}")
        print()


def main() -> int:
    args = parse_args()

    try:
        if args.session_dir:
            session_dir = args.session_dir.expanduser().resolve()
        elif args.share_dir:
            session_dir = find_latest_session(args.share_dir.expanduser().resolve())
        else:
            print("error: pass --session-dir or --share-dir", file=sys.stderr)
            return 1
    except FileNotFoundError as exc:
        print(f"error: {exc}", file=sys.stderr)
        return 1

    if not session_dir.is_dir():
        print(f"error: session directory does not exist: {session_dir}", file=sys.stderr)
        return 1

    print(f"Session dir: {session_dir}")
    print_file_inventory(session_dir)
    print_jsonl_summary("Context", session_dir / "context.jsonl", args.tail_lines, args.max_text)
    print_jsonl_summary("Wire", session_dir / "wire.jsonl", args.tail_lines, args.max_text)
    print_task_summary(session_dir, args.tail_lines, args.max_text)
    return 0


if __name__ == "__main__":
    raise SystemExit(main())


================================================
FILE: .agents/skills/gen-changelog/SKILL.md
================================================
---
name: gen-changelog
description: Generate changelog entries for code changes.
---

根据当前分支相对于 main 分支的修改,生成更新日志条目并同步到文档站点。

## 步骤

1. **分析变更**:查看 `git log main..HEAD --oneline` 和 `git diff main..HEAD --stat`,理解所有变更。
2. **更新源 CHANGELOG**:在根目录 `CHANGELOG.md` 的 `## Unreleased` 下添加条目;如果变更属于 `packages/` 或 `sdks/` 下的子包,同时更新对应目录的 `CHANGELOG.md`。
3. **同步英文文档 changelog**:运行 `node docs/scripts/sync-changelog.mjs` 将根 `CHANGELOG.md` 同步到 `docs/en/release-notes/changelog.md`。
4. **更新中文文档 changelog**:在 `docs/zh/release-notes/changelog.md` 的 `## 未发布` 下添加对应的中文翻译条目,遵循现有格式和用词规范(参考 `docs/AGENTS.md` 中的术语表和排版规范)。
5. **Breaking changes**(如有):如果变更包含破坏性变更(如移除/重命名选项、更改默认行为、迁移配置格式等),还需在 `docs/en/release-notes/breaking-changes.md` 和 `docs/zh/release-notes/breaking-changes.md` 的 `## Unreleased` / `## 未发布` 下添加对应条目,遵循现有的格式(版本标题 + 小节 + 受影响/迁移说明)。

## 注意事项

- 条目风格遵循现有 CHANGELOG 的格式:`- 分类: 描述`(如 `- Core: ...`、`- Web: ...`)。
- 只写对用户有意义的变更,不写纯内部重构。
- 中文翻译应遵循 `docs/AGENTS.md` 中的术语映射和排版规范。


================================================
FILE: .agents/skills/gen-docs/SKILL.md
================================================
---
name: gen-docs
description: Update Kimi Code CLI user documentation.
---

现在我们正在为当前项目 Kimi Code CLI 编写和维护用户文档,文档内容在 docs 目录下,docs/AGENTS.md 中有对文档的说明。

我们现在对代码库有了一些修改,请你参考最近的 git commit、staged changes、changelog.md 等的内容,根据 AGENTS.md 中的信息,必要时找到实际的代码全文,确保理解了所有变更对产品用户体验的真实改变,然后逐页、逐段地检查和更新文档内容。

你应该首先确保英文 changelog 使用 `node docs/scripts/sync-changelog.mjs` 进行了同步,然后确保中文文档符合最新代码的行为,最后,使用 translate-docs skill 进行双语同步。


================================================
FILE: .agents/skills/gen-rust/SKILL.md
================================================
---
name: gen-rust
description: Sync Rust implementation with Python changes (exclude UI/login) by reviewing recent changes, mapping modules, porting logic, and updating tests.
---

# gen-rust

Use this skill when the user wants Rust (kagent/kosong/kaos) to stay logically identical to Python (kimi_cli/kosong/kaos), excluding UI and login/auth. This includes code and tests: Rust behavior and tests must be fully synchronized with Python changes.

Note: The Rust binary is named `kagent`. User-facing CLI/output text in Rust must use `kagent`
instead of `kimi` to match the Rust command name.

## Quick workflow

1) **Build a complete change inventory**

Review recent changes to understand what needs syncing:

```sh
# Check staged changes
git diff --cached --name-only
git diff --cached -- src packages

# Check recent commits
git log --oneline -20 -- src packages
git diff HEAD~20..HEAD -- src packages

# Review CHANGELOG.md for context
head -50 CHANGELOG.md
```

2) **Classify changes**

- Exclude UI and login/auth changes (Shell/Print/ACP UI, login/logout commands).
- Everything else must be mirrored in Rust.
- Keep a small checklist: file -> change summary -> Rust target -> status.

3) **Map Python -> Rust**

Common mappings:
- `src/kimi_cli/llm.py` -> `rust/kagent/src/llm.rs`
- `src/kimi_cli/soul/*` -> `rust/kagent/src/soul/*`
- `src/kimi_cli/tools/*` -> `rust/kagent/src/tools/*`
- `src/kimi_cli/utils/*` -> `rust/kagent/src/utils/*`
- `src/kimi_cli/wire/*` -> `rust/kagent/src/wire/*`
- `packages/kosong/*` -> `rust/kosong/*`
- `packages/kaos/*` -> `rust/kaos/*`

4) **Port logic carefully**

- Match error messages and tool output text exactly (tests often assert strings).
- Preserve output types (text vs parts) and ordering.
- For media/tool outputs, verify ContentPart wrapping and serialization.
- If Python adds new helper modules, mirror minimal Rust utilities.
- Use `rg` to find existing analogs and references.

5) **Update tests**

- Update Rust tests that assert content/strings/parts.
- Mirror Python unit and integration tests when they exist; add missing Rust tests so coverage matches intent.
- Ensure E2E parity: use the existing Python E2E suite against the Rust binary by setting
  `KIMI_E2E_WIRE_CMD` (do not rewrite E2E in Rust). All E2E cases must pass or the gap must be documented.
- Prefer targeted tests first (`cargo test -p kagent --test <name>`), then full suite if asked.

6) **Verification is mandatory**

- Run the full Rust test suite and ensure all Rust tests pass.
- Run E2E tests with the wire command swapped to Rust (set `KIMI_E2E_WIRE_CMD`), and ensure they pass.

7) **Final report**

- List synced files and logic.
- Call out intentionally skipped UI/login changes.
- List tests run and results (must include full Rust tests and Rust E2E with wire command override).

## Pitfalls to avoid

- Skipping `llm.py`: it often changes model capability logic.
- Using commit message filtering instead of full diff.
- Forgetting to update Rust tests when output text/parts change.
- Mixing UI/login changes into core sync.
- Leaving test parity ambiguous; always state unit/integration/E2E status.

## Minimal diff checklist (template)

- [ ] Recent changes reviewed (staged, commits, changelog)
- [ ] Python diffs inspected for core logic
- [ ] Rust mappings applied
- [ ] Tests updated
- [ ] Targeted tests run
- [ ] Full Rust test suite passed
- [ ] Rust E2E passed with `KIMI_E2E_WIRE_CMD`


================================================
FILE: .agents/skills/pull-request/SKILL.md
================================================
---
name: pull-request
description: Create and submit a GitHub Pull Request.
type: flow
---

```mermaid
flowchart TB
    A(["BEGIN"]) --> B["当前分支有没有 dirty change?"]
    B -- 有 --> D(["END"])
    B -- 没有 --> n1["确保当前分支是一个不同于 main 的独立分支"]
    n1 --> n2["根据当前分支相对于 main 分支的修改,push 并提交一个 PR(利用 gh 命令),用英文编写 PR 标题和 description,描述所做的更改。PR title 要符合先前的 commit message 规范(PR title 就是 squash merge 之后的 commit message)。"]
    n2 --> D
```


================================================
FILE: .agents/skills/release/SKILL.md
================================================
---
name: release
description: Execute the release workflow for Kimi Code CLI packages.
type: flow
---

```d2
understand: |md
  Understand the release automation by reading AGENTS.md and
  .github/workflows/release*.yml.
|
check_changes: |md
  Check each package under packages/, sdks/, and repo root for changes since the
  last release (by tag). Note packages/kimi-code is a thin wrapper and must stay
  version-synced with kimi-cli.
|
has_changes: "Any packages changed?"
confirm_versions: |md
  For each changed package, confirm the new version with the user. Follow the
  project versioning policy: patch is always 0, bump minor for any change,
  major only changes by explicit manual decision.
|
update_files: |md
  Update the relevant pyproject.toml (and rust/Cargo.toml if root version changes),
  CHANGELOG.md (keep the Unreleased header), and breaking-changes.md in both languages.
|
root_change: "Is the root package version changing?"
sync_kimi_code: |md
  Sync packages/kimi-code/pyproject.toml version and dependency
  `kimi-cli==<version>`.
|
sync_kagent: |md
  Sync rust/Cargo.toml workspace version to match the root package version.
|
uv_sync: "Run uv sync."
gen_docs: |md
  Follow the gen-docs skill instructions to ensure docs are up to date.
|

new_branch: |md
  Create a new branch `bump-<package>-<new-version>` (multiple packages can share
  one branch; name it appropriately).
|
open_pr: |md
  Commit all changes, push to remote, and open a PR with gh describing the
  updates.
|
monitor_pr: "Monitor the PR until it is merged."
post_merge: |md
  After merge, switch to main, pull latest changes, and tell the user the git
  tag command needed for the final release tag (they will tag + push tags). Note:
  a single numeric tag releases kimi-cli, kimi-code, and kagent together.
|

BEGIN -> understand -> check_changes -> has_changes
has_changes -> END: no
has_changes -> confirm_versions: yes
confirm_versions -> update_files -> root_change
root_change -> sync_kimi_code: yes
root_change -> uv_sync: no
sync_kimi_code -> sync_kagent
sync_kagent -> uv_sync
uv_sync -> gen_docs -> new_branch -> open_pr -> monitor_pr -> post_merge -> END
```


================================================
FILE: .agents/skills/translate-docs/SKILL.md
================================================
---
name: translate-docs
description: Translate and sync bilingual documentation.
---

现在我们正在为当前项目 Kimi Code CLI 编写和维护用户文档,文档内容在 docs 目录下,docs/AGENTS.md 中有对文档的说明。

中文文档和英文 changelog 已经确保是正确符合预期的,现在请你逐页、逐段地翻译文档内容,确保中英双语保持同步。

## 翻译方向

- **Changelog**: 以英文为准,翻译到中文
- **其他所有页面**: 以中文为准,翻译到英文

## 注意事项

- 必要时可以参考代码文件以确保翻译的准确性
- 要保证不同语言的表达风格、结构标记等保持一致
- 但需要遵守两者可能不同的用词和排版偏好(主要是 sentence case、翻译对照之类的)


================================================
FILE: .agents/skills/worktree-status/SKILL.md
================================================
---
name: worktree-status
description: Audit all git worktrees in the current project. Use when the user asks about worktree status, which branches are merged, which have uncommitted changes, or which worktrees can be safely cleaned up.
---

# worktree-status

Report the status of every git worktree for the current project, covering
dirty state and merge status.

## When to use

- User asks "which worktrees can I clean up?"
- User asks "what's the status of my worktrees / branches?"
- Before batch-cleaning worktrees, to avoid losing uncommitted work

## Procedure

### 1. Pull latest main (MANDATORY)

You MUST pull latest main before any status checks. Without this, merge
detection (both ancestry and content diff) will produce stale results and
you may mistakenly conclude a branch is not merged.

```bash
cd "$(git rev-parse --show-toplevel)" && git pull origin main
```

### 2. Collect worktree info

```bash
PROJECT_DIR="$(git rev-parse --show-toplevel)"

for wt in $(git worktree list --porcelain | grep "^worktree " | sed 's/^worktree //' | grep -v "$PROJECT_DIR$"); do
  branch=$(git -C "$wt" branch --show-current 2>/dev/null)
  [ -z "$branch" ] && branch="(detached)"
  name=$(basename "$wt")

  # dirty?
  if [ -z "$(git -C "$wt" status --short 2>/dev/null)" ]; then
    dirty="clean"
  else
    dirty="DIRTY"
  fi

  # merged into origin/main?
  # NOTE: This project uses squash merges exclusively. `git merge-base
  # --is-ancestor` does NOT detect squash-merged branches. Always follow
  # up with a content diff (step 3) for branches that appear "not merged".
  if [ "$branch" != "(detached)" ]; then
    if git merge-base --is-ancestor "$branch" origin/main 2>/dev/null; then
      merged="merged"
    else
      merged="not merged (verify with content diff)"
    fi
  else
    merged="n/a"
  fi

  echo ""
  echo "[$name]  branch=$branch  $dirty  $merged"
  if [ "$dirty" = "DIRTY" ]; then
    git -C "$wt" status --short 2>/dev/null | sed 's/^/  /'
  fi
done
```

### 3. Detect squash-merged branches (content diff)

For any branch that shows "not merged", check whether the branch's
changes are already in main. The correct method is:

1. Find the files the branch actually changed (relative to merge-base).
2. For each changed file, compare the branch version with main.
   If all files are identical, the branch was squash-merged.

**⚠️ Do NOT use `git diff origin/main <branch>`** — that compares the
two tips directly, so commits added to main *after* the branch diverged
will show up as false differences.

```bash
BRANCH="<branch>"
BASE=$(git merge-base origin/main "$BRANCH")

# List files the branch touched
FILES=$(git diff --name-only "$BASE" "$BRANCH")

# Compare each file between branch and current main
for f in $FILES; do
  d=$(git diff "$BRANCH" origin/main -- "$f" | wc -l)
  if [ "$d" != "0" ]; then
    echo "❌ $f — differs"
  else
    echo "✅ $f — identical in main"
  fi
done
# All ✅ = squash-merged
```

### 4. (Optional) Check for associated tmux sessions

Only run this if `tmux` is available and relevant (e.g. worktrees were
created by codex-worker or similar tooling). Skip if not applicable.

```bash
tmux ls 2>/dev/null | grep -E 'codex-worker|<other-pattern>' || true
```

### 5. Present results

**Always present results as a Markdown table.** Every worktree must appear
as a row. Never use abbreviated or prose-only summaries.

| Worktree | Branch | Dirty | Merged | Can clean? |
|---|---|---|---|---|
| `example-wt` | `feat-foo` | ✅ clean | ✅ squash-merged | ✅ |
| `another-wt` | `fix-bar` | ⚠️ 3 files | ❌ not merged | ❌ dirty + not merged |
| `detached-wt` | (detached) | ⚠️ 14 files | n/a | ❌ has uncommitted changes |

Column definitions:

- **Dirty**: `✅ clean` or `⚠️ N files`
- **Merged**: `✅ merged` / `✅ squash-merged` (confirmed via content diff) / `❌ not merged` / `n/a`
- **Can clean?**: `✅` only when merged (or squash-merged) AND clean

Add extra columns (e.g. tmux session, notes) only when relevant.

### 6. Cleanup (only when asked)

Only clean worktrees the user explicitly approves. For each:

```bash
NAME="<worktree-name>"
git worktree remove "/path/to/$NAME"
git branch -D "<branch>"  # only if the branch is no longer needed
```


================================================
FILE: .github/ISSUE_TEMPLATE/1-bug-report.yml
================================================
name: Bug Report
description: Report an issue that should be fixed
labels:
  - bug
  - needs triage
body:
  - type: markdown
    attributes:
      value: |
        Thank you for submitting a bug report! It helps make Kimi Code CLI better for everyone.

        If you need help or support using Kimi Code CLI, and are not reporting a bug, please post on [kimi-cli/discussions](https://github.com/MoonshotAI/kimi-cli/discussions), where you can ask questions or engage with others on ideas for how to improve Kimi Code CLI.

        Make sure you are running the latest version of Kimi Code CLI (`uv tool upgrade kimi-cli` to upgrade). The bug you are experiencing may already have been fixed.

        Please try to include as much information as possible.

  - type: input
    id: version
    attributes:
      label: What version of Kimi Code CLI is running?
      description: Copy the output of `kimi --version` or `/version`
    validations:
      required: true
  - type: input
    id: plan
    attributes:
      label: Which open platform/subscription were you using?
      description: The one you selected when running `/login` or `/setup`
    validations:
      required: true
  - type: input
    id: model
    attributes:
      label: Which model were you using?
      description: The one you can see on the bottom status line, like `kimi-k2-turbo-preview`, `kimi-for-coding`, etc.
  - type: input
    id: platform
    attributes:
      label: What platform is your computer?
      description: |
        For MacOS and Linux: copy the output of `uname -mprs`
        For Windows: copy the output of `"$([Environment]::OSVersion | ForEach-Object VersionString) $(if ([Environment]::Is64BitOperatingSystem) { "x64" } else { "x86" })"` in the PowerShell console
  - type: textarea
    id: actual
    attributes:
      label: What issue are you seeing?
      description: Please include the full error messages and prompts with any private information redacted. If possible, please provide text instead of a screenshot.
    validations:
      required: true
  - type: textarea
    id: steps
    attributes:
      label: What steps can reproduce the bug?
      description: Explain the bug and provide a code snippet that can reproduce it. Please include session id and context usage if applicable.
    validations:
      required: true
  - type: textarea
    id: expected
    attributes:
      label: What is the expected behavior?
      description: If possible, please provide text instead of a screenshot.
  - type: textarea
    id: notes
    attributes:
      label: Additional information
      description: Is there anything else you think we should know?


================================================
FILE: .github/ISSUE_TEMPLATE/2-feature-request.yml
================================================
name: Feature Request
description: Propose a new feature for Kimi Code CLI
labels:
  - enhancement
body:
  - type: markdown
    attributes:
      value: |
        Is Kimi Code CLI missing a feature that you'd like to see? Feel free to propose it here.

        Before you submit a feature:
        1. Search existing issues for similar features. If you find one, 👍 it rather than opening a new one.
        2. The Kimi Code CLI team will try to balance the varying needs of the community when prioritizing or rejecting new features. Please understand that not all features will be accepted.

  - type: textarea
    id: feature
    attributes:
      label: What feature would you like to see?
    validations:
      required: true
  - type: textarea
    id: notes
    attributes:
      label: Additional information
      description: Is there anything else you think we should know?


================================================
FILE: .github/ISSUE_TEMPLATE/config.yml
================================================
blank_issues_enabled: true
contact_links:
  - name: Questions and General Discussion
    url: https://github.com/MoonshotAI/kimi-cli/discussions
    about: Have questions? Welcome to open a discussion!


================================================
FILE: .github/actions/macos-code-sign/action.yml
================================================
name: macos-code-sign
description: Sign and notarize macOS PyInstaller binaries

inputs:
  binary-path:
    description: Path to the binary to sign
    required: true
  apple-certificate-p12:
    description: Base64-encoded Apple signing certificate (P12)
    required: true
  apple-certificate-password:
    description: Password for the signing certificate
    required: true
  apple-notarization-key-p8:
    description: Base64-encoded Apple notarization key (P8)
    required: true
  apple-notarization-key-id:
    description: Apple notarization key ID
    required: true
  apple-notarization-issuer-id:
    description: Apple notarization issuer ID
    required: true

runs:
  using: composite
  steps:
    - name: Import signing certificate
      shell: bash
      env:
        APPLE_CERTIFICATE_P12: ${{ inputs.apple-certificate-p12 }}
        APPLE_CERTIFICATE_PASSWORD: ${{ inputs.apple-certificate-password }}
        KEYCHAIN_PASSWORD: actions
      run: |
        set -euo pipefail

        # Decode certificate
        cert_path="${RUNNER_TEMP}/certificate.p12"
        echo "$APPLE_CERTIFICATE_P12" | base64 -d > "$cert_path"

        # Create temporary keychain
        keychain_path="${RUNNER_TEMP}/signing.keychain-db"
        security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
        security set-keychain-settings -lut 21600 "$keychain_path"
        security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"

        # Add to keychain search list
        security list-keychains -d user -s "$keychain_path" $(security list-keychains -d user | tr -d '"')
        security default-keychain -s "$keychain_path"

        # Import certificate
        security import "$cert_path" -k "$keychain_path" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
        security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychain_path" > /dev/null

        # Find signing identity
        IDENTITY=$(security find-identity -v -p codesigning "$keychain_path" | grep "Developer ID Application" | head -1 | sed -n 's/.*"\(Developer ID Application[^"]*\)".*/\1/p')
        
        if [[ -z "$IDENTITY" ]]; then
          echo "❌ No Developer ID Application identity found"
          security find-identity -v -p codesigning "$keychain_path"
          exit 1
        fi

        echo "✅ Found signing identity: $IDENTITY"
        echo "APPLE_SIGNING_IDENTITY=$IDENTITY" >> "$GITHUB_ENV"
        echo "APPLE_KEYCHAIN_PATH=$keychain_path" >> "$GITHUB_ENV"

        rm -f "$cert_path"

    - name: Sign PyInstaller binary and embedded libraries
      shell: bash
      env:
        BINARY_PATH: ${{ inputs.binary-path }}
      run: |
        set -euo pipefail

        echo "Signing PyInstaller binary: $BINARY_PATH"

        # PyInstaller onefile binaries embed libraries that get extracted at runtime.
        # We need to unpack, sign everything, and repack.
        
        # First, try signing the binary directly with --deep
        # For single-file PyInstaller executables, this should work
        codesign --deep --force --options runtime --timestamp \
          --sign "$APPLE_SIGNING_IDENTITY" \
          --keychain "$APPLE_KEYCHAIN_PATH" \
          "$BINARY_PATH"

        echo "✅ Binary signed"
        codesign -dv --verbose=2 "$BINARY_PATH"

    - name: Notarize binary
      shell: bash
      env:
        BINARY_PATH: ${{ inputs.binary-path }}
        APPLE_NOTARIZATION_KEY_P8: ${{ inputs.apple-notarization-key-p8 }}
        APPLE_NOTARIZATION_KEY_ID: ${{ inputs.apple-notarization-key-id }}
        APPLE_NOTARIZATION_ISSUER_ID: ${{ inputs.apple-notarization-issuer-id }}
      run: |
        set -euo pipefail

        # Save API key
        key_path="${RUNNER_TEMP}/AuthKey.p8"
        echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$key_path"

        # Create zip for notarization
        binary_name=$(basename "$BINARY_PATH")
        zip_path="${RUNNER_TEMP}/${binary_name}.zip"
        ditto -c -k --keepParent "$BINARY_PATH" "$zip_path"

        echo "Submitting for notarization..."
        
        # Submit and wait
        result=$(xcrun notarytool submit "$zip_path" \
          --key "$key_path" \
          --key-id "$APPLE_NOTARIZATION_KEY_ID" \
          --issuer "$APPLE_NOTARIZATION_ISSUER_ID" \
          --wait \
          --timeout 10m \
          --output-format json 2>&1) || true

        echo "$result"
        
        status=$(echo "$result" | grep -o '"status":"[^"]*"' | cut -d'"' -f4 || echo "unknown")
        
        if [[ "$status" == "Accepted" ]]; then
          echo "✅ Notarization successful"
        else
          echo "⚠️ Notarization status: $status"
          # Get detailed log
          submission_id=$(echo "$result" | grep -o '"id":"[^"]*"' | cut -d'"' -f4 || echo "")
          if [[ -n "$submission_id" ]]; then
            echo "Fetching notarization log..."
            xcrun notarytool log "$submission_id" \
              --key "$key_path" \
              --key-id "$APPLE_NOTARIZATION_KEY_ID" \
              --issuer "$APPLE_NOTARIZATION_ISSUER_ID" || true
          fi
          exit 1
        fi

        # Cleanup
        rm -f "$key_path" "$zip_path"

    - name: Verify signature
      shell: bash
      env:
        BINARY_PATH: ${{ inputs.binary-path }}
      run: |
        set -euo pipefail
        
        echo "Verifying signature and notarization..."
        codesign -dv --verbose=2 "$BINARY_PATH"
        echo ""
        echo "Gatekeeper check:"
        spctl -a -vv "$BINARY_PATH" 2>&1 || true

    - name: Cleanup keychain
      if: always()
      shell: bash
      run: |
        if [[ -n "${APPLE_KEYCHAIN_PATH:-}" && -f "${APPLE_KEYCHAIN_PATH}" ]]; then
          security delete-keychain "$APPLE_KEYCHAIN_PATH" || true
        fi


================================================
FILE: .github/dependabot.yml
================================================
# To get started with Dependabot version updates, you'll need to specify which
# package ecosystems to update and where the package manifests are located.
# Please see the documentation for all configuration options:
# https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file

version: 2
updates:
  - package-ecosystem: "uv"
    directory: "/"
    schedule:
      interval: "daily"


================================================
FILE: .github/pr-title-checker-config.json
================================================
{
    "LABEL": {
        "name": "Invalid PR Title",
        "color": "B60205"
    },
    "CHECKS": {
        "regexp": "^(feat|fix|test|refactor|chore|style|docs|perf|build|ci|revert)(\\(.*\\))?:.*"
    },
    "MESSAGES": {
        "failure": "The PR title is invalid. Please refer to https://www.conventionalcommits.org/en/v1.0.0/ for the convention."
    }
}


================================================
FILE: .github/pull_request_template.md
================================================
<!--
Thank you for your contribution to Kimi Code CLI!
Please make sure you already discussed the feature or bugfix you are proposing in an issue with the maintainers.
Please understand that if you have not gotten confirmation from the maintainers, your pull request may be closed or ignored without further review due to limited bandwidth.

See https://github.com/MoonshotAI/kimi-cli/blob/main/CONTRIBUTING.md for more.
-->

## Related Issue

<!-- Please link to the issue here. -->

Resolve #(issue_number)

## Description

<!-- Please describe your changes in detail. -->

## Checklist

- [ ] I have read the [CONTRIBUTING](https://github.com/MoonshotAI/kimi-cli/blob/main/CONTRIBUTING.md) document.
- [ ] I have linked the related issue, if any.
- [ ] I have added tests that prove my fix is effective or that my feature works.
- [ ] I have run `make gen-changelog` to update the changelog.
- [ ] I have run `make gen-docs` to update the user documentation.


================================================
FILE: .github/workflows/ci-docs.yml
================================================
name: CI (docs)

on:
  pull_request:
    paths:
      - ".github/workflows/ci-docs.yml"
      - ".github/workflows/docs-pages.yml"
      - "docs/**"
      - "CHANGELOG.md"
  push:
    branches:
      - main
    paths:
      - ".github/workflows/ci-docs.yml"
      - ".github/workflows/docs-pages.yml"
      - "docs/**"
      - "CHANGELOG.md"

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Cache docs node_modules
        uses: actions/cache@v4
        with:
          path: docs/node_modules
          key: ${{ runner.os }}-docs-node-modules-${{ hashFiles('docs/package.json') }}
          restore-keys: |
            ${{ runner.os }}-docs-node-modules-

      - name: Install docs dependencies
        working-directory: docs
        run: npm install --no-package-lock

      - name: Build docs
        working-directory: docs
        run: npm run build


================================================
FILE: .github/workflows/ci-kimi-cli.yml
================================================
name: CI (kimi-cli)

on:
  pull_request:
    paths:
      - ".github/workflows/**"
      - "packages/**"
      - "src/**"
      - "tests/**"
      - "tests_e2e/**"
      - "tests_ai/**"
      - "web/**"
      - "pyproject.toml"
      - "uv.lock"
  push:
    branches:
      - main
    paths:
      - ".github/workflows/**"
      - "packages/**"
      - "src/**"
      - "tests/**"
      - "tests_e2e/**"
      - "tests_ai/**"
      - "web/**"
      - "pyproject.toml"
      - "uv.lock"

env:
  NO_COLOR: "1"
  TERM: dumb

jobs:
  check:
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Prepare building environment
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make prepare

      - name: Run checks
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make check-kimi-cli

  test:
    strategy:
      fail-fast: false
      matrix:
        python-version: ["3.12", "3.13", "3.14"]
    runs-on: ubuntu-22.04
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Prepare building environment
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make prepare

      - name: Run tests
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
          PYTHONUTF8: "1"
        run: make test-kimi-cli

  build:
    strategy:
      fail-fast: true
      matrix:
        include:
          - runner: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
            binary_path: dist/onefile/kimi
          - runner: ubuntu-22.04-arm
            target: aarch64-unknown-linux-gnu
            binary_path: dist/onefile/kimi
          - runner: macos-14
            target: aarch64-apple-darwin
            binary_path: dist/onefile/kimi
          - runner: windows-2022
            target: x86_64-pc-windows-msvc
            binary_path: dist/onefile/kimi.exe
    runs-on: ${{ matrix.runner }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install GNU Make (Windows)
        if: runner.os == 'Windows'
        run: choco install make -y

      - name: Set up Python 3.13
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: "3.13"

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Set up Node.js (web build)
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
          cache-dependency-path: web/package-lock.json

      - name: Prepare building environment
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make prepare

      - name: Build standalone binary
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make build-bin

      - name: Smoke test binary --help
        shell: python
        run: |
          import os
          import subprocess

          binary = os.path.abspath(os.environ["BINARY_PATH"])
          result = subprocess.run([binary, "--help"], capture_output=True, text=True, check=True)
          if "Kimi" not in result.stdout:
              raise SystemExit("'Kimi' not found in --help output")
        env:
          BINARY_PATH: ${{ matrix.binary_path }}

      - name: Upload binary artifact
        if: success()
        uses: actions/upload-artifact@v4
        with:
          name: kimi-${{ matrix.target }}
          path: ${{ matrix.binary_path }}
          if-no-files-found: error
          retention-days: 7

  release-validate:
    if: github.event_name == 'pull_request'
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Detect version bump
        id: version
        shell: python
        run: |
          import os
          import subprocess
          import tomllib

          output_path = os.environ["GITHUB_OUTPUT"]
          event_name = os.environ.get("GITHUB_EVENT_NAME", "")
          base_ref = os.environ.get("GITHUB_BASE_REF")
          if event_name != "pull_request" or not base_ref:
              with open(output_path, "a", encoding="utf-8") as output:
                  output.write("bump=false\n")
              raise SystemExit(0)

          subprocess.run(["git", "fetch", "origin", base_ref, "--depth=1"], check=True)
          base_blob = subprocess.check_output(
              ["git", "show", f"origin/{base_ref}:pyproject.toml"],
          )
          base_version = tomllib.loads(base_blob.decode())["project"]["version"]
          with open("pyproject.toml", "rb") as handle:
              head_version = tomllib.load(handle)["project"]["version"]
          bumped = base_version != head_version

          with open(output_path, "a", encoding="utf-8") as output:
              output.write(f"bump={'true' if bumped else 'false'}\n")
              output.write(f"base_version={base_version}\n")
              output.write(f"head_version={head_version}\n")

      - name: Show version bump info
        if: steps.version.outputs.bump == 'true'
        run: |
          echo "version bump: ${{ steps.version.outputs.base_version }} -> ${{ steps.version.outputs.head_version }}"

      - name: Check dependency versions
        if: steps.version.outputs.bump == 'true'
        run: |
          python scripts/check_kimi_dependency_versions.py \
            --root-pyproject pyproject.toml \
            --kosong-pyproject packages/kosong/pyproject.toml \
            --pykaos-pyproject packages/kaos/pyproject.toml

      - name: Check kimi-code version alignment
        if: steps.version.outputs.bump == 'true'
        run: |
          python scripts/check_version_tag.py \
            --pyproject packages/kimi-code/pyproject.toml \
            --expected-version "${{ steps.version.outputs.head_version }}"

      - name: Check kimi-code dependency pin
        if: steps.version.outputs.bump == 'true'
        shell: python
        run: |
          import tomllib
          from pathlib import Path

          expected_version = "${{ steps.version.outputs.head_version }}"
          data = tomllib.loads(Path("packages/kimi-code/pyproject.toml").read_text())
          deps = data["project"]["dependencies"]
          expected = f"kimi-cli=={expected_version}"
          if expected not in deps:
              raise SystemExit(
                  "kimi-code must depend on "
                  f"{expected}, got: {deps}"
              )

  nix-test:
    strategy:
      fail-fast: true
      matrix:
        include:
          - runner: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
            binary_path: dist/kimi
          - runner: ubuntu-22.04-arm
            target: aarch64-unknown-linux-gnu
            binary_path: dist/kimi
          - runner: macos-14
            target: aarch64-apple-darwin
            binary_path: dist/kimi
    runs-on: ${{ matrix.runner }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install Nix
        uses: DeterminateSystems/nix-installer-action@main

      - name: Run nix package
        run: nix run .#kimi-cli -- --version && nix run . -- --help


================================================
FILE: .github/workflows/ci-kimi-sdk.yml
================================================
name: CI (kimi-sdk)

on:
  pull_request:
    paths:
      - ".github/workflows/ci-kimi-sdk.yml"
      - "sdks/kimi-sdk/**"
      - "pyproject.toml"
      - "uv.lock"
  push:
    branches:
      - main
    paths:
      - ".github/workflows/ci-kimi-sdk.yml"
      - "sdks/kimi-sdk/**"
      - "pyproject.toml"
      - "uv.lock"

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.12", "3.13", "3.14"]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Install dependencies
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: uv sync --frozen --all-extras --project sdks/kimi-sdk

      - name: Run checks
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make check-kimi-sdk

      - name: Run tests
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make test-kimi-sdk

  docs:
    runs-on: ubuntu-latest
    env:
      FOOTER_VERSION: ${{ github.ref_name }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Install dependencies
        run: uv sync --frozen --all-extras --project sdks/kimi-sdk

      - name: Generate API documentation
        run: |
          uv run --project sdks/kimi-sdk pdoc kimi_sdk \
            --docformat google \
            --footer-text "kimi-sdk ${FOOTER_VERSION}" \
            -o sdks/kimi-sdk/docs

      - name: Upload docs preview
        uses: actions/upload-artifact@v4
        with:
          name: docs-preview
          path: sdks/kimi-sdk/docs


================================================
FILE: .github/workflows/ci-kosong.yml
================================================
name: CI (kosong)

on:
  pull_request:
    paths:
      - ".github/workflows/ci-kosong.yml"
      - "packages/kosong/**"
      - "pyproject.toml"
      - "uv.lock"
  push:
    branches:
      - main
    paths:
      - ".github/workflows/ci-kosong.yml"
      - "packages/kosong/**"
      - "pyproject.toml"
      - "uv.lock"

jobs:
  test:
    runs-on: ubuntu-latest
    strategy:
      matrix:
        python-version: ["3.12", "3.13", "3.14"]
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python ${{ matrix.python-version }}
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Install dependencies
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: uv sync --frozen --all-extras --project packages/kosong

      - name: Run checks
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make check-kosong

      - name: Run tests
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make test-kosong

  docs:
    runs-on: ubuntu-latest
    env:
      FOOTER_VERSION: ${{ github.ref_name }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Install dependencies
        run: uv sync --frozen --all-extras --project packages/kosong

      - name: Generate API documentation
        run: |
          uv run --project packages/kosong pdoc kosong \
            --docformat google \
            --footer-text "kosong ${FOOTER_VERSION}" \
            -o packages/kosong/docs

      - name: Upload docs preview
        uses: actions/upload-artifact@v4
        with:
          name: docs-preview
          path: packages/kosong/docs


================================================
FILE: .github/workflows/ci-pykaos.yml
================================================
name: CI (pykaos)

on:
  pull_request:
    paths:
      - ".github/workflows/ci-pykaos.yml"
      - "packages/kaos/**"
      - "pyproject.toml"
      - "uv.lock"
  push:
    branches:
      - main
    paths:
      - ".github/workflows/ci-pykaos.yml"
      - "packages/kaos/**"
      - "pyproject.toml"
      - "uv.lock"

jobs:
  test:
    name: ${{ matrix.target }} python-${{ matrix.python-version }}
    runs-on: ${{ matrix.runner }}
    strategy:
      fail-fast: false
      matrix:
        runner: [ubuntu-22.04, macos-15-intel, macos-14, windows-2022]
        python-version: ["3.12", "3.13", "3.14"]
        include:
          - runner: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
          - runner: macos-15-intel
            target: x86_64-apple-darwin
          - runner: macos-14
            target: aarch64-apple-darwin
          - runner: windows-2022
            target: x86_64-pc-windows-msvc
    defaults:
      run:
        shell: bash
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install GNU Make (Windows)
        if: runner.os == 'Windows'
        shell: powershell
        run: choco install make -y

      - name: Set up Python ${{ matrix.python-version }}
        id: setup-python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ matrix.python-version }}
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"
          enable-cache: true
          cache-dependency-glob: uv.lock

      - name: Install dependencies
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: uv sync --frozen --all-extras --project packages/kaos

      - name: Configure local SSH server
        if: matrix.runner == 'ubuntu-22.04'
        run: |
          set -euxo pipefail
          sudo apt-get update
          sudo apt-get install -y openssh-server
          sudo mkdir -p /run/sshd
          sudo sed -i 's/^#\?PasswordAuthentication .*/PasswordAuthentication no/' /etc/ssh/sshd_config
          sudo sed -i 's/^#\?PubkeyAuthentication .*/PubkeyAuthentication yes/' /etc/ssh/sshd_config
          sudo sed -i 's@^#\?AuthorizedKeysFile .*@AuthorizedKeysFile .ssh/authorized_keys@' /etc/ssh/sshd_config
          sudo service ssh restart || sudo service ssh start
          mkdir -p "$HOME/.ssh"
          chmod 700 "$HOME/.ssh"
          ssh-keygen -q -t ed25519 -f "$HOME/.ssh/id_ci" -N ""
          cat "$HOME/.ssh/id_ci.pub" >> "$HOME/.ssh/authorized_keys"
          chmod 600 "$HOME/.ssh/authorized_keys"
          ssh-keyscan -H 127.0.0.1 >> "$HOME/.ssh/known_hosts"
          ssh-keyscan -H localhost >> "$HOME/.ssh/known_hosts"
          echo "KAOS_SSH_HOST=127.0.0.1" >> "$GITHUB_ENV"
          echo "KAOS_SSH_PORT=22" >> "$GITHUB_ENV"
          echo "KAOS_SSH_USERNAME=$USER" >> "$GITHUB_ENV"
          echo "KAOS_SSH_KEY_PATHS=$HOME/.ssh/id_ci" >> "$GITHUB_ENV"

      - name: Verify SSH connectivity
        if: matrix.runner == 'ubuntu-22.04'
        run: ssh -i "$HOME/.ssh/id_ci" -o BatchMode=yes -o StrictHostKeyChecking=yes "$USER@127.0.0.1" true

      - name: Run checks
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make check-pykaos

      - name: Run tests
        env:
          UV_PYTHON: ${{ steps.setup-python.outputs.python-path }}
        run: make test-pykaos


================================================
FILE: .github/workflows/docs-pages.yml
================================================
name: Docs (GitHub Pages)

on:
  push:
    branches:
      - main

permissions:
  contents: read
  pages: write
  id-token: write

jobs:
  deploy:
    # Only run on the original repository, not on forks
    if: github.repository == 'MoonshotAI/kimi-cli'
    runs-on: ubuntu-latest
    environment:
      name: github-pages
      url: ${{ steps.deploy.outputs.page_url }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Node.js
        uses: actions/setup-node@v4
        with:
          node-version: "20"

      - name: Cache docs node_modules
        uses: actions/cache@v4
        with:
          path: docs/node_modules
          key: ${{ runner.os }}-docs-node-modules-${{ hashFiles('docs/package.json') }}
          restore-keys: |
            ${{ runner.os }}-docs-node-modules-

      - name: Configure GitHub Pages
        id: pages
        uses: actions/configure-pages@v5

      - name: Set VitePress base
        shell: bash
        env:
          BASE_PATH: ${{ steps.pages.outputs.base_path }}
        run: |
          set -euo pipefail
          if [[ -z "${BASE_PATH}" ]]; then
            base="/"
          else
            base="${BASE_PATH%/}/"
          fi
          echo "VITEPRESS_BASE=${base}" >> "$GITHUB_ENV"

      - name: Install docs dependencies
        working-directory: docs
        run: npm install --no-package-lock

      - name: Build docs
        working-directory: docs
        env:
          VITEPRESS_BASE: ${{ env.VITEPRESS_BASE }}
        run: npm run build

      - name: Add .nojekyll
        run: touch docs/.vitepress/dist/.nojekyll

      - name: Upload Pages artifact
        uses: actions/upload-pages-artifact@v3
        with:
          path: docs/.vitepress/dist

      - name: Deploy to GitHub Pages
        id: deploy
        uses: actions/deploy-pages@v4


================================================
FILE: .github/workflows/pr-title-checker.yml
================================================
name: PR Title Checker

on:
  pull_request:
    types: [opened, edited, labeled]

jobs:
  check:
    runs-on: ubuntu-latest
    name: pr-title-checker
    steps:
      - uses: thehanimo/pr-title-checker@v1.4.3
        with:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          configuration_path: ".github/pr-title-checker-config.json"


================================================
FILE: .github/workflows/release-kimi-cli.yml
================================================
name: Release (kimi-cli)

on:
  push:
    tags:
      - "[0-9]*"

permissions:
  contents: write

jobs:
  validate:
    name: Validate tag
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Check version tag
        run: |
          python scripts/check_version_tag.py \
            --pyproject pyproject.toml \
            --expected-version "${GITHUB_REF_NAME}"

      - name: Check kimi-code version tag
        run: |
          python scripts/check_version_tag.py \
            --pyproject packages/kimi-code/pyproject.toml \
            --expected-version "${GITHUB_REF_NAME}"

      - name: Check dependency versions
        run: |
          python scripts/check_kimi_dependency_versions.py \
            --root-pyproject pyproject.toml \
            --kosong-pyproject packages/kosong/pyproject.toml \
            --pykaos-pyproject packages/kaos/pyproject.toml

  build:
    name: Build binaries (${{ matrix.target }})
    needs: validate
    strategy:
      fail-fast: false
      matrix:
        include:
          - runner: ubuntu-22.04
            target: x86_64-unknown-linux-gnu
          - runner: ubuntu-22.04-arm
            target: aarch64-unknown-linux-gnu
          - runner: macos-14
            target: aarch64-apple-darwin
          - runner: windows-2022
            target: x86_64-pc-windows-msvc
          - runner: windows-11-arm
            target: aarch64-pc-windows-msvc
    runs-on: ${{ matrix.runner }}
    env:
      KIMI_WEB_STRICT_VERSION: "1"
      KIMI_WEB_EXPECT_VERSION: ${{ github.ref_name }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Install GNU Make (Windows)
        if: runner.os == 'Windows'
        run: choco install make -y

      - name: Set up Rust
        uses: dtolnay/rust-toolchain@stable

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"

      - name: Set up Node.js (web build)
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
          cache-dependency-path: web/package-lock.json

      - name: Prepare building environment
        run: make prepare-build

      # macOS: Setup signing certificate before build
      - name: Setup macOS signing certificate
        if: runner.os == 'macOS'
        env:
          APPLE_CERTIFICATE_P12: ${{ secrets.APPLE_CERTIFICATE_P12 }}
          APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
          KEYCHAIN_PASSWORD: actions
        run: |
          set -euo pipefail

          # Decode certificate
          cert_path="${RUNNER_TEMP}/certificate.p12"
          echo "$APPLE_CERTIFICATE_P12" | base64 -d > "$cert_path"

          # Create temporary keychain
          keychain_path="${RUNNER_TEMP}/signing.keychain-db"
          security create-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"
          security set-keychain-settings -lut 21600 "$keychain_path"
          security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$keychain_path"

          # Add to keychain search list
          security list-keychains -d user -s "$keychain_path" $(security list-keychains -d user | tr -d '"')
          security default-keychain -s "$keychain_path"

          # Import certificate
          security import "$cert_path" -k "$keychain_path" -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
          security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$keychain_path" > /dev/null

          # Find signing identity
          IDENTITY=$(security find-identity -v -p codesigning "$keychain_path" | grep "Developer ID Application" | head -1 | sed -n 's/.*"\(Developer ID Application[^"]*\)".*/\1/p')
          
          if [[ -z "$IDENTITY" ]]; then
            echo "❌ No Developer ID Application identity found"
            security find-identity -v -p codesigning "$keychain_path"
            exit 1
          fi

          echo "✅ Found signing identity: $IDENTITY"
          echo "APPLE_SIGNING_IDENTITY=$IDENTITY" >> "$GITHUB_ENV"
          echo "APPLE_KEYCHAIN_PATH=$keychain_path" >> "$GITHUB_ENV"

          rm -f "$cert_path"

      # Build onefile and onedir versions (all platforms)
      - name: Build standalone binary (onefile)
        run: make build-bin

      - name: Build standalone binary (onedir)
        env:
          PYINSTALLER_ONEDIR: "1"
        run: make build-bin-onedir

      # macOS: Sign onefile binary
      - name: Sign macOS onefile binary
        if: runner.os == 'macOS'
        run: |
          set -euo pipefail
          echo "Signing onefile binary..."
          codesign --deep --force --options runtime --timestamp \
            --sign "$APPLE_SIGNING_IDENTITY" \
            --keychain "$APPLE_KEYCHAIN_PATH" \
            dist/onefile/kimi
          echo "✅ Onefile binary signed"
          codesign -dv --verbose=2 dist/onefile/kimi

      # macOS: Sign onedir binaries (all dylibs and executables)
      - name: Sign macOS onedir binaries
        if: runner.os == 'macOS'
        run: |
          set -euo pipefail
          echo "Signing onedir binaries..."
          
          # 1. Sign all dylibs and so files first (excluding those inside frameworks)
          find dist/onedir/kimi -type f \( -name "*.dylib" -o -name "*.so" \) ! -path "*.framework/*" | while read -r lib; do
            echo "Signing: $lib"
            codesign --force --options runtime --timestamp \
              --sign "$APPLE_SIGNING_IDENTITY" \
              --keychain "$APPLE_KEYCHAIN_PATH" \
              "$lib"
          done
          
          # 2. Sign all frameworks with --deep (important for Python.framework)
          find dist/onedir/kimi -type d -name "*.framework" | while read -r framework; do
            echo "Signing framework: $framework"
            codesign --deep --force --options runtime --timestamp \
              --sign "$APPLE_SIGNING_IDENTITY" \
              --keychain "$APPLE_KEYCHAIN_PATH" \
              "$framework"
          done
          
          # 3. Sign the main executable last
          echo "Signing main executable: dist/onedir/kimi/kimi"
          codesign --force --options runtime --timestamp \
            --sign "$APPLE_SIGNING_IDENTITY" \
            --keychain "$APPLE_KEYCHAIN_PATH" \
            dist/onedir/kimi/kimi
          
          echo "✅ Onedir binaries signed"
          codesign -dv --verbose=2 dist/onedir/kimi/kimi
          codesign --verify --deep --strict dist/onedir/kimi/kimi && echo "✅ Deep verification passed"

      # macOS: Notarize onefile binary
      - name: Notarize macOS onefile binary
        if: runner.os == 'macOS'
        env:
          APPLE_NOTARIZATION_KEY_P8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }}
          APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
          APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
        run: |
          set -euo pipefail

          # Save API key
          key_path="${RUNNER_TEMP}/AuthKey.p8"
          echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$key_path"

          # Create zip for notarization (use --norsrc to avoid ._ AppleDouble files)
          zip_path="${RUNNER_TEMP}/kimi-onefile.zip"
          ditto -c -k --norsrc --keepParent dist/onefile/kimi "$zip_path"

          echo "Submitting onefile for notarization..."
          
          # Submit and capture output for status verification
          xcrun notarytool submit "$zip_path" \
            --key "$key_path" \
            --key-id "$APPLE_NOTARIZATION_KEY_ID" \
            --issuer "$APPLE_NOTARIZATION_ISSUER_ID" \
            --wait \
            --timeout 15m \
            2>&1 | tee /tmp/notarize-onefile.log

          # Verify notarization was accepted
          if ! grep -q "status: Accepted" /tmp/notarize-onefile.log; then
            echo "❌ Onefile notarization failed!"
            cat /tmp/notarize-onefile.log
            
            # Get detailed error log from Apple
            submission_id=$(grep "id:" /tmp/notarize-onefile.log | head -1 | awk '{print $2}')
            if [[ -n "$submission_id" ]]; then
              echo "Fetching notarization log for submission: $submission_id"
              xcrun notarytool log "$submission_id" \
                --key "$key_path" \
                --key-id "$APPLE_NOTARIZATION_KEY_ID" \
                --issuer "$APPLE_NOTARIZATION_ISSUER_ID" 2>&1 || true
            fi
            exit 1
          fi

          echo "✅ Onefile notarization completed and accepted"

          # Verify signature and notarization status
          echo "Verifying onefile signature..."
          codesign -dv --verbose=2 dist/onefile/kimi
          
          echo "Verifying onefile notarization (online check)..."
          spctl -a -vvv -t install dist/onefile/kimi

          # Cleanup
          rm -f "$zip_path" /tmp/notarize-onefile.log

      # macOS: Notarize onedir binaries
      - name: Notarize macOS onedir binaries
        if: runner.os == 'macOS'
        env:
          APPLE_NOTARIZATION_KEY_P8: ${{ secrets.APPLE_NOTARIZATION_KEY_P8 }}
          APPLE_NOTARIZATION_KEY_ID: ${{ secrets.APPLE_NOTARIZATION_KEY_ID }}
          APPLE_NOTARIZATION_ISSUER_ID: ${{ secrets.APPLE_NOTARIZATION_ISSUER_ID }}
        run: |
          set -euo pipefail

          # Save API key (might already exist from previous step)
          key_path="${RUNNER_TEMP}/AuthKey.p8"
          if [[ ! -f "$key_path" ]]; then
            echo "$APPLE_NOTARIZATION_KEY_P8" | base64 -d > "$key_path"
          fi

          # Create zip for notarization (use --norsrc to avoid ._ AppleDouble files)
          zip_path="${RUNNER_TEMP}/kimi-onedir.zip"
          ditto -c -k --norsrc --keepParent dist/onedir/kimi "$zip_path"

          echo "Submitting onedir for notarization..."
          
          # Submit and capture output for status verification
          xcrun notarytool submit "$zip_path" \
            --key "$key_path" \
            --key-id "$APPLE_NOTARIZATION_KEY_ID" \
            --issuer "$APPLE_NOTARIZATION_ISSUER_ID" \
            --wait \
            --timeout 15m \
            2>&1 | tee /tmp/notarize-onedir.log

          # Verify notarization was accepted
          if ! grep -q "status: Accepted" /tmp/notarize-onedir.log; then
            echo "❌ Onedir notarization failed!"
            cat /tmp/notarize-onedir.log
            
            # Get detailed error log from Apple
            submission_id=$(grep "id:" /tmp/notarize-onedir.log | head -1 | awk '{print $2}')
            if [[ -n "$submission_id" ]]; then
              echo "Fetching notarization log for submission: $submission_id"
              xcrun notarytool log "$submission_id" \
                --key "$key_path" \
                --key-id "$APPLE_NOTARIZATION_KEY_ID" \
                --issuer "$APPLE_NOTARIZATION_ISSUER_ID" 2>&1 || true
            fi
            exit 1
          fi

          echo "✅ Onedir notarization completed and accepted"

          # Verify signature and notarization status
          echo "Verifying onedir signature..."
          codesign -dv --verbose=2 dist/onedir/kimi/kimi
          
          echo "Verifying onedir notarization (online check)..."
          spctl -a -vvv -t install dist/onedir/kimi/kimi

          # Cleanup
          rm -f "$key_path" "$zip_path" /tmp/notarize-onedir.log

      # macOS: Cleanup keychain
      - name: Cleanup macOS keychain
        if: always() && runner.os == 'macOS'
        run: |
          if [[ -n "${APPLE_KEYCHAIN_PATH:-}" && -f "${APPLE_KEYCHAIN_PATH}" ]]; then
            security delete-keychain "$APPLE_KEYCHAIN_PATH" || true
          fi

      # Package onefile artifact (all platforms)
      - name: Package onefile artifact
        shell: python
        env:
          TAG: ${{ github.ref_name }}
          TARGET: ${{ matrix.target }}
        run: |
          import os
          import pathlib
          import tarfile
          import zipfile

          tag = os.environ["TAG"]
          target = os.environ["TARGET"]

          dist_dir = pathlib.Path("dist")
          artifacts_dir = pathlib.Path("artifacts")
          artifacts_dir.mkdir(parents=True, exist_ok=True)

          is_windows = "windows" in target
          is_macos = "apple-darwin" in target
          binary_name = "kimi.exe" if is_windows else "kimi"
          binary_path = dist_dir / "onefile" / binary_name
          if not binary_path.exists():
              raise SystemExit(f"Binary not found at {binary_path}")

          # Determine archive format and name
          # - Windows: .zip
          # - macOS: .tar.gz
          # - Linux: .tar.gz
          if is_windows:
              archive_name = f"kimi-{tag}-{target}.zip"
              archive_path = artifacts_dir / archive_name
              with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive_file:
                  archive_file.write(binary_path, arcname=binary_name)
          else:
              archive_name = f"kimi-{tag}-{target}.tar.gz"
              archive_path = artifacts_dir / archive_name
              with tarfile.open(archive_path, "w:gz") as archive_file:
                  archive_file.add(binary_path, arcname="kimi")

          print(f"Built onefile artifact: {archive_path}")

      # Package onedir artifact (all platforms)
      - name: Package onedir artifact
        shell: python
        env:
          TAG: ${{ github.ref_name }}
          TARGET: ${{ matrix.target }}
        run: |
          import os
          import pathlib
          import tarfile
          import zipfile

          tag = os.environ["TAG"]
          target = os.environ["TARGET"]

          dist_dir = pathlib.Path("dist")
          artifacts_dir = pathlib.Path("artifacts")
          artifacts_dir.mkdir(parents=True, exist_ok=True)

          is_windows = "windows" in target

          # Windows: onedir is dist/onedir/kimi with kimi.exe inside
          # Others: onedir is dist/onedir/kimi with kimi inside
          onedir_path = dist_dir / "onedir" / "kimi"
          if not onedir_path.exists() or not onedir_path.is_dir():
              raise SystemExit(f"Onedir directory not found at {onedir_path}")

          if is_windows:
              archive_name = f"kimi-{tag}-{target}-onedir.zip"
              archive_path = artifacts_dir / archive_name
              with zipfile.ZipFile(archive_path, "w", compression=zipfile.ZIP_DEFLATED) as archive_file:
                  # Add the directory contents with kimi/ as the root
                  for item in onedir_path.rglob("*"):
                      if item.is_file():
                          arcname = f"kimi/{item.relative_to(onedir_path)}"
                          archive_file.write(item, arcname=arcname)
          else:
              archive_name = f"kimi-{tag}-{target}-onedir.tar.gz"
              archive_path = artifacts_dir / archive_name
              with tarfile.open(archive_path, "w:gz") as archive_file:
                  # Add the directory contents with kimi/ as the root
                  for item in onedir_path.iterdir():
                      archive_file.add(item, arcname=f"kimi/{item.name}")

          print(f"Built onedir artifact: {archive_path}")

      - name: Set artifact name
        id: artifact
        shell: python
        run: |
          import os
          ref = os.environ["GITHUB_REF_NAME"].replace("/", "-")
          target = "${{ matrix.target }}"
          with open(os.environ["GITHUB_OUTPUT"], "a") as f:
              f.write(f"name=kimi-{ref}-{target}\n")

      - name: Upload artifact
        uses: actions/upload-artifact@v4
        with:
          name: ${{ steps.artifact.outputs.name }}
          path: artifacts/*
          if-no-files-found: error
          retention-days: 7

  release:
    name: Publish GitHub Release
    needs: build
    runs-on: ubuntu-latest
    steps:
      - name: Download all build artifacts
        uses: actions/download-artifact@v4
        with:
          path: ./downloads
          merge-multiple: true

      - name: Show downloaded files
        run: ls -laR downloads

      - name: Generate per-file SHA256 sums
        shell: bash
        run: |
          set -euxo pipefail
          cd downloads
          shopt -s nullglob
          for f in *.tar.gz *.zip; do
            sha256sum "$f" > "$f.sha256"
            echo "sha256($(basename "$f"))=$(cut -d' ' -f1 "$f.sha256")"
          done
          ls -la

      - name: Create GitHub Release and upload assets
        uses: softprops/action-gh-release@v2
        with:
          tag_name: ${{ github.ref_name }}
          name: ${{ github.ref_name }}
          generate_release_notes: true
          files: |
            downloads/*.tar.gz
            downloads/*.zip
            downloads/*.tar.gz.sha256
            downloads/*.zip.sha256
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

  publish-python:
    name: Publish Python package
    needs: validate
    runs-on: ubuntu-latest
    env:
      KIMI_WEB_STRICT_VERSION: "1"
      KIMI_WEB_EXPECT_VERSION: ${{ github.ref_name }}
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"

      - name: Set up Node.js (web build)
        uses: actions/setup-node@v4
        with:
          node-version: "20"
          cache: "npm"
          cache-dependency-path: web/package-lock.json

      - name: Build distributions
        run: make build-kimi-cli

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          packages-dir: dist


================================================
FILE: .github/workflows/release-kimi-sdk.yml
================================================
name: Release (kimi-sdk)

on:
  push:
    tags:
      - "kimi-sdk-*"

permissions:
  contents: read

jobs:
  validate:
    name: Validate tag
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Check version tag
        run: |
          python scripts/check_version_tag.py \
            --pyproject sdks/kimi-sdk/pyproject.toml \
            --expected-version "${GITHUB_REF_NAME#kimi-sdk-}"

  publish:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"

      - name: Build distributions
        run: make build-kimi-sdk

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          packages-dir: dist/kimi-sdk


================================================
FILE: .github/workflows/release-kosong.yml
================================================
name: Release (kosong)

on:
  push:
    tags:
      - "kosong-*"

permissions:
  contents: read

jobs:
  validate:
    name: Validate tag
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Check version tag
        run: |
          python scripts/check_version_tag.py \
            --pyproject packages/kosong/pyproject.toml \
            --expected-version "${GITHUB_REF_NAME#kosong-}"

  publish:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"

      - name: Build distributions
        run: make build-kosong

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          packages-dir: dist/kosong

  docs:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"

      - name: Install dependencies
        run: uv sync --frozen --all-extras --project packages/kosong

      - name: Generate API documentation
        run: |
          VERSION="${GITHUB_REF_NAME#kosong-}"
          uv run --project packages/kosong pdoc kosong \
            --docformat google \
            --footer-text "kosong ${VERSION}" \
            -o packages/kosong/docs

      - name: Disable Jekyll processing
        run: touch packages/kosong/docs/.nojekyll

      - name: Publish docs to gh-pages
        env:
          KOSONG_PAGES_TOKEN: ${{ secrets.KOSONG_PAGES_TOKEN }}
        run: |
          set -euo pipefail
          VERSION="${GITHUB_REF_NAME#kosong-}"
          PAGES_REPO="https://x-access-token:${KOSONG_PAGES_TOKEN}@github.com/MoonshotAI/kosong.git"
          PAGES_DIR="${RUNNER_TEMP}/kosong-gh-pages"

          git config --global user.name "github-actions[bot]"
          git config --global user.email "github-actions[bot]@users.noreply.github.com"

          rm -rf "$PAGES_DIR"
          git clone --depth 1 --branch gh-pages "$PAGES_REPO" "$PAGES_DIR"
          rsync -a --delete --exclude '.git' "packages/kosong/docs/" "$PAGES_DIR/"

          git -C "$PAGES_DIR" add -A
          if git -C "$PAGES_DIR" diff --cached --quiet; then
            echo "No documentation changes to publish."
            exit 0
          fi

          git -C "$PAGES_DIR" commit -m "docs: update for ${VERSION}"
          git -C "$PAGES_DIR" push origin gh-pages


================================================
FILE: .github/workflows/release-pykaos.yml
================================================
name: Release (pykaos)

on:
  push:
    tags:
      - "pykaos-*"

permissions:
  contents: read

jobs:
  validate:
    name: Validate tag
    runs-on: ubuntu-latest
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Check version tag
        run: |
          python scripts/check_version_tag.py \
            --pyproject packages/kaos/pyproject.toml \
            --expected-version "${GITHUB_REF_NAME#pykaos-}"

  publish:
    runs-on: ubuntu-latest
    needs: validate
    steps:
      - name: Checkout repository
        uses: actions/checkout@v4

      - name: Set up Python 3.14
        uses: actions/setup-python@v5
        with:
          python-version: "3.14"
          allow-prereleases: true

      - name: Set up uv
        uses: astral-sh/setup-uv@v1
        with:
          version: "0.8.5"

      - name: Build distributions
        run: make build-pykaos

      - name: Publish to PyPI
        uses: pypa/gh-action-pypi-publish@release/v1
        with:
          user: __token__
          password: ${{ secrets.PYPI_API_TOKEN }}
          packages-dir: dist/pykaos


================================================
FILE: .github/workflows/translator.yml
================================================
name: "Translator"
on:
  issues:
    types: [opened, edited]
  issue_comment:
    types: [created, edited]
  discussion:
    types: [created, edited]
  discussion_comment:
    types: [created, edited]
  pull_request_target:
    types: [opened, edited]
  pull_request_review_comment:
    types: [created, edited]

jobs:
  translate:
    permissions:
      issues: write
      discussions: write
      pull-requests: write
    runs-on: ubuntu-latest
    steps:
      - uses: lizheming/github-translate-action@1.1.2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        with:
          IS_MODIFY_TITLE: true
          APPEND_TRANSLATION: true


================================================
FILE: .github/workflows/typos.yml
================================================
name: Typo checker
on: [pull_request]

jobs:
  run:
    name: Spell Check with Typos
    runs-on: ubuntu-latest
    steps:
    - name: Checkout Actions Repository
      uses: actions/checkout@v4

    - name: Check spelling of the entire repository
      uses: crate-ci/typos@v1.38.1


================================================
FILE: .gitignore
================================================
# Python-generated files
__pycache__/
*.py[oc]
build/
dist/
wheels/
*.egg-info

# Virtual environments
.venv

# Project files
.vscode
.env
.env.local
/tests_local
uv.toml
.idea/*

# Build dependencies
src/kimi_cli/deps/bin
src/kimi_cli/deps/tmp

# Web build artifacts
src/kimi_cli/web/static/assets/

# Vis build artifacts
src/kimi_cli/vis/static/

# Generated reports
tests_ai/report.json

# nix build result
result
result-*

# macOS files
.DS_Store

# Rust files
target/

node_modules/
static/
.memo/
.entire
.claude

================================================
FILE: .pre-commit-config.yaml
================================================
default_install_hook_types:
  - pre-commit

repos:
  - repo: local
    hooks:
      - id: make-format-kimi-cli
        name: make format-kimi-cli
        entry: make format-kimi-cli
        language: system
        pass_filenames: false
      - id: make-check-kimi-cli
        name: make check-kimi-cli
        entry: make check-kimi-cli
        language: system
        pass_filenames: false


================================================
FILE: .python-version
================================================
3.14


================================================
FILE: AGENTS.md
================================================
# Kimi Code CLI

## Quick commands (use uv)

- `make prepare` (sync deps for all workspace packages and install git hooks)
- `make format`
- `make check`
- `make test`
- `make ai-test`
- `make build` / `make build-bin`

If running tools directly, use `uv run ...`.

## Project overview

Kimi Code CLI is a Python CLI agent for software engineering workflows. It supports an interactive
shell UI, ACP server mode for IDE integrations, and MCP tool loading.

## Tech stack

- Python 3.12+ (tooling configured for 3.14)
- CLI framework: Typer
- Async runtime: asyncio
- LLM framework: kosong
- MCP integration: fastmcp
- Logging: loguru
- Package management/build: uv + uv_build; PyInstaller for binaries
- Tests: pytest + pytest-asyncio; lint/format: ruff; types: pyright + ty

## Architecture overview

- **CLI entry**: `src/kimi_cli/cli.py` (Typer) parses flags (UI mode, agent spec, config, MCP)
  and routes into `KimiCLI` in `src/kimi_cli/app.py`.
- **App/runtime setup**: `KimiCLI.create` loads config (`src/kimi_cli/config.py`), chooses a
  model/provider (`src/kimi_cli/llm.py`), builds a `Runtime` (`src/kimi_cli/soul/agent.py`),
  loads an agent spec, restores `Context`, then constructs `KimiSoul`.
- **Agent specs**: YAML under `src/kimi_cli/agents/` loaded by `src/kimi_cli/agentspec.py`.
  Specs can `extend` base agents, select tools by import path, and define fixed subagents.
  System prompts live alongside specs; builtin args include `KIMI_NOW`, `KIMI_WORK_DIR`,
  `KIMI_WORK_DIR_LS`, `KIMI_AGENTS_MD`, `KIMI_SKILLS` (this file is injected via
  `KIMI_AGENTS_MD`).
- **Tooling**: `src/kimi_cli/soul/toolset.py` loads tools by import path, injects dependencies,
  and runs tool calls. Built-in tools live in `src/kimi_cli/tools/` (shell, file, web, todo,
  multiagent, dmail, think). MCP tools are loaded via `fastmcp`; CLI management is in
  `src/kimi_cli/mcp.py` and stored in the share dir.
- **Subagents**: `LaborMarket` in `src/kimi_cli/soul/agent.py` manages fixed and dynamic
  subagents. The Task tool (`src/kimi_cli/tools/multiagent/`) spawns them.
- **Core loop**: `src/kimi_cli/soul/kimisoul.py` is the main agent loop. It accepts user input,
  handles slash commands (`src/kimi_cli/soul/slash.py`), appends to `Context`
  (`src/kimi_cli/soul/context.py`), calls the LLM (kosong), runs tools, and performs compaction
  (`src/kimi_cli/soul/compaction.py`) when needed.
- **Approvals**: `src/kimi_cli/soul/approval.py` mediates user approvals for tool actions; the
  soul forwards approval requests over `Wire` for UI handling.
- **UI/Wire**: `src/kimi_cli/soul/run_soul` connects `KimiSoul` to a `Wire`
  (`src/kimi_cli/wire/`) so UI loops can stream events. UIs live in `src/kimi_cli/ui/`
  (shell/print/acp/wire).
- **Shell UI**: `src/kimi_cli/ui/shell/` handles interactive TUI input, shell command mode,
  and slash command autocomplete; it is the default interactive experience.
- **Slash commands**: Soul-level commands live in `src/kimi_cli/soul/slash.py`; shell-level
  commands live in `src/kimi_cli/ui/shell/slash.py`. The shell UI exposes both and dispatches
  based on the registry. Standard skills register `/skill:<skill-name>` and load `SKILL.md`
  as a user prompt; flow skills register `/flow:<skill-name>` and execute the embedded flow.

## Major modules and interfaces

- `src/kimi_cli/app.py`: `KimiCLI.create(...)` and `KimiCLI.run(...)` are the main programmatic
  entrypoints; this is what UI layers use.
- `src/kimi_cli/soul/agent.py`: `Runtime` (config, session, builtins), `Agent` (system prompt +
  toolset), and `LaborMarket` (subagent registry).
- `src/kimi_cli/soul/kimisoul.py`: `KimiSoul.run(...)` is the loop boundary; it emits Wire
  messages and executes tools via `KimiToolset`.
- `src/kimi_cli/soul/context.py`: conversation history + checkpoints; used by DMail for
  checkpointed replies.
- `src/kimi_cli/soul/toolset.py`: load tools, run tool calls, bridge to MCP tools.
- `src/kimi_cli/ui/*`: shell/print/acp frontends; they consume `Wire` messages.
- `src/kimi_cli/wire/*`: event types and transport used between soul and UI.

## Repo map

- `src/kimi_cli/agents/`: built-in agent YAML specs and prompts
- `src/kimi_cli/prompts/`: shared prompt templates
- `src/kimi_cli/soul/`: core runtime/loop, context, compaction, approvals
- `src/kimi_cli/tools/`: built-in tools
- `src/kimi_cli/ui/`: UI frontends (shell/print/acp/wire)
- `src/kimi_cli/acp/`: ACP server components
- `packages/kosong/`, `packages/kaos/`: workspace deps
  + Kosong is an LLM abstraction layer designed for modern AI agent applications.
    It unifies message structures, asynchronous tool orchestration, and pluggable
    chat providers so you can build agents with ease and avoid vendor lock-in.
  + PyKAOS is a lightweight Python library providing an abstraction layer for agents
    to interact with operating systems. File operations and command executions via KAOS
    can be easily switched between local environment and remote systems over SSH.
- `tests/`, `tests_ai/`: test suites
- `klips`: Kimi Code CLI Improvement Proposals

## Conventions and quality

- Python >=3.12 (ty config uses 3.14); line length 100.
- Ruff handles lint + format (rules: E, F, UP, B, SIM, I); pyright + ty for type checks.
- Tests use pytest + pytest-asyncio; files are `tests/test_*.py`.
- CLI entry points: `kimi` / `kimi-cli` -> `src/kimi_cli/cli.py`.
- User config: `~/.kimi/config.toml`; logs, sessions, and MCP config live in `~/.kimi/`.

## Git commit messages

Conventional Commits format:

```
<type>(<scope>): <subject>
```

Allowed types:
`feat`, `fix`, `test`, `refactor`, `chore`, `style`, `docs`, `perf`, `build`, `ci`, `revert`.

## Versioning

The project follows a **minor-bump-only** versioning scheme (`MAJOR.MINOR.PATCH`):

- **Patch** version is always `0`. Never bump it.
- **Minor** version is bumped for any change: new features, improvements, bug fixes, etc.
- **Major** version is only changed by explicit manual decision; it stays unchanged during
  normal development.

Examples: `0.68.0` → `0.69.0` → `0.70.0`; never `0.68.1`.

This rule applies to all packages in the repo (root, `packages/*`, `sdks/*`) as well as release
and skill workflows.

## Release workflow

1. Ensure `main` is up to date (pull latest).
2. Create a release branch, e.g. `bump-0.68` or `bump-pykaos-0.5.3`.
3. Update `CHANGELOG.md`: rename `[Unreleased]` to `[0.68] - YYYY-MM-DD`.
4. Update `pyproject.toml` version.
5. Run `uv sync` to align `uv.lock`.
6. Commit the branch and open a PR.
7. Merge the PR, then switch back to `main` and pull latest.
8. Tag and push:
   - `git tag 0.68` or `git tag pykaos-0.5.3`
   - `git push --tags`
9. GitHub Actions handles the release after tags are pushed.


================================================
FILE: CHANGELOG.md
================================================
# Changelog

<!--
Release notes will be parsed and available as /release-notes
The parser extracts for each version:
  - a short description (first paragraph after the version header)
  - bullet entries beginning with "- " under that version (across any subsections)
Internal builds may append content to the Unreleased section.
Only write entries that are worth mentioning to users.
-->

## Unreleased

- Shell: Show the current working directory, git branch, dirty state, and ahead/behind sync status directly in the prompt toolbar
- Shell: Surface active background bash task counts in the toolbar, rotate shortcut tips on a timer, and gracefully truncate the toolbar on narrow terminals to avoid overflow
- Web: Fix tool execution status synchronization on cancel and approval — tools now correctly transition to `output-denied` state when generation is stopped, and show the loading spinner (instead of checkmark) while executing after approval
- Web: Dismiss stale approval and question dialogs on session replay — when replaying a session or when the backend reports idle/stopped/error status, any pending approval/question dialogs are now properly dismissed to prevent orphaned interactive elements
- Web: Enable inline math formula rendering — single-dollar inline math (`$...$`) is now supported in addition to block math (`$$...$$`)
- Web: Improve Switch toggle proportions and alignment — the toggle track is now larger (36×20) with a consistent 16px thumb and smoother 16px travel animation

## 1.24.0 (2026-03-18)

- Shell: Increase pasted text placeholder thresholds to 1000 characters or 15 lines (previously 300 characters or 3 lines), making voice/typeless workflows less disruptive
- Core: Plan mode now supports multiple selectable approach options — when the agent's plan contains distinct alternative paths, `ExitPlanMode` can present 2–3 labeled choices for the user to pick which approach to execute; the chosen option is returned to the agent as the selected approach
- Core: Persist plan session ID and file path across process restarts — the plan session identifier and file slug are saved to `SessionState`, so restarting Kimi Code mid-plan resumes the same plan file in `~/.kimi/plans/` instead of creating a new one
- Core: Plan mode now supports incremental plan edits — the agent can use `StrReplaceFile` to surgically update sections of the plan file instead of rewriting the entire file with `WriteFile`, and non-plan file edits are now hard-blocked rather than requiring approval
- Core: Defer MCP startup and surface loading progress — MCP servers now initialize asynchronously after the shell UI starts, with live progress indicators showing connection status; Shell displays connecting and ready states in the status area, Web shows server connection status
- Core: Optimize lightweight startup paths — implement lazy-loading for CLI subcommands and version metadata, significantly reducing startup time for common commands like `--version` and `--help`
- Build: Fix Nix `FileCollisionError` for `bin/kimi` — remove duplicate entry point from `kimi-code` package so `kimi-cli` owns `bin/kimi` exclusively
- Shell: Preserve unsubmitted input across agent turns — text typed in the prompt while the agent is running is no longer lost when the turn ends; the user can press Enter to submit the draft as the next message
- Shell: Fix Ctrl-C and Ctrl-D not working correctly after an agent run completes — keyboard interrupts and EOF were silently swallowed instead of showing the tip or exiting the shell

## 1.23.0 (2026-03-17)

- Shell: Add background bash — the `Shell` tool now accepts `run_in_background=true` to launch long-running commands (builds, tests, servers) as background tasks, freeing the agent to continue working; new `TaskList`, `TaskOutput`, and `TaskStop` tools manage task lifecycle, and the system automatically notifies the agent when tasks reach a terminal state
- Shell: Add `/task` slash command with interactive task browser — a three-column TUI to view, monitor, and manage background tasks with real-time refresh, output preview, and keyboard-driven stopping
- Web: Fix global config not refreshing on other tabs when model is changed — when the model is changed in one tab, other tabs now detect the config update and automatically refresh their global config

## 1.22.0 (2026-03-13)

- Shell: Collapse long pasted text into `[Pasted text #n]` placeholders — text pasted via `Ctrl-V` or bracketed paste that exceeds 300 characters or 3 lines is displayed as a compact placeholder token in the prompt buffer while the full content is sent to the model; the external editor (`Ctrl-O`) expands placeholders for editing and re-folds them on save
- Shell: Cache pasted images as attachment placeholders — images pasted from the clipboard are stored on disk and shown as `[image:…]` tokens in the prompt, keeping the input buffer readable
- Shell: Fix UTF-16 surrogate characters in pasted text causing serialization errors — lone surrogates from Windows clipboard data are now sanitized before storage, preventing `UnicodeEncodeError` in history writes and JSON serialization
- Shell: Redesign slash command completion menu — replace the default completion popup with a full-width custom menu that shows command names and multi-line descriptions, with highlight and scroll support
- Shell: Fix cancelled shell commands not properly terminating child processes — when a running command is cancelled, the subprocess is now explicitly killed to prevent orphaned processes

## 1.21.0 (2026-03-12)

- Shell: Add inline running prompt with steer input — agent output is now rendered inside the prompt area while the model is running, and users can type and send follow-up messages (steers) without waiting for the turn to finish; approval requests and question panels are handled inline with keyboard navigation
- Core: Change steer injection from synthetic tool calls to regular user messages — steer content is now appended as a standard user message instead of a fake `_steer` tool-call/tool-result pair, improving compatibility with context serialization and visualization
- Wire: Add `SteerInput` event — a new Wire protocol event emitted when the user sends a follow-up steer message during a running turn
- Shell: Echo user input after submission in agent mode — the prompt symbol and entered text are printed back to the terminal for a clearer conversation transcript
- Shell: Improve session replay with steer inputs — replay now correctly reconstructs and displays steer messages alongside regular turns, and filters out internal system-reminder messages
- Shell: Fix upgrade command in toast notifications — the upgrade command text is now sourced from a single `UPGRADE_COMMAND` constant for consistency
- Core: Persist system prompt in `context.jsonl` — the system prompt is now written as the first record of the context file and frozen per session, so visualization tools can read the full conversation context and session restores reuse the original prompt instead of regenerating it
- Vis: Add session directory shortcuts in `kimi vis` — open the current session folder directly from the session page, copy the raw session directory path with `Copy DIR`, and support opening directories on both macOS and Windows
- Shell: Improve API key login UX — show a spinner during key verification, display a helpful hint when a 401 error suggests the wrong platform was selected, show a setup summary on success, and default thinking mode to "on"

## 1.20.0 (2026-03-11)

- Web: Add plan mode toggle in web UI — switch control in the input toolbar with a dashed blue border on the composer when plan mode is active, and support setting plan mode via the `set_plan_mode` Wire protocol method
- Core: Persist plan mode state across session restarts — `plan_mode` is saved to `SessionState` and restored when a session resumes
- Core: Fix StatusUpdate not reflecting plan mode changes triggered by tools — send a corrected `StatusUpdate` after `EnterPlanMode`/`ExitPlanMode` tool execution so the client sees the up-to-date state
- Core: Fix HTTP header values containing trailing whitespace/newlines on certain Linux systems (e.g. kernel 6.8.0-101) causing connection errors — strip whitespace from ASCII header values before sending
- Core: Fix OpenAI Responses provider sending implicit `reasoning.effort=null` which breaks Responses-compatible endpoints that require reasoning — reasoning parameters are now omitted unless explicitly set
- Vis: Add session download, import, export and delete — one-click ZIP download from session explorer and detail page, ZIP import into a dedicated `~/.kimi/imported_sessions/` directory with "Imported" filter toggle, `kimi export <session_id>` CLI command, and delete support for imported sessions with AlertDialog confirmation
- Core: Fix context compaction failing when conversation contains media parts (images, audio, video) — switch from blacklist filtering (exclude `ThinkPart`) to whitelist filtering (only keep `TextPart`) to prevent unsupported content types from being sent to the compaction API
- Web: Fix `@` file mention index not refreshing after switching sessions or when workspace files change — reset index on session switch, auto-refresh after 30s staleness, and support path-prefix search beyond the 500-file limit

## 1.19.0 (2026-03-10)

- Core: Add plan mode — the agent can enter a planning phase (`EnterPlanMode`) where only read-only tools (Glob, Grep, ReadFile) are available, write a structured plan to a file, and present it for user approval (`ExitPlanMode`) before executing; toggle manually via `/plan` slash command or `Shift-Tab` keyboard shortcut
- Vis: Add `kimi vis` command for launching an interactive visualization dashboard to inspect session traces — includes wire event timeline, context viewer, session explorer, and usage statistics
- Web: Fix session stream state management — guard against null reference errors during state resets and preserve slash commands across session switches to avoid a brief empty gap

## 1.18.0 (2026-03-09)

- ACP: Support embedded resource content in ACP mode so that Zed's `@` file references correctly include file contents
- Core: Use `parameters_json_schema` instead of `parameters` in Google GenAI provider to bypass Pydantic validation that rejects standard JSON Schema metadata fields in MCP tools
- Shell: Enhance `Ctrl-V` clipboard paste to support video files in addition to images — video file paths are inserted as text, and a crash when clipboard data is `None` is fixed
- Core: Pass session ID as `user_id` metadata to Anthropic API
- Web: Preserve slash commands on WebSocket reconnect and add automatic retry logic for session initialization

## 1.17.0 (2026-03-03)

- Core: Add `/export` command to export current session context (messages, metadata) to a Markdown file, and `/import` command to import context from a file or another session ID into the current session
- Shell: Show token counts (used/total) alongside context usage percentage in the status bar (e.g., `context: 42.0% (4.2k/10.0k)`)
- Shell: Rotate keyboard shortcut tips in the toolbar — tips cycle through available shortcuts on each prompt submission to save horizontal space
- MCP: Add loading indicators for MCP server connections — Shell displays a "Connecting to MCP servers..." spinner and Web shows a status message while MCP tools are being loaded
- Web: Fix scrollable file list overflow in the toolbar changes panel
- Core: Add `compaction_trigger_ratio` config option (default `0.85`) to control when auto-compaction triggers — compaction now fires when context usage reaches the configured ratio or when remaining space falls below `reserved_context_size`, whichever comes first
- Core: Support custom instructions in `/compact` command (e.g., `/compact keep database discussions`) to guide what the compaction preserves
- Web: Add URL action parameters (`?action=create` to open create-session dialog, `?action=create-in-dir&workDir=xxx` to create a session directly) for external integrations, and support Cmd/Ctrl+Click on new-session buttons to open session creation in a new browser tab
- Web: Add todo list display in prompt toolbar — shows task progress with expandable panel when the `SetTodoList` tool is active
- ACP: Add authentication check for session operations with `AUTH_REQUIRED` error responses for terminal-based login flow

## 1.16.0 (2026-02-27)

- Web: Update ASCII logo banner to a new styled design
- Core: Add `--add-dir` CLI option and `/add-dir` slash command to expand the workspace scope with additional directories — added directories are accessible to all file tools (read, write, glob, replace), persisted across sessions, and shown in the system prompt
- Shell: Add `Ctrl-O` keyboard shortcut to open the current input in an external editor (`$VISUAL`/`$EDITOR`), with auto-detection fallback to VS Code, Vim, Vi, or Nano
- Shell: Add `/editor` slash command to configure and switch the default external editor, with interactive selection and persistent config storage
- Shell: Add `/new` slash command to create and switch to a new session without restarting Kimi Code CLI
- Wire: Auto-hide `AskUserQuestion` tool when the client does not support the `supports_question` capability, preventing the LLM from invoking unsupported interactions
- Core: Estimate context token count after compaction so context usage percentage is not reported as 0%
- Web: Show context usage percentage with one decimal place for better precision

## 1.15.0 (2026-02-27)

- Shell: Simplify input prompt by removing username prefix for a cleaner appearance
- Shell: Add horizontal separator line and expanded keyboard shortcut hints to the toolbar
- Shell: Add number key shortcuts (1–5) for quick option selection in question and approval panels, with redesigned bordered panel UI and keyboard hints
- Shell: Add tab-style navigation for multi-question panels — use Left/Right arrows or Tab to switch between questions, with visual indicators for answered, current, and pending states, and automatic state restoration when revisiting a question
- Shell: Allow Space key to submit single-select questions in the question panel
- Web: Add tab-style navigation for multi-question dialogs with clickable tab bar, keyboard navigation, and state restoration when revisiting a question
- Core: Set process title to "Kimi Code" (visible in `ps` / Activity Monitor / terminal tab title) and label web worker subprocesses as "kimi-code-worker"

## 1.14.0 (2026-02-26)

- Shell: Make FetchURL tool's URL parameter a clickable hyperlink in the terminal
- Tool: Add `AskUserQuestion` tool for presenting structured questions with predefined options during execution, supporting single-select, multi-select, and custom text input
- Wire: Add `QuestionRequest` / `QuestionResponse` message types and capability negotiation for structured question interactions
- Shell: Add interactive question panel for `AskUserQuestion` with keyboard-driven option selection
- Web: Add `QuestionDialog` component for answering structured questions inline, replacing the prompt composer when a question is pending
- Core: Persist session state across sessions — approval decisions (YOLO mode, auto-approved actions) and dynamic subagents are now saved and restored when resuming a session
- Core: Use atomic JSON writes for metadata and session state files to prevent data corruption on crash
- Wire: Add `steer` request to inject user messages into an active agent turn (protocol version 1.4)
- Web: Allow Cmd/Ctrl+Click on FetchURL tool's URL parameter to open the link in a new browser tab, with platform-appropriate tooltip hint

## 1.13.0 (2026-02-24)

- Core: Add automatic connection recovery that recreates the HTTP client on connection and timeout errors before retrying, improving resilience against transient network failures

## 1.12.0 (2026-02-11)

- Web: Add subagent activity rendering to display subagent steps (thinking, tool calls, text) inside Task tool messages
- Web: Add Think tool rendering as a lightweight reasoning-style block
- Web: Replace emoji status indicators with Lucide icons for tool states and add category-specific icons for tool names
- Web: Enhance Reasoning component with improved thinking labels and status icons
- Web: Enhance Todo component with status icons and improved styling
- Web: Implement WebSocket reconnection with automatic request resending and stale connection watchdog
- Web: Enhance session creation dialog with command value handling
- Web: Support tilde (`~`) expansion in session work directory paths
- Web: Fix assistant message content overflow clipping
- Wire: Fix deadlock when multiple subagents run concurrently by not blocking the UI loop on approval and tool-call requests
- Wire: Clean up stale pending requests after agent turn ends
- Web: Show placeholder text in prompt input with hints for slash commands and file mentions
- Web: Fix Ctrl+C not working in uvicorn web server by restoring default SIGINT handler and terminal state after shell mode exits
- Web: Improve session stop handling with proper async cleanup and timeout
- ACP: Add protocol version negotiation framework for client-server compatibility
- ACP: Add session resume method to restore session state (experimental)

## 1.11.0 (2026-02-10)

- Web: Move context usage indicator from workspace header to prompt toolbar with a hover card showing detailed token usage breakdown
- Web: Add folder indicator with work directory path to the bottom of the file changes panel
- Web: Fix stderr not being restored when switching to web mode, which could suppress web server error output
- Web: Fix port availability check by setting SO_REUSEADDR on the test socket

## 1.10.0 (2026-02-09)

- Web: Add copy and fork action buttons to assistant messages for quick content copying and session forking
- Web: Add keyboard shortcuts for approval actions — press `1` to approve, `2` to approve for session, `3` to decline
- Web: Add message queueing — queue follow-up messages while the AI is processing; queued messages are sent automatically when the response completes
- Web: Replace Git diff status bar with unified prompt toolbar showing activity status, message queue, and file changes in collapsible tabs
- Web: Load global MCP configuration in web worker so web sessions can use MCP tools
- Web: Improve mobile prompt input UX — reduce textarea min-height, add `autoComplete="off"`, and disable focus ring on small screens
- Web: Handle models that stream text before thinking by ensuring thinking messages always appear before text in the message list
- Web: Show more specific status messages during session connection ("Loading history...", "Starting environment..." instead of generic "Connecting...")
- Web: Send error status when session environment initialization fails instead of leaving UI in a waiting state
- Web: Auto-reconnect when no session status received within 15 seconds after history replay completes
- Web: Use non-blocking file I/O in session streaming to avoid blocking the event loop during history replay

## 1.9.0 (2026-02-06)

- Config: Add `default_yolo` config option to enable YOLO (auto-approve) mode by default
- Config: Accept both `max_steps_per_turn` and `max_steps_per_run` as aliases for the loop control setting
- Wire: Add `replay` request to stream recorded Wire events (protocol version 1.3)
- Web: Add session fork feature to branch off a new session from any assistant response
- Web: Add session archive feature with auto-archive for sessions older than 15 days
- Web: Add multi-select mode for bulk archive, unarchive, and delete operations
- Web: Add media preview for tool results (images/videos from ReadMediaFile) with clickable thumbnails
- Web: Add shell command and todo list display components for tool outputs
- Web: Add activity status indicator showing agent state (processing, waiting for approval, etc.)
- Web: Add error fallback UI when images fail to load
- Web: Redesign tool input UI with expandable parameters and syntax highlighting for long values
- Web: Show compaction indicator when context is being compacted
- Web: Improve auto-scroll behavior in chat for smoother following of new content
- Web: Update `last_session_id` for work directory when session stream starts
- Shell: Remove `Ctrl-/` keyboard shortcut that triggered `/help` command
- Rust: Move the Rust implementation to `MoonshotAI/kimi-agent-rs` with independent releases; binary renamed to `kimi-agent`
- Core: Preserve session id when reloading configuration so the session resumes correctly
- Shell: Fix session replay showing messages that were cleared by `/clear` or `/reset`
- Web: Fix approval request states not updating when session is interrupted or cancelled
- Web: Fix IME composition issue when selecting slash commands
- Web: Fix UI not clearing messages after `/clear`, `/reset`, or `/compact` commands

## 1.8.0 (2026-02-05)

- CLI: Fix startup errors (e.g. invalid config files) being silently swallowed instead of displayed

## 1.7.0 (2026-02-05)

- Rust: Add `kagent`, the Rust implementation of Kimi agent kernel with wire-mode support (experimental)
- Auth: Fix OAuth token refresh conflicts when running multiple sessions simultaneously
- Web: Add file mention menu (`@`) to reference uploaded attachments and workspace files with autocomplete
- Web: Add slash command menu in chat input with autocomplete, keyboard navigation, and alias support
- Web: Prompt to create directory when specified path doesn't exist during session creation
- Web: Fix authentication token persistence by switching from sessionStorage to localStorage with 24-hour expiry
- Web: Add server-side pagination for session list with virtualized scrolling for better performance
- Web: Improve session and work directories loading with smarter caching and invalidation
- Web: Fix WebSocket errors during history replay by checking connection state before sending
- Web: Git diff status bar now shows untracked files (new files not yet added to git)
- Web: Restrict sensitive APIs only in public mode; update origin enforcement logic

## 1.6 (2026-02-03)

- Web: Add token-based authentication and access control for network mode (`--network`, `--lan-only`, `--public`)
- Web: Add security options: `--auth-token`, `--allowed-origins`, `--restrict-sensitive-apis`, `--dangerously-omit-auth`
- Web: Change `--host` option to bind to specific IP address; add automatic network address detection
- Web: Fix WebSocket disconnect when creating new sessions
- Web: Increase maximum image dimension from 1024 to 4096 pixels
- Web: Improve UI responsiveness with enhanced hover effects and better layout handling
- Wire: Add `TurnEnd` event to signal the completion of an agent turn (protocol version 1.2)
- Core: Fix custom agent prompt files containing `$` causing silent startup failure

## 1.5 (2026-01-30)

- Web: Add Git diff status bar showing uncommitted changes in session working directory
- Web: Add "Open in" menu for opening files/directories in Terminal, VS Code, Cursor, or other local applications
- Web: Add search functionality to filter sessions by title or working directory
- Web: Improve session title display with proper overflow handling

## 1.4 (2026-01-30)

- Shell: Merge `/login` and `/setup` commands; `/setup` is now an alias for `/login`
- Shell: `/usage` now shows remaining quota percentage; add `/status` alias
- Config: Add `KIMI_SHARE_DIR` environment variable to customize the share directory path (default: `~/.kimi`)
- Web: Add new Web UI for browser-based interaction
- CLI: Add `kimi web` subcommand to launch the Web UI server
- Auth: Fix encoding error when device name or OS version contains non-ASCII characters
- Auth: OAuth credentials are now stored in files instead of keyring; existing tokens are automatically migrated on startup
- Auth: Fix authorization failure after the system sleeps or hibernates

## 1.3 (2026-01-28)

- Auth: Fix authentication issue during agent turns
- Tool: Wrap media content with descriptive tags in `ReadMediaFile` for better path traceability

## 1.2 (2026-01-27)

- UI: Show description for `kimi-for-coding` model

## 1.1 (2026-01-27)

- LLM: Fix `kimi-for-coding` model's capabilities

## 1.0 (2026-01-27)

- Shell: Add `/login` and `/logout` slash commands for login and logout
- CLI: Add `kimi login` and `kimi logout` subcommands
- Core: Fix subagent approval request handling

## 0.88 (2026-01-26)

- MCP: Remove `Mcp-Session-Id` header when connecting to MCP servers to fix compatibility

## 0.87 (2026-01-25)

- Shell: Fix Markdown rendering error when HTML blocks appear outside any element
- Skills: Add more user-level and project-level skills directory candidates
- Core: Improve system prompt guidance for media file generation and processing tasks
- Shell: Fix image pasting from clipboard on macOS

## 0.86 (2026-01-24)

- Build: Fix binary builds

## 0.85 (2026-01-24)

- Shell: Cache pasted images to disk for persistence across sessions
- Shell: Deduplicate cached attachments based on content hash
- Shell: Fix display of image/audio/video attachments in message history
- Tool: Use file path as media identifier in `ReadMediaFile` for better traceability
- Tool: Fix some MP4 files not being recognized as videos
- Shell: Handle Ctrl-C during slash command execution
- Shell: Fix shlex parsing error in shell mode when input contains invalid shell syntax
- Shell: Fix stderr output from MCP servers and third-party libraries polluting shell UI
- Wire: Graceful shutdown with proper cleanup of pending requests when connection closes or Ctrl-C is received

## 0.84 (2026-01-22)

- Build: Add cross-platform standalone binary builds for Windows, macOS (with code signing and notarization), and Linux (x86_64 and ARM64)
- Shell: Fix slash command autocomplete showing suggestions for exact command/alias matches
- Tool: Treat SVG files as text instead of images
- Flow: Support D2 markdown block strings (`|md` syntax) for multiline node labels in flow skills
- Core: Fix possible "event loop is closed" error after running `/reload`, `/setup`, or `/clear`
- Core: Fix panic when `/clear` is used in a continued session

## 0.83 (2026-01-21)

- Tool: Add `ReadMediaFile` tool for reading image/video files; `ReadFile` now focuses on text files only
- Skills: Flow skills now also register as `/skill:<skill-name>` commands (in addition to `/flow:<skill-name>`)

## 0.82 (2026-01-21)

- Tool: Allow `WriteFile` and `StrReplaceFile` tools to edit/write files outside the working directory when using absolute paths
- Tool: Upload videos to Kimi files API when using Kimi provider, replacing inline data URLs with `ms://` references
- Config: Add `reserved_context_size` setting to customize auto-compaction trigger threshold (default: 50000 tokens)

## 0.81 (2026-01-21)

- Skills: Add flow skill type with embedded Agent Flow (Mermaid/D2) in SKILL.md, invoked via `/flow:<skill-name>` commands
- CLI: Remove `--prompt-flow` option; use flow skills instead
- Core: Replace `/begin` command with `/flow:<skill-name>` commands for flow skills

## 0.80 (2026-01-20)

- Wire: Add `initialize` method for exchanging client/server info, external tools registration and slash commands advertisement
- Wire: Support external tool calls via Wire protocol
- Wire: Rename `ApprovalRequestResolved` to `ApprovalResponse` (backwards-compatible)

## 0.79 (2026-01-19)

- Skills: Add project-level skills support, discovered from `.agents/skills/` (or `.kimi/skills/`, `.claude/skills/`)
- Skills: Unified skills discovery with layered loading (builtin → user → project); user-level skills now prefer `~/.config/agents/skills/`
- Shell: Support fuzzy matching for slash command autocomplete
- Shell: Enhanced approval request preview with shell command and diff content display, use `Ctrl-E` to expand full content
- Wire: Add `ShellDisplayBlock` type for shell command display in approval requests
- Shell: Reorder `/help` to show keyboard shortcuts before slash commands
- Wire: Return proper JSON-RPC 2.0 error responses for invalid requests

## 0.78 (2026-01-16)

- CLI: Add D2 flowchart format support for Prompt Flow (`.d2` extension)

## 0.77 (2026-01-15)

- Shell: Fix line breaking in `/help` and `/changelog` fullscreen pager display
- Shell: Use `/model` to toggle thinking mode instead of Tab key
- Config: Add `default_thinking` config option (need to run `/model` to select thinking mode after upgrade)
- LLM: Add `always_thinking` capability for models that always use thinking mode
- CLI: Rename `--command`/`-c` to `--prompt`/`-p`, keep `--command`/`-c` as alias, remove `--query`/`-q`
- Wire: Fix approval requests not responding properly in Wire mode
- CLI: Add `--prompt-flow` option to load a Mermaid flowchart file as a Prompt Flow
- Core: Add `/begin` slash command if a Prompt Flow is loaded to start the flow
- Core: Replace Ralph Loop with Prompt Flow-based implementation

## 0.76 (2026-01-12)

- Tool: Make `ReadFile` tool description reflect model capabilities for image/video support
- Tool: Fix TypeScript files (`.ts`, `.tsx`, `.mts`, `.cts`) being misidentified as video files
- Shell: Allow slash commands (`/help`, `/exit`, `/version`, `/changelog`, `/feedback`) in shell mode
- Shell: Improve `/help` with fullscreen pager, showing slash commands, skills, and keyboard shortcuts
- Shell: Improve `/changelog` and `/mcp` display with consistent bullet-style formatting
- Shell: Show current model name in the bottom status bar
- Shell: Add `Ctrl-/` shortcut to show help

## 0.75 (2026-01-09)

- Tool: Improve `ReadFile` tool description
- Skills: Add built-in `kimi-cli-help` skill to answer Kimi Code CLI usage and configuration questions

## 0.74 (2026-01-09)

- ACP: Allow ACP clients to select and switch models (with thinking variants)
- ACP: Add `terminal-auth` authentication method for setup flow
- CLI: Deprecate `--acp` option in favor of `kimi acp` subcommand
- Tool: Support reading image and video files in `ReadFile` tool

## 0.73 (2026-01-09)

- Skills: Add built-in skill-creator skill shipped with the package
- Tool: Expand `~` to the home directory in `ReadFile` paths
- MCP: Ensure MCP tools finish loading before starting the agent loop
- Wire: Fix Wire mode failing to accept valid `cancel` requests
- Setup: Allow `/model` to switch between all available models for the selected provider
- Lib: Re-export all Wire message types from `kimi_cli.wire.types`, as a replacement of `kimi_cli.wire.message`
- Loop: Add `max_ralph_iterations` loop control config to limit extra Ralph iterations
- Config: Rename `max_steps_per_run` to `max_steps_per_turn` in loop control config (backward-compatible)
- CLI: Add `--max-steps-per-turn`, `--max-retries-per-step` and `--max-ralph-iterations` options to override loop control config
- SlashCmd: Make `/yolo` toggle auto-approve mode
- UI: Show a YOLO badge in the shell prompt

## 0.72 (2026-01-04)

- Python: Fix installation on Python 3.14.

## 0.71 (2026-01-04)

- ACP: Route file reads/writes and shell commands through ACP clients for synced edits/output
- Shell: Add `/model` slash command to switch default models and reload when using the default config
- Skills: Add `/skill:<name>` slash commands to load `SKILL.md` instructions on demand
- CLI: Add `kimi info` subcommand for version/protocol details (supports `--json`)
- CLI: Add `kimi term` to launch the Toad terminal UI
- Python: Bump the default tooling/CI version to 3.14

## 0.70 (2025-12-31)

- CLI: Add `--final-message-only` (and `--quiet` alias) to only output the final assistant message in print UI
- LLM: Add `video_in` model capability and support video inputs

## 0.69 (2025-12-29)

- Core: Support discovering skills in `~/.kimi/skills` or `~/.claude/skills`
- Python: Lower the minimum required Python version to 3.12
- Nix: Add flake packaging; install with `nix profile install .#kimi-cli` or run `nix run .#kimi-cli`
- CLI: Add `kimi-cli` script alias for invoking the CLI; can be run via `uvx kimi-cli`
- Lib: Move LLM config validation into `create_llm` and return `None` when missing config

## 0.68 (2025-12-24)

- CLI: Add `--config` and `--config-file` options to pass in config JSON/TOML
- Core: Allow `Config` in addition to `Path` for the `config` parameter of `KimiCLI.create`
- Tool: Include diff display blocks in `WriteFile` and `StrReplaceFile` approvals/results
- Wire: Add display blocks to approval requests (including diffs) with backward-compatible defaults
- ACP: Show file diff previews in tool results and approval prompts
- ACP: Connect to MCP servers managed by ACP clients
- ACP: Run shell commands in ACP client terminal if supported
- Lib: Add `KimiToolset.find` method to find tools by class or name
- Lib: Add `ToolResultBuilder.display` method to append display blocks to tool results
- MCP: Add `kimi mcp auth` and related subcommands to manage MCP authorization

## 0.67 (2025-12-22)

- ACP: Advertise slash commands in single-session ACP mode (`kimi --acp`)
- MCP: Add `mcp.client` config section to configure MCP tool call timeout and other future options
- Core: Improve default system prompt and `ReadFile` tool
- UI: Fix Ctrl-C not working in some rare cases

## 0.66 (2025-12-19)

- Lib: Provide `token_usage` and `message_id` in `StatusUpdate` Wire message
- Lib: Add `KimiToolset.load_tools` method to load tools with dependency injection
- Lib: Add `KimiToolset.load_mcp_tools` method to load MCP tools
- Lib: Move `MCPTool` from `kimi_cli.tools.mcp` to `kimi_cli.soul.toolset`
- Lib: Add `InvalidToolError`, `MCPConfigError` and `MCPRuntimeError`
- Lib: Make the detailed Kimi Code CLI exception classes extend `ValueError` or `RuntimeError`
- Lib: Allow passing validated `list[fastmcp.mcp_config.MCPConfig]` as `mcp_configs` for `KimiCLI.create` and `load_agent`
- Lib: Fix exception raising for `KimiCLI.create`, `load_agent`, `KimiToolset.load_tools` and `KimiToolset.load_mcp_tools`
- LLM: Add provider type `vertexai` to support Vertex AI
- LLM: Rename Gemini Developer API provider type from `google_genai` to `gemini`
- Config: Migrate config file from JSON to TOML
- MCP: Connect to MCP servers in background and parallel to reduce startup time
- MCP: Add `mcp-session-id` HTTP header when connecting to MCP servers
- Lib: Split slash commands (prev "meta commands") into two groups: Shell-level and KimiSoul-level
- Lib: Add `available_slash_commands` property to `Soul` protocol
- ACP: Advertise slash commands `/init`, `/compact` and `/yolo` to ACP clients
- SlashCmd: Add `/mcp` slash command to display MCP server and tool status

## 0.65 (2025-12-16)

- Lib: Support creating named sessions via `Session.create(work_dir, session_id)`
- CLI: Automatically create new session when specified session ID is not found
- CLI: Delete empty sessions on exit and ignore sessions whose context file is empty when listing
- UI: Improve session replaying
- Lib: Add `model_config: LLMModel | None` and `provider_config: LLMProvider | None` properties to `LLM` class
- MetaCmd: Add `/usage` meta command to show API usage for Kimi Code users

## 0.64 (2025-12-15)

- UI: Fix UTF-16 surrogate characters input on Windows
- Core: Add `/sessions` meta command to list existing sessions and switch to a selected one
- CLI: Add `--session/-S` option to specify session ID to resume
- MCP: Add `kimi mcp` subcommand group to manage global MCP config file `~/.kimi/mcp.json`

## 0.63 (2025-12-12)

- Tool: Fix `FetchURL` tool incorrect output when fetching via service fails
- Tool: Use `bash` instead of `sh` in `Shell` tool for better compatibility
- Tool: Fix `Grep` tool unicode decoding error on Windows
- ACP: Support ACP session continuation (list/load sessions) with `kimi acp` subcommand
- Lib: Add `Session.find` and `Session.list` static methods to find and list sessions
- ACP: Update agent plans on the client side when `SetTodoList` tool is called
- UI: Prevent normal messages starting with `/` from being treated as meta commands

## 0.62 (2025-12-08)

- ACP: Fix tool results (including Shell tool output) not being displayed in ACP clients like Zed
- ACP: Fix compatibility with the latest version of Zed IDE (0.215.3)
- Tool: Use PowerShell instead of CMD on Windows for better usability
- Core: Fix startup crash when there is broken symbolic link in the working directory
- Core: Add builtin `okabe` agent file with `SendDMail` tool enabled
- CLI: Add `--agent` option to specify builtin agents like `default` and `okabe`
- Core: Improve compaction logic to better preserve relevant information

## 0.61 (2025-12-04)

- Lib: Fix logging when used as a library
- Tool: Harden file path check to protect against shared-prefix escape
- LLM: Improve compatibility with some third-party OpenAI Responses and Anthropic API providers

## 0.60 (2025-12-01)

- LLM: Fix interleaved thinking for Kimi and OpenAI-compatible providers

## 0.59 (2025-11-28)

- Core: Move context file location to `.kimi/sessions/{workdir_md5}/{session_id}/context.jsonl`
- Lib: Move `WireMessage` type alias to `kimi_cli.wire.message`
- Lib: Add `kimi_cli.wire.message.Request` type alias request messages (which currently only includes `ApprovalRequest`)
- Lib: Add `kimi_cli.wire.message.is_event`, `is_request` and `is_wire_message` utility functions to check the type of wire messages
- Lib: Add `kimi_cli.wire.serde` module for serialization and deserialization of wire messages
- Lib: Change `StatusUpdate` Wire message to not using `kimi_cli.soul.StatusSnapshot`
- Core: Record Wire messages to a JSONL file in session directory
- Core: Introduce `TurnBegin` Wire message to mark the beginning of each agent turn
- UI: Print user input again with a panel in shell mode
- Lib: Add `Session.dir` property to get the session directory path
- UI: Improve "Approve for session" experience when there are multiple parallel subagents
- Wire: Reimplement Wire server mode (which is enabled with `--wire` option)
- Lib: Rename `ShellApp` to `Shell`, `PrintApp` to `Print`, `ACPServer` to `ACP` and `WireServer` to `WireOverStdio` for better consistency
- Lib: Rename `KimiCLI.run_shell_mode` to `run_shell`, `run_print_mode` to `run_print`, `run_acp_server` to `run_acp`, and `run_wire_server` to `run_wire_stdio` for better consistency
- Lib: Add `KimiCLI.run` method to run a turn with given user input and yield Wire messages
- Print: Fix stream-json print mode not flushing output properly
- LLM: Improve compatibility with some OpenAI and Anthropic API providers
- Core: Fix chat provider error after compaction when using Anthropic API

## 0.58 (2025-11-21)

- Core: Fix field inheritance of agent spec files when using `extend`
- Core: Support using MCP tools in subagents
- Tool: Add `CreateSubagent` tool to create subagents dynamically (not enabled in default agent)
- Tool: Use MoonshotFetch service in `FetchURL` tool for Kimi Code plan
- Tool: Truncate Grep tool output to avoid exceeding token limit

## 0.57 (2025-11-20)

- LLM: Fix Google GenAI provider when thinking toggle is not on
- UI: Improve approval request wordings
- Tool: Remove `PatchFile` tool
- Tool: Rename `Bash`/`CMD` tool to `Shell` tool
- Tool: Move `Task` tool to `kimi_cli.tools.multiagent` module

## 0.56 (2025-11-19)

- LLM: Add support for Google GenAI provider

## 0.55 (2025-11-18)

- Lib: Add `kimi_cli.app.enable_logging` function to enable logging when directly using `KimiCLI` class
- Core: Fix relative path resolution in agent spec files
- Core: Prevent from panic when LLM API connection failed
- Tool: Optimize `FetchURL` tool for better content extraction
- Tool: Increase MCP tool call timeout to 60 seconds
- Tool: Provide better error message in `Glob` tool when pattern is `**`
- ACP: Fix thinking content not displayed properly
- UI: Minor UI improvements in shell mode

## 0.54 (2025-11-13)

- Lib: Move `WireMessage` from `kimi_cli.wire.message` to `kimi_cli.wire`
- Print: Fix `stream-json` output format missing the last assistant message
- UI: Add warning when API key is overridden by `KIMI_API_KEY` environment variable
- UI: Make a bell sound when there's an approval request
- Core: Fix context compaction and clearing on Windows

## 0.53 (2025-11-12)

- UI: Remove unnecessary trailing spaces in console output
- Core: Throw error when there are unsupported message parts
- MetaCmd: Add `/yolo` meta command to enable YOLO mode after startup
- Tool: Add approval request for MCP tools
- Tool: Disable `Think` tool in default agent
- CLI: Restore thinking mode from last time when `--thinking` is not specified
- CLI: Fix `/reload` not working in binary packed by PyInstaller

## 0.52 (2025-11-10)

- CLI: Remove `--ui` option in favor of `--print`, `--acp`, and `--wire` flags (shell is still the default)
- CLI: More intuitive session continuation behavior
- Core: Add retry for LLM empty responses
- Tool: Change `Bash` tool to `CMD` tool on Windows
- UI: Fix completion after backspacing
- UI: Fix code block rendering issues on light background colors

## 0.51 (2025-11-08)

- Lib: Rename `Soul.model` to `Soul.model_name`
- Lib: Rename `LLMModelCapability` to `ModelCapability` and move to `kimi_cli.llm`
- Lib: Add `"thinking"` to `ModelCapability`
- Lib: Remove `LLM.supports_image_in` property
- Lib: Add required `Soul.model_capabilities` property
- Lib: Rename `KimiSoul.set_thinking_mode` to `KimiSoul.set_thinking`
- Lib: Add `KimiSoul.thinking` property
- UI: Better checks and notices for LLM model capabilities
- UI: Clear the screen for `/clear` meta command
- Tool: Support auto-downloading ripgrep on Windows
- CLI: Add `--thinking` option to start in thinking mode
- ACP: Support thinking content in ACP mode

## 0.50 (2025-11-07)

### Changed

- Improve UI look and feel
- Improve Task tool observability

## 0.49 (2025-11-06)

### Fixed

- Minor UX improvements

## 0.48 (2025-11-06)

### Added

- Support Kimi K2 thinking mode

## 0.47 (2025-11-05)

### Fixed

- Fix Ctrl-W not working in some environments
- Do not load SearchWeb tool when the search service is not configured

## 0.46 (2025-11-03)

### Added

- Introduce Wire over stdio for local IPC (experimental, subject to change)
- Support Anthropic provider type

### Fixed

- Fix binary packed by PyInstaller not working due to wrong entrypoint

## 0.45 (2025-10-31)

### Added

- Allow `KIMI_MODEL_CAPABILITIES` environment variable to override model capabilities
- Add `--no-markdown` option to disable markdown rendering
- Support `openai_responses` LLM provider type

### Fixed

- Fix crash when continuing a session

## 0.44 (2025-10-30)

### Changed

- Improve startup time

### Fixed

- Fix potential invalid bytes in user input

## 0.43 (2025-10-30)

### Added

- Basic Windows support (experimental)
- Display warnings when base URL or API key is overridden in environment variables
- Support image input if the LLM model supports it
- Replay recent context history when continuing a session

### Fixed

- Ensure new line after executing shell commands

## 0.42 (2025-10-28)

### Added

- Support Ctrl-J or Alt-Enter to insert a new line

### Changed

- Change mode switch shortcut from Ctrl-K to Ctrl-X
- Improve overall robustness

### Fixed

- Fix ACP server `no attribute` error

## 0.41 (2025-10-26)

### Fixed

- Fix a bug for Glob tool when no matching files are found
- Ensure reading files with UTF-8 encoding

### Changed

- Disable reading command/query from stdin in shell mode
- Clarify the API platform selection in `/setup` meta command

## 0.40 (2025-10-24)

### Added

- Support `ESC` key to interrupt the agent loop

### Fixed

- Fix SSL certificate verification error in some rare cases
- Fix possible decoding error in Bash tool

## 0.39 (2025-10-24)

### Fixed

- Fix context compaction threshold check
- Fix panic when SOCKS proxy is set in the shell session

## 0.38 (2025-10-24)

- Minor UX improvements

## 0.37 (2025-10-24)

### Fixed

- Fix update checking

## 0.36 (2025-10-24)

### Added

- Add `/debug` meta command to debug the context
- Add auto context compaction
- Add approval request mechanism
- Add `--yolo` option to automatically approve all actions
- Render markdown content for better readability

### Fixed

- Fix "unknown error" message when interrupting a meta command

## 0.35 (2025-10-22)

### Changed

- Minor UI improvements
- Auto download ripgrep if not found in the system
- Always approve tool calls in `--print` mode
- Add `/feedback` meta command

## 0.34 (2025-10-21)

### Added

- Add `/update` meta command to check for updates and auto-update in background
- Support running interactive shell commands in raw shell mode
- Add `/setup` meta command to setup LLM provider and model
- Add `/reload` meta command to reload configuration

## 0.33 (2025-10-18)

### Added

- Add `/version` meta command
- Add raw shell mode, which can be switched to by Ctrl-K
- Show shortcuts in bottom status line

### Fixed

- Fix logging redirection
- Merge duplicated input histories

## 0.32 (2025-10-16)

### Added

- Add bottom status line
- Support file path auto-completion (`@filepath`)

### Fixed

- Do not auto-complete meta command in the middle of user input

## 0.31 (2025-10-14)

### Fixed

- Fix step interrupting by Ctrl-C, for real

## 0.30 (2025-10-14)

### Added

- Add `/compact` meta command to allow manually compacting context

### Fixed

- Fix `/clear` meta command when context is empty

## 0.29 (2025-10-14)

### Added

- Support Enter key to accept completion in shell mode
- Remember user input history across sessions in shell mode
- Add `/reset` meta command as an alias for `/clear`

### Fixed

- Fix step interrupting by Ctrl-C

### Changed

- Disable `SendDMail` tool in Kimi Koder agent

## 0.28 (2025-10-13)

### Added

- Add `/init` meta command to analyze the codebase and generate an `AGENTS.md` file
- Add `/clear` meta command to clear the context

### Fixed

- Fix `ReadFile` output

## 0.27 (2025-10-11)

### Added

- Add `--mcp-config-file` and `--mcp-config` options to load MCP configs

### Changed

- Rename `--agent` option to `--agent-file`

## 0.26 (2025-10-11)

### Fixed

- Fix possible encoding error in `--output-format stream-json` mode

## 0.25 (2025-10-11)

### Changed

- Rename package name `ensoul` to `kimi-cli`
- Rename `ENSOUL_*` builtin system prompt arguments to `KIMI_*`
- Further decouple `App` with `Soul`
- Split `Soul` protocol and `KimiSoul` implementation for better modularity

## 0.24 (2025-10-10)

### Fixed

- Fix ACP `cancel` method

## 0.23 (2025-10-09)

### Added

- Add `extend` field to agent file to support agent file extension
- Add `exclude_tools` field to agent file to support excluding tools
- Add `subagents` field to agent file to support defining subagents

## 0.22 (2025-10-09)

### Changed

- Improve `SearchWeb` and `FetchURL` tool call visualization
- Improve search result output format

## 0.21 (2025-10-09)

### Added

- Add `--print` option as a shortcut for `--ui print`, `--acp` option as a shortcut for `--ui acp`
- Support `--output-format stream-json` to print output in JSON format
- Add `SearchWeb` tool with `services.moonshot_search` configuration. You need to configure it with `"services": {"moonshot_search": {"api_key": "your-search-api-key"}}` in your config file.
- Add `FetchURL` tool
- Add `Think` tool
- Add `PatchFile` tool, not enabled in Kimi Koder agent
- Enable `SendDMail` and `Task` tool in Kimi Koder agent with better tool prompts
- Add `ENSOUL_NOW` builtin system prompt argument

### Changed

- Better-looking `/release-notes`
- Improve tool descriptions
- Improve tool output truncation

## 0.20 (2025-09-30)

### Added

- Add `--ui acp` option to start Agent Client Protocol (ACP) server

## 0.19 (2025-09-29)

### Added

- Support piped stdin for print UI
- Support `--input-format=stream-json` for piped JSON input

### Fixed

- Do not include `CHECKPOINT` messages in the context when `SendDMail` is not enabled

## 0.18 (2025-09-29)

### Added

- Support `max_context_size` in LLM model configurations to configure the maximum context size (in tokens)

### Improved

- Improve `ReadFile` tool description

## 0.17 (2025-09-29)

### Fixed

- Fix step count in error message when exceeded max steps
- Fix history file assertion error in `kimi_run`
- Fix error handling in print mode and single command shell mode
- Add retry for LLM API connection errors and timeout errors

### Changed

- Increase default max-steps-per-run to 100

## 0.16.0 (2025-09-26)

### Tools

- Add `SendDMail` tool (disabled in Kimi Koder, can be enabled in custom agent)

### SDK

- Session history file can be specified via `_history_file` parameter when creating a new session

## 0.15.0 (2025-09-26)

- Improve tool robustness

## 0.14.0 (2025-09-25)

### Added

- Add `StrReplaceFile` tool

### Improved

- Emphasize the use of the same language as the user

## 0.13.0 (2025-09-25)

### Added

- Add `SetTodoList` tool
- Add `User-Agent` in LLM API calls

### Improved

- Better system prompt and tool description
- Better error messages for LLM

## 0.12.0 (2025-09-24)

### Added

- Add `print` UI mode, which can be used via `--ui print` option
- Add logging and `--debug` option

### Changed

- Catch EOF error for better experience

## 0.11.1 (2025-09-22)

### Changed

- Rename `max_retry_per_step` to `max_retries_per_step`

## 0.11.0 (2025-09-22)

### Added

- Add `/release-notes` command
- Add retry for LLM API errors
- Add loop control configuration, e.g. `{"loop_control": {"max_steps_per_run": 50, "max_retry_per_step": 3}}`

### Changed

- Better extreme cases handling in `read_file` tool
- Prevent Ctrl-C from exiting the CLI, force the use of Ctrl-D or `exit` instead

## 0.10.1 (2025-09-18)

- Make slash commands look slightly better
- Improve `glob` tool

## 0.10.0 (2025-09-17)

### Added

- Add `read_file` tool
- Add `write_file` tool
- Add `glob` tool
- Add `task` tool

### Changed

- Improve tool call visualization
- Improve session management
- Restore context usage when `--continue` a session

## 0.9.0 (2025-09-15)

- Remove `--session` and `--continue` options

## 0.8.1 (2025-09-14)

- Fix config model dumping

## 0.8.0 (2025-09-14)

- Add `shell` tool and basic system prompt
- Add tool call visualization
- Add context usage count
- Support interrupting the agent loop
- Support project-level `AGENTS.md`
- Support custom agent defined with YAML
- Support oneshot task via `kimi -c`


================================================
FILE: CONTRIBUTING.md
================================================
# Contributing to Kimi Code CLI

Thank you for being interested in contributing to Kimi Code CLI!

We welcome all kinds of contributions, including bug fixes, features, document improvements, typo fixes, etc. To maintain a high-quality codebase and user experience, we provide the following guidelines for contributions:

1. We only merge pull requests that aligns with our roadmap. For any pull request that introduces changes larger than 100 lines of code, we highly recommend discussing with us by [raising an issue](https://github.com/MoonshotAI/kimi-cli/issues) or in an existing issue before you start working on it. Otherwise your pull request may be closed or ignored without review.
2. We insist on high code quality. Please ensure your code is as good as, if not better than, the code written by frontier coding agents. Changes may be requested before your pull request can be merged.

## Prek hooks

We use [prek](https://github.com/j178/prek) to run formatting and checks via git hooks.

Recommended setup:
1. Run `make prepare` to sync dependencies and install the prek hooks.
2. Optionally run on all files before sending a PR: `prek run --all-files`.

Manual setup (if you do not want to use `make prepare`):
1. Install prek (pick one): `uv tool install prek`, `pipx install prek`, or `pip install prek`.
2. Install the hooks in this repo: `prek install`.

After installation, the hooks run on every commit. The repo uses prek workspace mode, so only the
projects with changed files run their hooks. You can skip them for an intermediate commit with
`git commit --no-verify`, or run them manually with `prek run --all-files`.

The hooks execute the relevant `make format-*` and `make check-*` targets, so ensure dependencies
are installed (`make prepare` or `uv sync`).


================================================
FILE: LICENSE
================================================

                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

================================================
FILE: Makefile
================================================
.DEFAULT_GOAL := prepare

.PHONY: help
help: ## Show available make targets.
	@echo "Available make targets:"
	@awk 'BEGIN { FS = ":.*## " } /^[A-Za-z0-9_.-]+:.*## / { printf "  %-20s %s\n", $$1, $$2 }' $(MAKEFILE_LIST)

.PHONY: install-prek
install-prek: ## Install prek and repo git hooks.
	@echo "==> Installing prek"
	@uv tool install prek
	@echo "==> Installing git hooks with prek"
	@uv tool run prek install

.PHONY: prepare
prepare: download-deps install-prek ## Sync dependencies for all workspace packages and install prek hooks.
	@echo "==> Syncing dependencies for all workspace packages"
	@uv sync --frozen --all-extras --all-packages

.PHONY: prepare-build
prepare-build: download-deps ## Sync dependencies for releases without workspace sources.
	@echo "==> Syncing dependencies for release builds (no sources)"
	@uv sync --all-extras --all-packages --no-sources

# for kimi web development
.PHONY: web-back web-front
web-back: ## Start web backend with uvicorn (reload enabled).
	@LOG_LEVEL=DEBUG uv run uvicorn kimi_cli.web.app:create_app --factory --reload --port 5494
web-front: ## Start web frontend (vite dev server).
	@npm --prefix web run dev

# for kimi vis development
.PHONY: vis-back vis-front
vis-back: ## Start vis backend with uvicorn (reload enabled).
	@LOG_LEVEL=DEBUG uv run uvicorn kimi_cli.vis.app:create_app --factory --reload --port 5495
vis-front: ## Start vis frontend (vite dev server).
	@npm --prefix vis run dev

.PHONY: format format-kimi-cli format-kosong format-pykaos format-kimi-sdk format-web
format: format-kimi-cli format-kosong format-pykaos format-kimi-sdk format-web ## Auto-format all workspace packages.
format-kimi-cli: ## Auto-format Kimi Code CLI sources with ruff.
	@echo "==> Formatting Kimi Code CLI sources"
	@uv run ruff check --fix
	@uv run ruff format
format-kosong: ## Auto-format kosong sources with ruff.
	@echo "==> Formatting kosong sources"
	@uv run --project packages/kosong --directory packages/kosong ruff check --fix
	@uv run --project packages/kosong --directory packages/kosong ruff format
format-pykaos: ## Auto-format pykaos sources with ruff.
	@echo "==> Formatting pykaos sources"
	@uv run --project packages/kaos --directory packages/kaos ruff check --fix
	@uv run --project packages/kaos --directory packages/kaos ruff format
format-kimi-sdk: ## Auto-format kimi-sdk sources with ruff.
	@echo "==> Formatting kimi-sdk sources"
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk ruff check --fix
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk ruff format
format-web: ## Auto-format web sources with npm run format.
	@echo "==> Formatting web sources"
	@if command -v npm >/dev/null 2>&1; then \
		npm --prefix web run format; \
	else \
		echo "npm not found. Install Node.js (npm) to run web formatting."; \
		exit 1; \
	fi
.PHONY: check check-kimi-cli check-kosong check-pykaos check-kimi-sdk check-web
check: check-kimi-cli check-kosong check-pykaos check-kimi-sdk check-web ## Run linting and type checks for all packages.
check-kimi-cli: ## Run linting and type checks for Kimi Code CLI.
	@echo "==> Checking Kimi Code CLI (ruff + pyright + ty; ty is non-blocking)"
	@uv run ruff check
	@uv run ruff format --check
	@uv run pyright
	@uv run ty check || true
check-kosong: ## Run linting and type checks for kosong.
	@echo "==> Checking kosong (ruff + pyright + ty; ty is non-blocking)"
	@uv run --project packages/kosong --directory packages/kosong ruff check
	@uv run --project packages/kosong --directory packages/kosong ruff format --check
	@uv run --project packages/kosong --directory packages/kosong pyright
	@uv run --project packages/kosong --directory packages/kosong ty check || true
check-pykaos: ## Run linting and type checks for pykaos.
	@echo "==> Checking pykaos (ruff + pyright + ty; ty is non-blocking)"
	@uv run --project packages/kaos --directory packages/kaos ruff check
	@uv run --project packages/kaos --directory packages/kaos ruff format --check
	@uv run --project packages/kaos --directory packages/kaos pyright
	@uv run --project packages/kaos --directory packages/kaos ty check || true
check-kimi-sdk: ## Run linting and type checks for kimi-sdk.
	@echo "==> Checking kimi-sdk (ruff + pyright + ty; ty is non-blocking)"
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk ruff check
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk ruff format --check
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk pyright
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk ty check || true
check-web: ## Run linting and type checks for web.
	@echo "==> Checking web (biome + tsc)"
	@if command -v npm >/dev/null 2>&1; then \
		npm --prefix web run lint && npm --prefix web run typecheck; \
	else \
		echo "npm not found. Install Node.js (npm) to run web checks."; \
		exit 1; \
	fi
.PHONY: test test-kimi-cli test-kosong test-pykaos test-kimi-sdk
test: test-kimi-cli test-kosong test-pykaos test-kimi-sdk ## Run all test suites.
test-kimi-cli: ## Run Kimi Code CLI tests.
	@echo "==> Running Kimi Code CLI tests"
	@uv run pytest tests -vv
	@uv run pytest tests_e2e -vv
test-kosong: ## Run kosong tests (including doctests).
	@echo "==> Running kosong tests"
	@uv run --project packages/kosong --directory packages/kosong pytest --doctest-modules -vv
test-pykaos: ## Run pykaos tests.
	@echo "==> Running pykaos tests"
	@uv run --project packages/kaos --directory packages/kaos pytest tests -vv
test-kimi-sdk: ## Run kimi-sdk tests.
	@echo "==> Running kimi-sdk tests"
	@uv run --project sdks/kimi-sdk --directory sdks/kimi-sdk pytest tests -vv
.PHONY: build build-kimi-cli build-kosong build-pykaos build-kimi-sdk build-bin build-bin-onedir
build: build-web build-vis build-kimi-cli build-kosong build-pykaos build-kimi-sdk ## Build Python packages for release.
build-kimi-cli: build-web build-vis ## Build the kimi-cli and kimi-code sdists and wheels.
	@echo "==> Building kimi-cli distributions"
	@uv build --package kimi-cli --no-sources --out-dir dist
	@echo "==> Building kimi-code distributions"
	@uv build --package kimi-code --no-sources --out-dir dist
build-kosong: ## Build the kosong sdist and wheel.
	@echo "==> Building kosong distributions"
	@uv build --package kosong --no-sources --out-dir dist/kosong
build-pykaos: ## Build the pykaos sdist and wheel.
	@echo "==> Building pykaos distributions"
	@uv build --package pykaos --no-sources --out-dir dist/pykaos
build-kimi-sdk: ## Build the kimi-sdk sdist and wheel.
	@echo "==> Building kimi-sdk distributions"
	@uv build --package kimi-sdk --no-sources --out-dir dist/kimi-sdk
build-web: ## Build web UI and sync into kimi-cli package.
	@echo "==> Building web UI"
	@uv run scripts/build_web.py
build-vis: ## Build vis UI and sync into kimi-cli package.
	@echo "==> Building vis UI"
	@uv run scripts/build_vis.py
build-bin: build-web build-vis ## Build the standalone executable with PyInstaller (one-file mode).
	@echo "==> Building PyInstaller binary (one-file)"
	@uv run pyinstaller kimi.spec
	@mkdir -p dist/onefile
	@if [ -f dist/kimi.exe ]; then mv dist/kimi.exe dist/onefile/; elif [ -f dist/kimi ]; then mv dist/kimi dist/onefile/; fi
build-bin-onedir: build-web build-vis ## Build the standalone executable with PyInstaller (one-dir mode).
	@echo "==> Building PyInstaller binary (one-dir)"
	@rm -rf dist/onedir dist/kimi
	@uv run pyinstaller kimi.spec
	@if [ -f dist/kimi/kimi-exe.exe ]; then mv dist/kimi/kimi-exe.exe dist/kimi/kimi.exe; elif [ -f dist/kimi/kimi-exe ]; then mv dist/kimi/kimi-exe dist/kimi/kimi; fi
	@mkdir -p dist/onedir && mv dist/kimi dist/onedir/
.PHONY: ai-test
ai-test: ## Run the test suite with Kimi Code CLI.
	@echo "==> Running AI test suite"
	@uv run tests_ai/scripts/run.py tests_ai

.PHONY: gen-changelog gen-docs
gen-changelog: ## Generate changelog with Kimi Code CLI.
	@echo "==> Generating changelog"
	@uv run kimi --yolo --prompt /skill:gen-changelog
gen-docs: ## Generate user docs with Kimi Code CLI.
	@echo "==> Generating user docs"
	@uv run kimi --yolo --prompt /skill:gen-docs

include src/kimi_cli/deps/Makefile


================================================
FILE: NOTICE
================================================
Kimi Code CLI
Copyright 2025 Moonshot AI

This product includes software developed at
Moonshot AI (https://www.moonshot.ai/).

The Kimi Code CLI project (formerly Kimi CLI) contains or reuses code
that is licensed under the Apache 2.0 license from the following projects:
- OpenAI Codex (https://github.com/openai/codex)

  See: src/kimi_cli/skills/skill-creator/SKILL.md

OpenAI Codex
Copyright 2025 OpenAI


================================================
FILE: README.md
================================================
# Kimi Code CLI

[![Commit Activity](https://img.shields.io/github/commit-activity/w/MoonshotAI/kimi-cli)](https://github.com/MoonshotAI/kimi-cli/graphs/commit-activity)
[![Checks](https://img.shields.io/github/check-runs/MoonshotAI/kimi-cli/main)](https://github.com/MoonshotAI/kimi-cli/actions)
[![Version](https://img.shields.io/pypi/v/kimi-cli)](https://pypi.org/project/kimi-cli/)
[![Downloads](https://img.shields.io/pypi/dw/kimi-cli)](https://pypistats.org/packages/kimi-cli)
[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/MoonshotAI/kimi-cli)

[Kimi Code](https://www.kimi.com/code/) | [Documentation](https://moonshotai.github.io/kimi-cli/en/) | [文档](https://moonshotai.github.io/kimi-cli/zh/)

Kimi Code CLI is an AI agent that runs in the terminal, helping you complete software development tasks and terminal operations. It can read and edit code, execute shell commands, search and fetch web pages, and autonomously plan and adjust actions during execution.

## Getting Started

See [Getting Started](https://moonshotai.github.io/kimi-cli/en/guides/getting-started.html) for how to install and start using Kimi Code CLI.

## Key Features

### Shell command mode

Kimi Code CLI is not only a coding agent, but also a shell. You can switch the shell command mode by pressing `Ctrl-X`. In this mode, you can directly run shell commands without leaving Kimi Code CLI.

![](./docs/media/shell-mode.gif)

> [!NOTE]
> Built-in shell commands like `cd` are not supported yet.

### VS Code extension

Kimi Code CLI can be integrated with [Visual Studio Code](https://code.visualstudio.com/) via the [Kimi Code VS Code Extension](https://marketplace.visualstudio.com/items?itemName=moonshot-ai.kimi-code).

![VS Code Extension](./docs/media/vscode.png)

### IDE integration via ACP

Kimi Code CLI supports [Agent Client Protocol] out of the box. You can use it together with any ACP-compatible editor or IDE.

[Agent Client Protocol]: https://github.com/agentclientprotocol/agent-client-protocol

To use Kimi Code CLI with ACP clients, make sure to run Kimi Code CLI in the terminal and send `/login` to complete the login first. Then, you can configure your ACP client to start Kimi Code CLI as an ACP agent server with command `kimi acp`.

For example, to use Kimi Code CLI with [Zed](https://zed.dev/) or [JetBrains](https://blog.jetbrains.com/ai/2025/12/bring-your-own-ai-agent-to-jetbrains-ides/), add the following configuration to your `~/.config/zed/settings.json` or `~/.jetbrains/acp.json` file:

```json
{
  "agent_servers": {
    "Kimi Code CLI": {
      "command": "kimi",
      "args": ["acp"],
      "env": {}
    }
  }
}
```

Then you can create Kimi Code CLI threads in IDE's agent panel.

![](./docs/media/acp-integration.gif)

### Zsh integration

You can use Kimi Code CLI together with Zsh, to empower your shell experience with AI agent capabilities.

Install the [zsh-kimi-cli](https://github.com/MoonshotAI/zsh-kimi-cli) plugin via:

```sh
git clone https://github.com/MoonshotAI/zsh-kimi-cli.git \
  ${ZSH_CUSTOM:-~/.oh-my-zsh/custom}/plugins/kimi-cli
```

> [!NOTE]
> If you are using a plugin manager other than Oh My Zsh, you may need to refer to the plugin's README for installation instructions.

Then add `kimi-cli` to your Zsh plugin list in `~/.zshrc`:

```sh
plugins=(... kimi-cli)
```

After restarting Zsh, you can switch to agent mode by pressing `Ctrl-X`.

### MCP support

Kimi Code CLI supports MCP (Model Context Protocol) tools.

**`kimi mcp` sub-command group**

You can manage MCP servers with `kimi mcp` sub-command group. For example:

```sh
# Add streamable HTTP server:
kimi mcp add --transport http context7 https://mcp.context7.com/mcp --header "CONTEXT7_API_KEY: ctx7sk-your-key"

# Add streamable HTTP server with OAuth authorization:
kimi mcp add --transport http --auth oauth linear https://mcp.linear.app/mcp

# Add stdio server:
kimi mcp add --transport stdio chrome-devtools -- npx chrome-devtools-mcp@latest

# List added MCP servers:
kimi mcp list

# Remove an MCP server:
kimi mcp remove chrome-devtools

# Authorize an MCP server:
kimi mcp auth linear
```

**Ad-hoc MCP configuration**

Kimi Code CLI also supports ad-hoc MCP server configuration via CLI option.

Given an MCP config file in the well-known MCP config format like the following:

```json
{
  "mcpServers": {
    "context7": {
      "url": "https://mcp.context7.com/mcp",
      "headers": {
        "CONTEXT7_API_KEY": "YOUR_API_KEY"
      }
    },
    "chrome-devtools": {
      "command": "npx",
      "args": ["-y", "chrome-devtools-mcp@latest"]
    }
  }
}
```

Run `kimi` with `--mcp-config-file` option to connect to the specified MCP servers:

```sh
kimi --mcp-config-file /path/to/mcp.json
```

### More

See more features in the [Documentation](https://moonshotai.github.io/kimi-cli/en/).

## Development

To develop Kimi Code CLI, run:

```sh
git clone https://github.com/MoonshotAI/kimi-cli.git
cd kimi-cli

make prepare  # prepare the development environment
```

Then you can start working on Kimi Code CLI.

Refer to the following commands after you make changes:

```sh
uv run kimi  # run Kimi Code CLI

make format  # format code
make check  # run linting and type checking
make test  # run tests
make test-kimi-cli  # run Kimi Code CLI tests only
make test-kosong  # run kosong tests only
make test-pykaos  # run pykaos tests only
make build-web  # build the web UI and sync it into the package (requires Node.js/npm)
make build  # build python packages
make build-bin  # build standalone binary
make help  # show all make targets
```

Note: `make build` and `make build-bin` automatically run `make build-web` to embed the web UI.


================================================
FILE: SECURITY.md
================================================
# Security Policy

## Supported Versions

Currently, Kimi CLI only provides security support for the latest version.

## Reporting a Vulnerability

Please report a vulnerability via the [MoonshotAI/kimi-cli - Security](https://github.com/MoonshotAI/kimi-cli/security) page, or open an [issue](https://github.com/MoonshotAI/kimi-cli/issues) if it can be published publicly.


================================================
FILE: docs/.gitignore
================================================
node_modules/
.vitepress/cache/
.vitepress/dist/


================================================
FILE: docs/.pre-commit-config.yaml
================================================
orphan: true

# Docs changes do not need pre-commit hooks.
repos: []


================================================
FILE: docs/.vitepress/config.ts
================================================
import { defineConfig } from 'vitepress'
import { withMermaid } from 'vitepress-plugin-mermaid'
import llmstxt from 'vitepress-plugin-llms'

const rawBase = process.env.VITEPRESS_BASE
const base = rawBase
  ? rawBase.startsWith('/')
    ? rawBase.endsWith('/') ? rawBase : `${rawBase}/`
    : `/${rawBase}/`
  : '/'

export default withMermaid(defineConfig({
  base,
  title: 'Kimi Code CLI Docs',
  description: 'Kimi Code CLI Documentation',

  locales: {
    zh: {
      label: '简体中文',
      lang: 'zh-CN',
      link: '/zh/',
      title: 'Kimi Code CLI 文档',
      description: 'Kimi Code CLI 用户文档',
      themeConfig: {
        nav: [
          { text: '指南', link: '/zh/guides/getting-started', activeMatch: '/zh/guides/' },
          { text: '定制化', link: '/zh/customization/mcp', activeMatch: '/zh/customization/' },
          { text: '配置', link: '/zh/configuration/config-files', activeMatch: '/zh/configuration/' },
          { text: '参考手册', link: '/zh/reference/kimi-command', activeMatch: '/zh/reference/' },
          { text: '常见问题', link: '/zh/faq' },
          { text: '发布说明', link: '/zh/release-notes/changelog', activeMatch: '/zh/release-notes/' },
        ],
        sidebar: {
          '/zh/guides/': [
            {
              text: '指南',
              items: [
                { text: '开始使用', link: '/zh/guides/getting-started' },
                { text: '常见使用案例', link: '/zh/guides/use-cases' },
                { text: '交互与输入', link: '/zh/guides/interaction' },
                { text: '会话与上下文', link: '/zh/guides/sessions' },
                { text: '在 IDE 中使用', link: '/zh/guides/ides' },
                { text: '集成到工具', link: '/zh/guides/integrations' },
              ],
            },
          ],
          '/zh/customization/': [
            {
              text: '定制化',
              items: [
                { text: 'Model Context Protocol', link: '/zh/customization/mcp' },
                { text: 'Agent Skills', link: '/zh/customization/skills' },
                { text: 'Agent 与子 Agent', link: '/zh/customization/agents' },
                { text: 'Print 模式', link: '/zh/customization/print-mode' },
                { text: 'Wire 模式', link: '/zh/customization/wire-mode' },
              ],
            },
          ],
          '/zh/configuration/': [
            {
              text: '配置',
              items: [
                { text: '配置文件', link: '/zh/configuration/config-files' },
                { text: '平台与模型', link: '/zh/configuration/providers' },
                { text: '配置覆盖', link: '/zh/configuration/overrides' },
                { text: '环境变量', link: '/zh/configuration/env-vars' },
                { text: '数据路径', link: '/zh/configuration/data-locations' },
              ],
            },
          ],
          '/zh/reference/': [
            {
              text: '参考手册',
              items: [
                { text: 'kimi 命令', link: '/zh/reference/kimi-command' },
                { text: 'kimi info 子命令', link: '/zh/reference/kimi-info' },
                { text: 'kimi acp 子命令', link: '/zh/reference/kimi-acp' },
                { text: 'kimi mcp 子命令', link: '/zh/reference/kimi-mcp' },
                { text: 'kimi term 子命令', link: '/zh/reference/kimi-term' },
                { text: 'kimi vis 子命令', link: '/zh/reference/kimi-vis' },
                { text: 'kimi web 子命令', link: '/zh/reference/kimi-web' },
                { text: '斜杠命令', link: '/zh/reference/slash-commands' },
                { text: '键盘快捷键', link: '/zh/reference/keyboard' },
              ],
            },
          ],
          '/zh/release-notes/': [
            {
              text: '发布说明',
              items: [
                { text: '变更记录', link: '/zh/release-notes/changelog' },
                { text: '破坏性变更与迁移说明', link: '/zh/release-notes/breaking-changes' },
              ],
            },
          ],
        },
      },
    },
    en: {
      label: 'English',
      lang: 'en-US',
      link: '/en/',
      title: 'Kimi Code CLI Docs',
      description: 'Kimi Code CLI User Documentation',
      themeConfig: {
        nav: [
          { text: 'Guides', link: '/en/guides/getting-started', activeMatch: '/en/guides/' },
          { text: 'Customization', link: '/en/customization/mcp', activeMatch: '/en/customization/' },
          { text: 'Configuration', link: '/en/configuration/config-files', activeMatch: '/en/configuration/' },
          { text: 'Reference', link: '/en/reference/kimi-command', activeMatch: '/en/reference/' },
          { text: 'FAQ', link: '/en/faq' },
          { text: 'Release Notes', link: '/en/release-notes/changelog', activeMatch: '/en/release-notes/' },
        ],
        sidebar: {
          '/en/guides/': [
            {
              text: 'Guides',
              items: [
                { text: 'Getting Started', link: '/en/guides/getting-started' },
                { text: 'Common Use Cases', link: '/en/guides/use-cases' },
                { text: 'Interaction and Input', link: '/en/guides/interaction' },
                { text: 'Sessions and Context', link: '/en/guides/sessions' },
                { text: 'Using in IDEs', link: '/en/guides/ides' },
                { text: 'Integrations with Tools', link: '/en/guides/integrations' },
              ],
            },
          ],
          '/en/customization/': [
            {
              text: 'Customization',
              items: [
                { text: 'Model Context Protocol', link: '/en/customization/mcp' },
                { text: 'Agent Skills', link: '/en/customization/skills' },
                { text: 'Agents and Subagents', link: '/en/customization/agents' },
                { text: 'Print Mode', link: '/en/customization/print-mode' },
                { text: 'Wire Mode', link: '/en/customization/wire-mode' },
              ],
            },
          ],
          '/en/configuration/': [
            {
              text: 'Configuration',
              items: [
                { text: 'Config Files', link: '/en/configuration/config-files' },
                { text: 'Providers and Models', link: '/en/configuration/providers' },
                { text: 'Config Overrides', link: '/en/configuration/overrides' },
                { text: 'Environment Variables', link: '/en/configuration/env-vars' },
                { text: 'Data Locations', link: '/en/configuration/data-locations' },
              ],
            },
          ],
          '/en/reference/': [
            {
              text: 'Reference',
              items: [
                { text: 'kimi Command', link: '/en/reference/kimi-command' },
                { text: 'kimi info Subcommand', link: '/en/reference/kimi-info' },
                { text: 'kimi acp Subcommand', link: '/en/reference/kimi-acp' },
                { text: 'kimi mcp Subcommand', link: '/en/reference/kimi-mcp' },
                { text: 'kimi term Subcommand', link: '/en/reference/kimi-term' },
                { text: 'kimi vis Subcommand', link: '/en/reference/kimi-vis' },
                { text: 'kimi web Subcommand', link: '/en/reference/kimi-web' },
                { text: 'Slash Commands', link: '/en/reference/slash-commands' },
                { text: 'Keyboard Shortcuts', link: '/en/reference/keyboard' },
              ],
            },
          ],
          '/en/release-notes/': [
            {
              text: 'Release Notes',
              items: [
                { text: 'Changelog', link: '/en/release-notes/changelog' },
                { text: 'Breaking Changes and Migration', link: '/en/release-notes/breaking-changes' },
              ],
            },
          ],
        },
      },
    },
  },

  themeConfig: {
    outline: [2, 3],
    search: { provider: 'local' },
    socialLinks: [
      { icon: 'github', link: 'https://github.com/MoonshotAI/kimi-cli' },
    ],
  },

  vite: {
    plugins: [llmstxt()],
  },
}))


================================================
FILE: docs/.vitepress/theme/index.ts
================================================
import DefaultTheme from 'vitepress/theme'
import './style.css'

export default DefaultTheme


================================================
FILE: docs/.vitepress/theme/style.css
================================================
:root {
  --vp-c-brand-1: rgb(52, 118, 246);
  --vp-c-brand-2: rgb(72, 138, 255);
  --vp-c-brand-3: rgb(92, 158, 255);
  --vp-c-brand-soft: rgba(52, 118, 246, 0.14);
}

.dark {
  --vp-c-brand-1: rgb(72, 138, 255);
  --vp-c-brand-2: rgb(52, 118, 246);
  --vp-c-brand-3: rgb(92, 158, 255);
  --vp-c-brand-soft: rgba(52, 118, 246, 0.16);
}


================================================
FILE: docs/AGENTS.md
================================================
# Documentation Agent Guide

This repository uses VitePress for the documentation site. The current docs are structural scaffolds only; everything beyond the headings is placeholder guidance. The `Reference Code` blocks are there to guide future writing and should be removed once the docs are complete.

## Structure

- Locales live under `docs/en/` and `docs/zh/` with mirrored paths and filenames.
- Main sections (nav + sidebar) are:
  - Guides: getting-started, use-cases, interaction, sessions, ides, integrations
  - Customization: mcp, skills, agents, print-mode, wire-mode
  - Configuration: config-files, providers, overrides, env-vars, data-locations
  - Reference: kimi-command, kimi-acp, kimi-mcp, slash-commands, keyboard, tools, exit-codes
  - FAQ: setup, interaction, acp, mcp, print-wire, updates
  - Release notes: changelog, breaking-changes
- Navigation and sidebar are defined in `docs/.vitepress/config.ts`. Any new or renamed page must be wired there for both locales.

## Source of truth

- **Changelog page**: The English version (`docs/en/release-notes/changelog.md`) is the source of truth. It is auto-synced from the root `CHANGELOG.md` via script. The Chinese changelog should be translated from the English version.
- **All other pages**: The Chinese version (`docs/zh/`) is the source of truth. English translations should be based on the Chinese docs.

Only the Chinese documentation and the English changelog are manually reviewed. Other translations (e.g., English versions of non-changelog pages) may be auto-generated by AI agents.

## Authoring workflow

- Each page is a scaffold: expand the bullets into prose while keeping the section ordering, and keep the `::: info Reference Code` blocks aligned with the relevant section.
- For changelog: edit the root `CHANGELOG.md`, then run `npm run sync` to update the English docs.
- For other pages: edit the Chinese version first, then translate to English.

## Naming conventions

- Filenames are kebab-case and mirror across locales (same slug in `docs/en/` and `docs/zh/`).
- Use consistent section labels that match the sidebar titles.
- Use backticks for flags, commands, subcommands, command arguments, file paths, code identifiers, type names, field names, field values, and keyboard shortcuts.

## Wording conventions

- Do not change H1 titles or nav/sidebar labels.
- English H2+ headings use sentence case (only the first word capitalized unless it is a proper noun). Treat "Wire" as a proper noun; do not treat "agent", "shell mode", or "print mode" as proper nouns.
- Chinese H2+ headings keep English words in sentence case; preserve proper nouns listed in the term table below.
- Use `API key` in English and `API 密钥` in Chinese; keep `JSON`, `JSONL`, `OAuth`, `macOS`, and `uv` as-is.
- Use straight double quotes with spaces for quoted content: `"被引内容"` (not curly quotes). Add a space before and after the quoted text when adjacent to CJK characters. Use corner brackets `「」` for special terms (e.g., `「工具」`, `「会话」`).
- Prefer "终端" over "命令行" in Chinese when both are applicable (e.g., "运行在终端中", "终端界面", "终端操作").
- Use "工具调用" / "tool call", not "工具使用" / "tool use".
- Use inline code for tool names (e.g., `Task`, `ReadFile`, `Shell`).

Term mapping (Chinese <-> English, and proper noun handling):

| Chinese | English | Proper noun (zh) | Proper noun (en) |
| --- | --- | --- | --- |
| Agent | agent | yes | no |
| Shell | shell | yes | no |
| Shell 模式 | shell mode | yes | no |
| Print 模式 | print mode | yes | no |
| Wire 模式 | Wire mode | yes | yes (Wire) |
| Thinking 模式 | thinking mode | yes | no |
| MCP | MCP | yes | yes |
| ACP | ACP | yes | yes |
| Kimi Code CLI | Kimi Code CLI | yes | yes |
| Agent Skills | Agent Skills | yes | yes |
| Skill | skill | yes | no |
| 系统提示词 | system prompt | no | no |
| 提示词 | prompt | no | no |
| 会话 | session | no | no |
| 上下文 | context | no | no |
| 子 Agent | subagent | yes (Agent) | no |
| API 密钥 | API key | yes | no |
| JSON | JSON | yes | yes |
| JSONL | JSONL | yes | yes |
| OAuth | OAuth | yes | yes |
| macOS | macOS | yes | yes |
| uv | uv | yes | yes |
| 审批请求 | approval request | no | no |
| 斜杠命令 | slash command | no | no |
| 工具调用 | tool call | no | no |
| Frontmatter | frontmatter | yes | no |
| User 消息 | user message | yes (User) | no |
| Assistant 消息 | assistant message | yes (Assistant) | no |
| Tool 消息 | tool message | yes (Tool) | no |
| 轮次 | turn | no | no |
| 供应商 | provider | no | no |
| Prompt Flow | Prompt Flow | yes | yes |
| Ralph 循环 | Ralph Loop | yes | yes |
| Diff | diff | yes | no |

JetBrains IDE terminology (Chinese UI translations):

| English | Chinese |
| --- | --- |
| AI Chat | AI 聊天 |
| Registry | 注册表 |
| Configure ACP agents | (未翻译) |

## Typography

- **Spacing around mixed content**: Add a space between Chinese characters and English words, numbers, inline code, or links. Exception: no space before full-width punctuation.
  - ✓ 在 Python 中使用 \`class\` 关键字
  - ✗ 在Python中使用\`class\`关键字
  - ✓ 详见 \[配置文件\](./config.md)。
  - ✗ 详见\[配置文件\](./config.md)。
- **Full-width punctuation**: Use full-width punctuation in Chinese text: `,。;:?!()` not `, . ; : ? ! ( )`.
- **Code block language**: Always specify language for fenced code blocks (e.g., ` ```sh `, ` ```toml `, ` ```json `). Exception: natural language examples (user prompts) may omit the language.
- **Callout titles**: Use short category titles for callout blocks (`::: tip`, `::: warning`, `::: info`, `::: danger`). Put the detailed description in the block content, not the title.
  - Chinese: use `提示` for tip, `注意` for warning, `说明` for info, `警告` for danger.
  - English: use no title or short words like `Note` for warning.
  - ✓ `::: tip 提示` + content starting with the key point
  - ✓ `::: warning 注意` + content `\`KIMI_SHARE_DIR\` 不影响 Skills 的搜索路径。...`
  - ✗ `::: warning 不影响 Skills` (title too long, should be in content)
  - ✗ `::: tip Skills 路径独立于 KIMI_SHARE_DIR` (title too long)
- **Version info blocks**: For version change callouts, use `::: info` with a category title (Added/Changed/Removed in English; 新增/变更/移除 in Chinese). The content should be a complete sentence.
  - ✓ `::: info 新增` + content `新增于 Wire 1.2。`
  - ✗ `::: info 新增于 Wire 1.2` (title too long)
  - ✓ `::: info Changed` + content `Renamed in Wire 1.1. ...`
  - ✗ `::: info Renamed in Wire 1.1` (title too long)

## Writing style

- **Natural narrative**: Organize content like writing an article, guiding readers smoothly through the material.
- **Avoid fragmentation**: Don't turn every point into a subheading; use paragraph transitions instead.
- **Global perspective**: "Getting Started" introduces core concepts only; detailed usage belongs in later pages.
- **Progressive depth**: Guides → Customization → Configuration → Reference, information deepens gradually.
- **No "next steps"**: VitePress already provides prev/next navigation; don't add manual `::: tip 接下来` blocks at page end.

### Example: good vs bad

Outline prompt:

```
* Install and upgrade
  * System requirements: Python 3.12+, recommend uv
  * Install, upgrade, uninstall steps
```

**Bad** (mechanical conversion to headings):

```markdown
## Install and upgrade

### System requirements

- Python 3.12+
- Recommend uv

### Install

...

### Upgrade

...
```

**Good** (natural narrative):

```markdown
## Install and upgrade

Kimi Code CLI requires Python 3.12+. We recommend using uv for installation and management.

If you haven't installed uv yet, please refer to the uv installation docs first. Install Kimi Code CLI:

(code block)

Verify the installation:

(code block)

Upgrade to the latest version:

(code block)
```

## Build and preview

- Docs are built with VitePress from `docs/`.
- Common commands (run inside `docs/`):
  - `npm install` (or `bun install` if you use bun)
  - `npm run dev`
  - `npm run build`
  - `npm run preview`
- The build output is `docs/.vitepress/dist`.

## Changelog syncing

The English changelog (`docs/en/release-notes/changelog.md`) is auto-generated from the root `CHANGELOG.md`. Do not edit it manually.

- The sync script is `docs/scripts/sync-changelog.mjs`.
- It runs automatically before `npm run dev` and `npm run build`.
- To run manually: `npm run sync` (from the `docs/` directory).
- The script converts title format (`## [0.69] - 2025-12-29` → `## 0.69 (2025-12-29)`) and removes HTML comments.


================================================
FILE: docs/en/configuration/config-files.md
================================================
# Config Files

Kimi Code CLI uses configuration files to manage API providers, models, services, and runtime parameters, supporting both TOML and JSON formats.

## Config file location

The default configuration file is located at `~/.kimi/config.toml`. On first run, if the configuration file doesn't exist, Kimi Code CLI will automatically create a default configuration file.

You can specify a different configuration file (TOML or JSON format) with the `--config-file` flag:

```sh
kimi --config-file /path/to/config.toml
```

When calling Kimi Code CLI programmatically, you can also pass the complete configuration content directly via the `--config` flag:

```sh
kimi --config '{"default_model": "kimi-for-coding", "providers": {...}, "models": {...}}'
```

## Config items

The configuration file contains the following top-level configuration items:

| Item | Type | Description |
| --- | --- | --- |
| `default_model` | `string` | Default model name, must be a model defined in `models` |
| `default_thinking` | `boolean` | Whether to enable thinking mode by default (defaults to `false`) |
| `default_yolo` | `boolean` | Whether to enable YOLO (auto-approve) mode by default (defaults to `false`) |
| `default_editor` | `string` | Default external editor command (e.g. `"vim"`, `"code --wait"`), auto-detects when empty |
| `providers` | `table` | API provider configuration |
| `models` | `table` | Model configuration |
| `loop_control` | `table` | Agent loop control parameters |
| `background` | `table` | Background task runtime parameters |
| `services` | `table` | External service configuration (search, fetch) |
| `mcp` | `table` | MCP client configuration |

### Complete configuration example

```toml
default_model = "kimi-for-coding"
default_thinking = false
default_yolo = false
default_edi
Download .txt
gitextract_akc1nk5y/

├── .agents/
│   └── skills/
│       ├── codex-worker/
│       │   └── SKILL.md
│       ├── feature-smoke-test/
│       │   ├── SKILL.md
│       │   ├── references/
│       │   │   └── prompt-patterns.md
│       │   └── scripts/
│       │       └── inspect_session.py
│       ├── gen-changelog/
│       │   └── SKILL.md
│       ├── gen-docs/
│       │   └── SKILL.md
│       ├── gen-rust/
│       │   └── SKILL.md
│       ├── pull-request/
│       │   └── SKILL.md
│       ├── release/
│       │   └── SKILL.md
│       ├── translate-docs/
│       │   └── SKILL.md
│       └── worktree-status/
│           └── SKILL.md
├── .github/
│   ├── ISSUE_TEMPLATE/
│   │   ├── 1-bug-report.yml
│   │   ├── 2-feature-request.yml
│   │   └── config.yml
│   ├── actions/
│   │   └── macos-code-sign/
│   │       └── action.yml
│   ├── dependabot.yml
│   ├── pr-title-checker-config.json
│   ├── pull_request_template.md
│   └── workflows/
│       ├── ci-docs.yml
│       ├── ci-kimi-cli.yml
│       ├── ci-kimi-sdk.yml
│       ├── ci-kosong.yml
│       ├── ci-pykaos.yml
│       ├── docs-pages.yml
│       ├── pr-title-checker.yml
│       ├── release-kimi-cli.yml
│       ├── release-kimi-sdk.yml
│       ├── release-kosong.yml
│       ├── release-pykaos.yml
│       ├── translator.yml
│       └── typos.yml
├── .gitignore
├── .pre-commit-config.yaml
├── .python-version
├── AGENTS.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── LICENSE
├── Makefile
├── NOTICE
├── README.md
├── SECURITY.md
├── docs/
│   ├── .gitignore
│   ├── .pre-commit-config.yaml
│   ├── .vitepress/
│   │   ├── config.ts
│   │   └── theme/
│   │       ├── index.ts
│   │       └── style.css
│   ├── AGENTS.md
│   ├── en/
│   │   ├── configuration/
│   │   │   ├── config-files.md
│   │   │   ├── data-locations.md
│   │   │   ├── env-vars.md
│   │   │   ├── overrides.md
│   │   │   └── providers.md
│   │   ├── customization/
│   │   │   ├── agents.md
│   │   │   ├── mcp.md
│   │   │   ├── print-mode.md
│   │   │   ├── skills.md
│   │   │   └── wire-mode.md
│   │   ├── faq.md
│   │   ├── guides/
│   │   │   ├── getting-started.md
│   │   │   ├── ides.md
│   │   │   ├── integrations.md
│   │   │   ├── interaction.md
│   │   │   ├── sessions.md
│   │   │   └── use-cases.md
│   │   ├── index.md
│   │   ├── reference/
│   │   │   ├── keyboard.md
│   │   │   ├── kimi-acp.md
│   │   │   ├── kimi-command.md
│   │   │   ├── kimi-info.md
│   │   │   ├── kimi-mcp.md
│   │   │   ├── kimi-term.md
│   │   │   ├── kimi-vis.md
│   │   │   ├── kimi-web.md
│   │   │   └── slash-commands.md
│   │   └── release-notes/
│   │       ├── breaking-changes.md
│   │       └── changelog.md
│   ├── index.md
│   ├── package.json
│   ├── scripts/
│   │   └── sync-changelog.mjs
│   └── zh/
│       ├── configuration/
│       │   ├── config-files.md
│       │   ├── data-locations.md
│       │   ├── env-vars.md
│       │   ├── overrides.md
│       │   └── providers.md
│       ├── customization/
│       │   ├── agents.md
│       │   ├── mcp.md
│       │   ├── print-mode.md
│       │   ├── skills.md
│       │   └── wire-mode.md
│       ├── faq.md
│       ├── guides/
│       │   ├── getting-started.md
│       │   ├── ides.md
│       │   ├── integrations.md
│       │   ├── interaction.md
│       │   ├── sessions.md
│       │   └── use-cases.md
│       ├── index.md
│       ├── reference/
│       │   ├── keyboard.md
│       │   ├── kimi-acp.md
│       │   ├── kimi-command.md
│       │   ├── kimi-info.md
│       │   ├── kimi-mcp.md
│       │   ├── kimi-term.md
│       │   ├── kimi-vis.md
│       │   ├── kimi-web.md
│       │   └── slash-commands.md
│       └── release-notes/
│           ├── breaking-changes.md
│           └── changelog.md
├── examples/
│   ├── .gitignore
│   ├── custom-echo-soul/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── custom-kimi-soul/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── custom-tools/
│   │   ├── README.md
│   │   ├── main.py
│   │   ├── my_tools/
│   │   │   ├── __init__.py
│   │   │   └── ls.py
│   │   ├── myagent.yaml
│   │   └── pyproject.toml
│   ├── kimi-cli-stream-json/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── kimi-cli-wire-messages/
│   │   ├── README.md
│   │   ├── main.py
│   │   └── pyproject.toml
│   ├── kimi-psql/
│   │   ├── README.md
│   │   ├── agent.yaml
│   │   ├── main.py
│   │   └── pyproject.toml
│   └── sample-plugin/
│       ├── SKILL.md
│       ├── plugin.json
│       └── scripts/
│           ├── calc.ts
│           └── greet.py
├── flake.nix
├── kimi.spec
├── klips/
│   ├── .pre-commit-config.yaml
│   ├── klip-0-klip.md
│   ├── klip-1-kimi-cli-monorepo.md
│   ├── klip-10-agent-flow.md
│   ├── klip-11-kimi-code-rename.md
│   ├── klip-12-wire-initialize-external-tools.md
│   ├── klip-14-kimi-code-oauth-login.md
│   ├── klip-15-kagent-sidecar-integration.md
│   ├── klip-2-acpkaos.md
│   ├── klip-3-kimi-cli-user-docs.md
│   ├── klip-6-setup-auto-refresh-models.md
│   ├── klip-7-kimi-sdk.md
│   ├── klip-8-config-and-skills-layout.md
│   └── klip-9-shell-ui-flicker-mitigation.md
├── packages/
│   ├── kaos/
│   │   ├── .pre-commit-config.yaml
│   │   ├── CHANGELOG.md
│   │   ├── LICENSE
│   │   ├── NOTICE
│   │   ├── README.md
│   │   ├── pyproject.toml
│   │   ├── src/
│   │   │   └── kaos/
│   │   │       ├── __init__.py
│   │   │       ├── _current.py
│   │   │       ├── local.py
│   │   │       ├── path.py
│   │   │       ├── py.typed
│   │   │       └── ssh.py
│   │   └── tests/
│   │       ├── test_kaos_path.py
│   │       ├── test_local_kaos.py
│   │       ├── test_local_kaos_cmd.py
│   │       ├── test_local_kaos_sh.py
│   │       └── test_ssh_kaos.py
│   ├── kimi-code/
│   │   ├── pyproject.toml
│   │   └── src/
│   │       └── kimi_code/
│   │           └── __init__.py
│   └── kosong/
│       ├── .pre-commit-config.yaml
│       ├── CHANGELOG.md
│       ├── LICENSE
│       ├── NOTICE
│       ├── README.md
│       ├── pyproject.toml
│       ├── src/
│       │   └── kosong/
│       │       ├── __init__.py
│       │       ├── __main__.py
│       │       ├── _generate.py
│       │       ├── chat_provider/
│       │       │   ├── __init__.py
│       │       │   ├── chaos.py
│       │       │   ├── echo/
│       │       │   │   ├── __init__.py
│       │       │   │   ├── dsl.py
│       │       │   │   ├── echo.py
│       │       │   │   └── scripted_echo.py
│       │       │   ├── kimi.py
│       │       │   ├── mock.py
│       │       │   └── openai_common.py
│       │       ├── contrib/
│       │       │   ├── __init__.py
│       │       │   ├── chat_provider/
│       │       │   │   ├── __init__.py
│       │       │   │   ├── anthropic.py
│       │       │   │   ├── common.py
│       │       │   │   ├── google_genai.py
│       │       │   │   ├── openai_legacy.py
│       │       │   │   └── openai_responses.py
│       │       │   └── context/
│       │       │       ├── __init__.py
│       │       │       └── linear.py
│       │       ├── message.py
│       │       ├── py.typed
│       │       ├── tooling/
│       │       │   ├── __init__.py
│       │       │   ├── empty.py
│       │       │   ├── error.py
│       │       │   ├── mcp.py
│       │       │   └── simple.py
│       │       └── utils/
│       │           ├── __init__.py
│       │           ├── aio.py
│       │           ├── jsonschema.py
│       │           └── typing.py
│       └── tests/
│           ├── api_snapshot_tests/
│           │   ├── common.py
│           │   ├── test_anthropic.py
│           │   ├── test_google_genai.py
│           │   ├── test_kimi.py
│           │   ├── test_openai_legacy.py
│           │   └── test_openai_responses.py
│           ├── test_chat_provider.py
│           ├── test_context.py
│           ├── test_echo_chat_provider.py
│           ├── test_generate.py
│           ├── test_json_schema_deref.py
│           ├── test_kimi_stream_usage.py
│           ├── test_message.py
│           ├── test_openai_common.py
│           ├── test_scripted_echo_chat_provider.py
│           ├── test_step.py
│           ├── test_tool_call.py
│           └── test_tool_result.py
├── pyproject.toml
├── pytest.ini
├── scripts/
│   ├── build_vis.py
│   ├── build_web.py
│   ├── check_kimi_dependency_versions.py
│   ├── check_version_tag.py
│   ├── cleanup_tmp_sessions.py
│   ├── install.ps1
│   └── install.sh
├── sdks/
│   └── kimi-sdk/
│       ├── CHANGELOG.md
│       ├── LICENSE
│       ├── NOTICE
│       ├── README.md
│       ├── pyproject.toml
│       ├── src/
│       │   └── kimi_sdk/
│       │       ├── __init__.py
│       │       └── py.typed
│       └── tests/
│           └── test_smoke.py
├── src/
│   └── kimi_cli/
│       ├── __init__.py
│       ├── __main__.py
│       ├── acp/
│       │   ├── AGENTS.md
│       │   ├── __init__.py
│       │   ├── convert.py
│       │   ├── kaos.py
│       │   ├── mcp.py
│       │   ├── server.py
│       │   ├── session.py
│       │   ├── tools.py
│       │   ├── types.py
│       │   └── version.py
│       ├── agents/
│       │   ├── default/
│       │   │   ├── agent.yaml
│       │   │   ├── sub.yaml
│       │   │   └── system.md
│       │   └── okabe/
│       │       └── agent.yaml
│       ├── agentspec.py
│       ├── app.py
│       ├── auth/
│       │   ├── __init__.py
│       │   ├── oauth.py
│       │   └── platforms.py
│       ├── background/
│       │   ├── __init__.py
│       │   ├── ids.py
│       │   ├── manager.py
│       │   ├── models.py
│       │   ├── store.py
│       │   ├── summary.py
│       │   └── worker.py
│       ├── cli/
│       │   ├── __init__.py
│       │   ├── __main__.py
│       │   ├── _lazy_group.py
│       │   ├── export.py
│       │   ├── info.py
│       │   ├── mcp.py
│       │   ├── plugin.py
│       │   ├── toad.py
│       │   ├── vis.py
│       │   └── web.py
│       ├── config.py
│       ├── constant.py
│       ├── deps/
│       │   └── Makefile
│       ├── exception.py
│       ├── llm.py
│       ├── metadata.py
│       ├── notifications/
│       │   ├── __init__.py
│       │   ├── llm.py
│       │   ├── manager.py
│       │   ├── models.py
│       │   ├── notifier.py
│       │   ├── store.py
│       │   └── wire.py
│       ├── plugin/
│       │   ├── __init__.py
│       │   ├── manager.py
│       │   └── tool.py
│       ├── prompts/
│       │   ├── __init__.py
│       │   ├── compact.md
│       │   └── init.md
│       ├── py.typed
│       ├── session.py
│       ├── session_state.py
│       ├── share.py
│       ├── skill/
│       │   ├── __init__.py
│       │   └── flow/
│       │       ├── __init__.py
│       │       ├── d2.py
│       │       └── mermaid.py
│       ├── skills/
│       │   ├── kimi-cli-help/
│       │   │   └── SKILL.md
│       │   └── skill-creator/
│       │       └── SKILL.md
│       ├── soul/
│       │   ├── __init__.py
│       │   ├── agent.py
│       │   ├── approval.py
│       │   ├── compaction.py
│       │   ├── context.py
│       │   ├── denwarenji.py
│       │   ├── dynamic_injection.py
│       │   ├── dynamic_injections/
│       │   │   ├── __init__.py
│       │   │   └── plan_mode.py
│       │   ├── kimisoul.py
│       │   ├── message.py
│       │   ├── slash.py
│       │   └── toolset.py
│       ├── tools/
│       │   ├── AGENTS.md
│       │   ├── __init__.py
│       │   ├── ask_user/
│       │   │   ├── __init__.py
│       │   │   └── description.md
│       │   ├── background/
│       │   │   ├── __init__.py
│       │   │   ├── list.md
│       │   │   ├── output.md
│       │   │   └── stop.md
│       │   ├── display.py
│       │   ├── dmail/
│       │   │   ├── __init__.py
│       │   │   └── dmail.md
│       │   ├── file/
│       │   │   ├── __init__.py
│       │   │   ├── glob.md
│       │   │   ├── glob.py
│       │   │   ├── grep.md
│       │   │   ├── grep_local.py
│       │   │   ├── plan_mode.py
│       │   │   ├── read.md
│       │   │   ├── read.py
│       │   │   ├── read_media.md
│       │   │   ├── read_media.py
│       │   │   ├── replace.md
│       │   │   ├── replace.py
│       │   │   ├── utils.py
│       │   │   ├── write.md
│       │   │   └── write.py
│       │   ├── multiagent/
│       │   │   ├── __init__.py
│       │   │   ├── create.md
│       │   │   ├── create.py
│       │   │   ├── task.md
│       │   │   └── task.py
│       │   ├── plan/
│       │   │   ├── __init__.py
│       │   │   ├── description.md
│       │   │   ├── enter.py
│       │   │   ├── enter_description.md
│       │   │   └── heroes.py
│       │   ├── shell/
│       │   │   ├── __init__.py
│       │   │   ├── bash.md
│       │   │   └── powershell.md
│       │   ├── test.py
│       │   ├── think/
│       │   │   ├── __init__.py
│       │   │   └── think.md
│       │   ├── todo/
│       │   │   ├── __init__.py
│       │   │   └── set_todo_list.md
│       │   ├── utils.py
│       │   └── web/
│       │       ├── __init__.py
│       │       ├── fetch.md
│       │       ├── fetch.py
│       │       ├── search.md
│       │       └── search.py
│       ├── ui/
│       │   ├── __init__.py
│       │   ├── acp/
│       │   │   └── __init__.py
│       │   ├── print/
│       │   │   ├── __init__.py
│       │   │   └── visualize.py
│       │   └── shell/
│       │       ├── __init__.py
│       │       ├── console.py
│       │       ├── debug.py
│       │       ├── echo.py
│       │       ├── export_import.py
│       │       ├── keyboard.py
│       │       ├── mcp_status.py
│       │       ├── oauth.py
│       │       ├── placeholders.py
│       │       ├── prompt.py
│       │       ├── replay.py
│       │       ├── setup.py
│       │       ├── slash.py
│       │       ├── startup.py
│       │       ├── task_browser.py
│       │       ├── update.py
│       │       ├── usage.py
│       │       └── visualize.py
│       ├── utils/
│       │   ├── __init__.py
│       │   ├── aiohttp.py
│       │   ├── aioqueue.py
│       │   ├── broadcast.py
│       │   ├── changelog.py
│       │   ├── clipboard.py
│       │   ├── datetime.py
│       │   ├── diff.py
│       │   ├── editor.py
│       │   ├── environment.py
│       │   ├── envvar.py
│       │   ├── export.py
│       │   ├── frontmatter.py
│       │   ├── io.py
│       │   ├── logging.py
│       │   ├── media_tags.py
│       │   ├── message.py
│       │   ├── path.py
│       │   ├── proctitle.py
│       │   ├── pyinstaller.py
│       │   ├── rich/
│       │   │   ├── __init__.py
│       │   │   ├── columns.py
│       │   │   ├── markdown.py
│       │   │   ├── markdown_sample.md
│       │   │   ├── markdown_sample_short.md
│       │   │   └── syntax.py
│       │   ├── signals.py
│       │   ├── slashcmd.py
│       │   ├── string.py
│       │   ├── subprocess_env.py
│       │   ├── term.py
│       │   └── typing.py
│       ├── vis/
│       │   ├── __init__.py
│       │   ├── api/
│       │   │   ├── __init__.py
│       │   │   ├── sessions.py
│       │   │   ├── statistics.py
│       │   │   └── system.py
│       │   └── app.py
│       ├── web/
│       │   ├── __init__.py
│       │   ├── api/
│       │   │   ├── __init__.py
│       │   │   ├── config.py
│       │   │   ├── open_in.py
│       │   │   └── sessions.py
│       │   ├── app.py
│       │   ├── auth.py
│       │   ├── models.py
│       │   ├── runner/
│       │   │   ├── __init__.py
│       │   │   ├── messages.py
│       │   │   ├── process.py
│       │   │   └── worker.py
│       │   └── store/
│       │       ├── __init__.py
│       │       └── sessions.py
│       └── wire/
│           ├── __init__.py
│           ├── file.py
│           ├── jsonrpc.py
│           ├── protocol.py
│           ├── serde.py
│           ├── server.py
│           └── types.py
├── tests/
│   ├── __init__.py
│   ├── acp/
│   │   ├── __init__.py
│   │   ├── conftest.py
│   │   ├── test_protocol_v1.py
│   │   ├── test_session_notifications.py
│   │   └── test_version.py
│   ├── auth/
│   │   └── test_ascii_header.py
│   ├── background/
│   │   ├── test_manager.py
│   │   ├── test_store.py
│   │   └── test_worker.py
│   ├── conftest.py
│   ├── core/
│   │   ├── test_agent_flow.py
│   │   ├── test_agent_spec.py
│   │   ├── test_ask_user_plan_mode.py
│   │   ├── test_config.py
│   │   ├── test_context.py
│   │   ├── test_create_llm.py
│   │   ├── test_default_agent.py
│   │   ├── test_exceptions.py
│   │   ├── test_inspect_plan_edit_target.py
│   │   ├── test_kimisoul_ralph_loop.py
│   │   ├── test_kimisoul_retry_recovery.py
│   │   ├── test_kimisoul_slash_commands.py
│   │   ├── test_kimisoul_steer.py
│   │   ├── test_load_agent.py
│   │   ├── test_load_agents_md.py
│   │   ├── test_normalize_history.py
│   │   ├── test_notifications.py
│   │   ├── test_plan_mode.py
│   │   ├── test_plan_mode_injection_provider.py
│   │   ├── test_plan_mode_reminder.py
│   │   ├── test_plan_slash.py
│   │   ├── test_plugin.py
│   │   ├── test_plugin_manager.py
│   │   ├── test_plugin_tool.py
│   │   ├── test_session.py
│   │   ├── test_session_state.py
│   │   ├── test_shell_mcp_status.py
│   │   ├── test_simple_compaction.py
│   │   ├── test_skill.py
│   │   ├── test_soul_import_command.py
│   │   ├── test_soul_message.py
│   │   ├── test_startup_imports.py
│   │   ├── test_startup_progress.py
│   │   ├── test_status_formatting.py
│   │   ├── test_str_replace_file_plan_mode.py
│   │   ├── test_toolset.py
│   │   ├── test_wire_message.py
│   │   ├── test_wire_plan_mode.py
│   │   ├── test_wire_server_steer.py
│   │   └── test_write_file_plan_mode.py
│   ├── e2e/
│   │   ├── __init__.py
│   │   ├── shell_pty_helpers.py
│   │   ├── test_basic_e2e.py
│   │   ├── test_cli_error_output.py
│   │   ├── test_media_e2e.py
│   │   └── test_shell_pty_e2e.py
│   ├── notifications/
│   │   └── test_notification_manager.py
│   ├── test_additional_dirs_state.py
│   ├── test_attachment_cache.py
│   ├── test_clipboard.py
│   ├── tools/
│   │   ├── test_additional_dirs.py
│   │   ├── test_ask_user.py
│   │   ├── test_background_tools.py
│   │   ├── test_create_subagent.py
│   │   ├── test_extract_key_argument.py
│   │   ├── test_fetch_url.py
│   │   ├── test_glob.py
│   │   ├── test_grep.py
│   │   ├── test_read_file.py
│   │   ├── test_read_media_file.py
│   │   ├── test_read_media_file_desc.py
│   │   ├── test_shell_bash.py
│   │   ├── test_shell_powershell.py
│   │   ├── test_str_replace_file.py
│   │   ├── test_tool_descriptions.py
│   │   ├── test_tool_schemas.py
│   │   └── test_write_file.py
│   ├── ui_and_conv/
│   │   ├── test_acp_convert.py
│   │   ├── test_acp_server_auth.py
│   │   ├── test_export_import.py
│   │   ├── test_file_completer.py
│   │   ├── test_live_view_notifications.py
│   │   ├── test_print_final_only.py
│   │   ├── test_print_notifications.py
│   │   ├── test_prompt_clipboard.py
│   │   ├── test_prompt_external_editor.py
│   │   ├── test_prompt_history.py
│   │   ├── test_prompt_placeholders.py
│   │   ├── test_prompt_tips.py
│   │   ├── test_question_panel.py
│   │   ├── test_replay.py
│   │   ├── test_sanitize_surrogates.py
│   │   ├── test_shell_editor_slash.py
│   │   ├── test_shell_export_import_commands.py
│   │   ├── test_shell_prompt_echo.py
│   │   ├── test_shell_prompt_router.py
│   │   ├── test_shell_run_placeholders.py
│   │   ├── test_shell_slash_commands.py
│   │   ├── test_shell_task_slash.py
│   │   ├── test_slash_completer.py
│   │   ├── test_status_block.py
│   │   ├── test_task_browser.py
│   │   ├── test_tool_call_block.py
│   │   └── test_visualize_running_prompt.py
│   ├── utils/
│   │   ├── test_atomic_json_write.py
│   │   ├── test_broadcast_queue.py
│   │   ├── test_changelog.py
│   │   ├── test_diff_utils.py
│   │   ├── test_editor.py
│   │   ├── test_file_utils.py
│   │   ├── test_frontmatter.py
│   │   ├── test_is_within_workspace.py
│   │   ├── test_list_directory.py
│   │   ├── test_message_utils.py
│   │   ├── test_pyinstaller_utils.py
│   │   ├── test_result_builder.py
│   │   ├── test_rich_markdown.py
│   │   ├── test_slash_command.py
│   │   ├── test_typing_utils.py
│   │   ├── test_utils_environment.py
│   │   └── test_utils_path.py
│   ├── vis/
│   │   └── test_app.py
│   └── web/
│       └── test_open_in.py
├── tests_ai/
│   ├── scripts/
│   │   ├── main.yaml
│   │   ├── run.py
│   │   └── worker.yaml
│   ├── test_cli_loading_time.md
│   ├── test_encoding_error_handling.md
│   └── test_utf8_encoding.md
├── tests_e2e/
│   ├── AGENTS.md
│   ├── __init__.py
│   ├── test_mcp_cli.py
│   ├── test_wire_approvals_tools.py
│   ├── test_wire_config.py
│   ├── test_wire_errors.py
│   ├── test_wire_prompt.py
│   ├── test_wire_protocol.py
│   ├── test_wire_question.py
│   ├── test_wire_real_llm.py
│   ├── test_wire_sessions.py
│   ├── test_wire_skills_mcp.py
│   ├── test_wire_steer.py
│   └── wire_helpers.py
├── vis/
│   ├── components.json
│   ├── index.html
│   ├── package.json
│   ├── src/
│   │   ├── App.tsx
│   │   ├── components/
│   │   │   ├── markdown.tsx
│   │   │   └── ui/
│   │   │       ├── alert-dialog.tsx
│   │   │       ├── select.tsx
│   │   │       └── tooltip.tsx
│   │   ├── features/
│   │   │   ├── context-viewer/
│   │   │   │   ├── assistant-message.tsx
│   │   │   │   ├── context-space-map.tsx
│   │   │   │   ├── context-viewer.tsx
│   │   │   │   ├── tool-call-block.tsx
│   │   │   │   └── user-message.tsx
│   │   │   ├── dual-view/
│   │   │   │   └── dual-view.tsx
│   │   │   ├── session-picker/
│   │   │   │   └── session-picker.tsx
│   │   │   ├── sessions-explorer/
│   │   │   │   ├── explorer-toolbar.tsx
│   │   │   │   ├── project-group.tsx
│   │   │   │   ├── session-card.tsx
│   │   │   │   └── sessions-explorer.tsx
│   │   │   ├── state-viewer/
│   │   │   │   └── state-viewer.tsx
│   │   │   ├── statistics/
│   │   │   │   └── statistics-view.tsx
│   │   │   └── wire-viewer/
│   │   │       ├── decision-path.tsx
│   │   │       ├── integrity-check.tsx
│   │   │       ├── timeline-view.tsx
│   │   │       ├── tool-call-detail.tsx
│   │   │       ├── tool-stats-dashboard.tsx
│   │   │       ├── turn-efficiency.tsx
│   │   │       ├── turn-tree.tsx
│   │   │       ├── usage-chart.tsx
│   │   │       ├── wire-event-card.tsx
│   │   │       ├── wire-filters.tsx
│   │   │       └── wire-viewer.tsx
│   │   ├── hooks/
│   │   │   └── use-theme.ts
│   │   ├── index.css
│   │   ├── lib/
│   │   │   ├── api.ts
│   │   │   ├── cache.ts
│   │   │   └── utils.ts
│   │   └── main.tsx
│   ├── tsconfig.app.json
│   ├── tsconfig.json
│   ├── tsconfig.node.json
│   └── vite.config.ts
└── web/
    ├── .gitignore
    ├── biome.jsonc
    ├── components.json
    ├── index.html
    ├── openapi.json
    ├── openapitools.json
    ├── package.json
    ├── scripts/
    │   └── generate-api.sh
    ├── src/
    │   ├── App.tsx
    │   ├── bootstrap.tsx
    │   ├── components/
    │   │   ├── ai-elements/
    │   │   │   ├── chain-of-thought.tsx
    │   │   │   ├── code-block.tsx
    │   │   │   ├── confirmation.tsx
    │   │   │   ├── context.tsx
    │   │   │   ├── conversation.tsx
    │   │   │   ├── index.ts
    │   │   │   ├── loader.tsx
    │   │   │   ├── message.tsx
    │   │   │   ├── model-selector.tsx
    │   │   │   ├── prompt-input.tsx
    │   │   │   ├── reasoning.tsx
    │   │   │   ├── shimmer.tsx
    │   │   │   ├── streamdown.tsx
    │   │   │   ├── subagent-steps.tsx
    │   │   │   └── tool.tsx
    │   │   ├── error-boundary.tsx
    │   │   ├── kimi-cli-brand.tsx
    │   │   └── ui/
    │   │       ├── alert-dialog.tsx
    │   │       ├── alert.tsx
    │   │       ├── badge.tsx
    │   │       ├── button-group.tsx
    │   │       ├── button.tsx
    │   │       ├── card.tsx
    │   │       ├── carousel.tsx
    │   │       ├── checkbox.tsx
    │   │       ├── collapsible.tsx
    │   │       ├── command.tsx
    │   │       ├── context-menu.tsx
    │   │       ├── dialog.tsx
    │   │       ├── diff/
    │   │       │   ├── index.tsx
    │   │       │   ├── lazy.tsx
    │   │       │   ├── theme.css
    │   │       │   └── utils/
    │   │       │       ├── guess-lang.ts
    │   │       │       ├── index.ts
    │   │       │       └── parse.ts
    │   │       ├── dropdown-menu.tsx
    │   │       ├── hover-card.tsx
    │   │       ├── input-group.tsx
    │   │       ├── input.tsx
    │   │       ├── kbd.tsx
    │   │       ├── progress.tsx
    │   │       ├── resizable.tsx
    │   │       ├── scroll-area.tsx
    │   │       ├── select.tsx
    │   │       ├── separator.tsx
    │   │       ├── sonner.tsx
    │   │       ├── switch.tsx
    │   │       ├── textarea.tsx
    │   │       ├── theme-toggle.tsx
    │   │       ├── toggle-group.tsx
    │   │       ├── toggle.tsx
    │   │       └── tooltip.tsx
    │   ├── config/
    │   │   └── media.ts
    │   ├── features/
    │   │   ├── chat/
    │   │   │   ├── chat-workspace-container.tsx
    │   │   │   ├── chat.tsx
    │   │   │   ├── components/
    │   │   │   │   ├── activity-status-indicator.tsx
    │   │   │   │   ├── approval-dialog.tsx
    │   │   │   │   ├── assistant-message.tsx
    │   │   │   │   ├── attachment-button.tsx
    │   │   │   │   ├── chat-conversation.tsx
    │   │   │   │   ├── chat-prompt-composer.tsx
    │   │   │   │   ├── chat-workspace-header.tsx
    │   │   │   │   ├── open-in-menu.tsx
    │   │   │   │   ├── prompt-toolbar/
    │   │   │   │   │   ├── index.tsx
    │   │   │   │   │   ├── open-in-button.tsx
    │   │   │   │   │   ├── toolbar-changes.tsx
    │   │   │   │   │   ├── toolbar-context.tsx
    │   │   │   │   │   ├── toolbar-queue.tsx
    │   │   │   │   │   └── toolbar-todo.tsx
    │   │   │   │   ├── question-dialog.tsx
    │   │   │   │   ├── session-info-popover.tsx
    │   │   │   │   └── virtualized-message-list.tsx
    │   │   │   ├── file-mention-menu.tsx
    │   │   │   ├── global-config-controls.tsx
    │   │   │   ├── message-search-dialog.tsx
    │   │   │   ├── message-search-utils.ts
    │   │   │   ├── queue-store.ts
    │   │   │   ├── slash-command-menu.tsx
    │   │   │   ├── useFileMentions.ts
    │   │   │   └── useSlashCommands.ts
    │   │   ├── sessions/
    │   │   │   ├── create-session-dialog.tsx
    │   │   │   └── sessions.tsx
    │   │   └── tool/
    │   │       ├── components/
    │   │       │   └── display-content.tsx
    │   │       └── store.ts
    │   ├── hooks/
    │   │   ├── types.ts
    │   │   ├── use-theme.ts
    │   │   ├── useGitDiffStats.ts
    │   │   ├── useGlobalConfig.ts
    │   │   ├── useSessionStream.ts
    │   │   ├── useSessions.ts
    │   │   ├── useVideoThumbnail.ts
    │   │   ├── utils.ts
    │   │   └── wireTypes.ts
    │   ├── index.css
    │   ├── lib/
    │   │   ├── api/
    │   │   │   ├── .openapi-generator/
    │   │   │   │   ├── FILES
    │   │   │   │   └── VERSION
    │   │   │   ├── .openapi-generator-ignore
    │   │   │   ├── apis/
    │   │   │   │   ├── ConfigApi.ts
    │   │   │   │   ├── DefaultApi.ts
    │   │   │   │   ├── OpenInApi.ts
    │   │   │   │   ├── SessionsApi.ts
    │   │   │   │   ├── WorkDirsApi.ts
    │   │   │   │   └── index.ts
    │   │   │   ├── docs/
    │   │   │   │   ├── ConfigApi.md
    │   │   │   │   ├── ConfigModel.md
    │   │   │   │   ├── ConfigToml.md
    │   │   │   │   ├── CreateSessionRequest.md
    │   │   │   │   ├── DefaultApi.md
    │   │   │   │   ├── GenerateTitleRequest.md
    │   │   │   │   ├── GenerateTitleResponse.md
    │   │   │   │   ├── GitDiffStats.md
    │   │   │   │   ├── GitFileDiff.md
    │   │   │   │   ├── GlobalConfig.md
    │   │   │   │   ├── HTTPValidationError.md
    │   │   │   │   ├── ModelCapability.md
    │   │   │   │   ├── OpenInApi.md
    │   │   │   │   ├── OpenInRequest.md
    │   │   │   │   ├── OpenInResponse.md
    │   │   │   │   ├── ProviderType.md
    │   │   │   │   ├── Session.md
    │   │   │   │   ├── SessionStatus.md
    │   │   │   │   ├── SessionsApi.md
    │   │   │   │   ├── UpdateConfigTomlRequest.md
    │   │   │   │   ├── UpdateConfigTomlResponse.md
    │   │   │   │   ├── UpdateGlobalConfigRequest.md
    │   │   │   │   ├── UpdateGlobalConfigResponse.md
    │   │   │   │   ├── UpdateSessionRequest.md
    │   │   │   │   ├── UploadSessionFileResponse.md
    │   │   │   │   ├── ValidationError.md
    │   │   │   │   ├── ValidationErrorLocInner.md
    │   │   │   │   └── WorkDirsApi.md
    │   │   │   ├── index.ts
    │   │   │   ├── models/
    │   │   │   │   ├── ConfigModel.ts
    │   │   │   │   ├── ConfigToml.ts
    │   │   │   │   ├── CreateSessionRequest.ts
    │   │   │   │   ├── GenerateTitleRequest.ts
    │   │   │   │   ├── GenerateTitleResponse.ts
    │   │   │   │   ├── GitDiffStats.ts
    │   │   │   │   ├── GitFileDiff.ts
    │   │   │   │   ├── GlobalConfig.ts
    │   │   │   │   ├── HTTPValidationError.ts
    │   │   │   │   ├── ModelCapability.ts
    │   │   │   │   ├── OpenInRequest.ts
    │   │   │   │   ├── OpenInResponse.ts
    │   │   │   │   ├── ProviderType.ts
    │   │   │   │   ├── Session.ts
    │   │   │   │   ├── SessionStatus.ts
    │   │   │   │   ├── UpdateConfigTomlRequest.ts
    │   │   │   │   ├── UpdateConfigTomlResponse.ts
    │   │   │   │   ├── UpdateGlobalConfigRequest.ts
    │   │   │   │   ├── UpdateGlobalConfigResponse.ts
    │   │   │   │   ├── UpdateSessionRequest.ts
    │   │   │   │   ├── UploadSessionFileResponse.ts
    │   │   │   │   ├── ValidationError.ts
    │   │   │   │   ├── ValidationErrorLocInner.ts
    │   │   │   │   └── index.ts
    │   │   │   └── runtime.ts
    │   │   ├── apiClient.ts
    │   │   ├── auth.ts
    │   │   ├── utils.ts
    │   │   └── version.ts
    │   ├── main.tsx
    │   └── react-scan.d.ts
    ├── tsconfig.app.json
    ├── tsconfig.json
    ├── tsconfig.node.json
    └── vite.config.ts
Download .txt
Showing preview only (514K chars total). Download the full file or copy to clipboard to get everything.
SYMBOL INDEX (5340 symbols across 514 files)

FILE: .agents/skills/feature-smoke-test/scripts/inspect_session.py
  function parse_args (line 14) | def parse_args() -> argparse.Namespace:
  function truncate (line 32) | def truncate(text: str, max_text: int) -> str:
  function extract_text (line 39) | def extract_text(content: Any) -> str:
  function load_json (line 61) | def load_json(path: Path) -> dict[str, Any] | None:
  function iter_jsonl (line 70) | def iter_jsonl(path: Path) -> list[dict[str, Any]]:
  function find_latest_session (line 87) | def find_latest_session(share_dir: Path) -> Path:
  function print_header (line 113) | def print_header(title: str) -> None:
  function summarize_context_record (line 118) | def summarize_context_record(record: dict[str, Any], max_text: int) -> str:
  function summarize_wire_record (line 159) | def summarize_wire_record(record: dict[str, Any], max_text: int) -> str:
  function print_jsonl_summary (line 196) | def print_jsonl_summary(title: str, path: Path, tail_lines: int, max_tex...
  function print_file_inventory (line 226) | def print_file_inventory(session_dir: Path) -> None:
  function tail_text_file (line 236) | def tail_text_file(path: Path, tail_lines: int, max_text: int) -> list[s...
  function print_task_summary (line 243) | def print_task_summary(session_dir: Path, tail_lines: int, max_text: int...
  function main (line 296) | def main() -> int:

FILE: docs/scripts/sync-changelog.mjs
  constant HEADER (line 22) | const HEADER = `# Changelog

FILE: examples/custom-echo-soul/main.py
  class EchoSoul (line 11) | class EchoSoul:
    method __init__ (line 12) | def __init__(self) -> None:
    method name (line 16) | def name(self) -> str:
    method model_name (line 20) | def model_name(self) -> str:
    method model_capabilities (line 24) | def model_capabilities(self) -> set[ModelCapability]:
    method status (line 28) | def status(self) -> StatusSnapshot:
    method available_slash_commands (line 32) | def available_slash_commands(self) -> list[SlashCommand[Any]]:
    method run (line 35) | async def run(self, user_input: str | list[ContentPart]) -> None:

FILE: examples/custom-kimi-soul/main.py
  class HakimiSoul (line 22) | class HakimiSoul(KimiSoul):
    method create (line 24) | async def create(
    method name (line 52) | def name(self) -> str:
    method run (line 56) | async def run(self, user_input: str | list[ContentPart]) -> None:
  class MyBashParams (line 62) | class MyBashParams(BaseModel):
  class MyBashTool (line 66) | class MyBashTool(CallableTool2):
    method __call__ (line 71) | async def __call__(self, params: MyBashParams) -> ToolReturnValue:
  function main (line 84) | async def main():

FILE: examples/custom-tools/main.py
  function main (line 10) | async def main():

FILE: examples/custom-tools/my_tools/ls.py
  class Params (line 5) | class Params(BaseModel):
  class Ls (line 9) | class Ls(CallableTool2):
    method __call__ (line 14) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: examples/kimi-cli-stream-json/main.py
  function main (line 8) | async def main():

FILE: examples/kimi-cli-wire-messages/main.py
  function main (line 10) | async def main():

FILE: examples/kimi-psql/main.py
  class ExecuteSqlParams (line 47) | class ExecuteSqlParams(BaseModel):
  class ExecuteSql (line 53) | class ExecuteSql(CallableTool2[ExecuteSqlParams]):
    method __init__ (line 68) | def __init__(self, conninfo: str):
    method __call__ (line 79) | async def __call__(self, params: ExecuteSqlParams) -> ToolReturnValue:
  class PsqlProcess (line 141) | class PsqlProcess:
    method __init__ (line 144) | def __init__(self, psql_args: list[str]):
    method start (line 151) | def start(self) -> None:
    method _sync_window_size (line 178) | def _sync_window_size(self) -> None:
    method _handle_sigwinch (line 186) | def _handle_sigwinch(self, signum: int, frame: object) -> None:
    method read (line 190) | def read(self, timeout: float = 0.1) -> bytes:
    method write (line 202) | def write(self, data: bytes) -> None:
    method is_running (line 208) | def is_running(self) -> bool:
    method stop (line 221) | def stop(self) -> None:
    method master_fd (line 241) | def master_fd(self) -> int | None:
  class PsqlMode (line 250) | class PsqlMode(Enum):
    method toggle (line 254) | def toggle(self) -> "PsqlMode":
  function create_psql_soul (line 263) | async def create_psql_soul(llm: LLM | None, conninfo: str) -> KimiSoul:
  class PsqlShell (line 299) | class PsqlShell:
    method __init__ (line 305) | def __init__(self, soul: KimiSoul, psql_process: PsqlProcess):
    method _create_prompt_session (line 313) | def _create_prompt_session(self) -> PromptSession[str]:
    method run (line 342) | async def run(self) -> None:
    method _print_welcome (line 361) | def _print_welcome(self) -> None:
    method _run_ai_mode (line 377) | async def _run_ai_mode(self) -> None:
    method _run_psql_mode (line 431) | async def _run_psql_mode(self) -> None:
    method _switch_mode (line 500) | def _switch_mode(self) -> None:
  function main (line 518) | def main(
  function _run_async (line 545) | async def _run_async(

FILE: packages/kaos/src/kaos/__init__.py
  function type_check (line 16) | def type_check(
  class AsyncReadable (line 32) | class AsyncReadable(Protocol):
    method __aiter__ (line 35) | def __aiter__(self) -> AsyncIterator[bytes]:
    method at_eof (line 39) | def at_eof(self) -> bool:
    method feed_data (line 43) | def feed_data(self, data: bytes) -> None:
    method feed_eof (line 47) | def feed_eof(self) -> None:
    method read (line 51) | async def read(self, n: int = -1) -> bytes:
    method readline (line 55) | async def readline(self) -> bytes:
    method readexactly (line 59) | async def readexactly(self, n: int) -> bytes:
    method readuntil (line 63) | async def readuntil(self, separator: bytes) -> bytes:
  class AsyncWritable (line 69) | class AsyncWritable(Protocol):
    method can_write_eof (line 72) | def can_write_eof(self) -> bool:
    method close (line 76) | def close(self) -> None:
    method drain (line 80) | async def drain(self) -> None:
    method is_closing (line 84) | def is_closing(self) -> bool:
    method wait_closed (line 88) | async def wait_closed(self) -> None:
    method write (line 92) | def write(self, data: bytes) -> None:
    method writelines (line 96) | def writelines(self, data: Iterable[bytes], /) -> None:
    method write_eof (line 100) | def write_eof(self) -> None:
  class KaosProcess (line 106) | class KaosProcess(Protocol):
    method pid (line 114) | def pid(self) -> int:
    method returncode (line 119) | def returncode(self) -> int | None:
    method wait (line 123) | async def wait(self) -> int:
    method kill (line 127) | async def kill(self) -> None:
  class Kaos (line 133) | class Kaos(Protocol):
    method pathclass (line 139) | def pathclass(self) -> type[PurePath]:
    method normpath (line 143) | def normpath(self, path: StrOrKaosPath) -> KaosPath:
    method gethome (line 147) | def gethome(self) -> KaosPath:
    method getcwd (line 151) | def getcwd(self) -> KaosPath:
    method chdir (line 155) | async def chdir(self, path: StrOrKaosPath) -> None:
    method stat (line 159) | async def stat(self, path: StrOrKaosPath, *, follow_symlinks: bool = T...
    method iterdir (line 163) | def iterdir(self, path: StrOrKaosPath) -> AsyncGenerator[KaosPath]:
    method glob (line 167) | def glob(
    method readbytes (line 173) | async def readbytes(self, path: StrOrKaosPath, n: int | None = None) -...
    method readtext (line 177) | async def readtext(
    method readlines (line 187) | def readlines(
    method writebytes (line 197) | async def writebytes(self, path: StrOrKaosPath, data: bytes) -> int:
    method writetext (line 201) | async def writetext(
    method mkdir (line 213) | async def mkdir(
    method exec (line 219) | async def exec(self, *args: str, env: Mapping[str, str] | None = None)...
  class StatResult (line 232) | class StatResult:
  function get_current_kaos (line 247) | def get_current_kaos() -> Kaos:
  function set_current_kaos (line 254) | def set_current_kaos(kaos: Kaos) -> contextvars.Token[Kaos]:
  function reset_current_kaos (line 261) | def reset_current_kaos(token: contextvars.Token[Kaos]) -> None:
  function pathclass (line 268) | def pathclass() -> type[PurePath]:
  function normpath (line 272) | def normpath(path: StrOrKaosPath) -> KaosPath:
  function gethome (line 276) | def gethome() -> KaosPath:
  function getcwd (line 280) | def getcwd() -> KaosPath:
  function chdir (line 284) | async def chdir(path: StrOrKaosPath) -> None:
  function stat (line 288) | async def stat(path: StrOrKaosPath, *, follow_symlinks: bool = True) -> ...
  function iterdir (line 292) | def iterdir(path: StrOrKaosPath) -> AsyncGenerator[KaosPath]:
  function glob (line 296) | def glob(
  function readbytes (line 302) | async def readbytes(path: StrOrKaosPath, n: int | None = None) -> bytes:
  function readtext (line 306) | async def readtext(
  function readlines (line 315) | def readlines(
  function writebytes (line 324) | async def writebytes(path: StrOrKaosPath, data: bytes) -> int:
  function writetext (line 328) | async def writetext(
  function mkdir (line 341) | async def mkdir(path: StrOrKaosPath, parents: bool = False, exist_ok: bo...
  function exec (line 345) | async def exec(*args: str, env: Mapping[str, str] | None = None) -> Kaos...

FILE: packages/kaos/src/kaos/local.py
  function type_check (line 27) | def type_check(local: LocalKaos) -> None:
  class LocalKaos (line 31) | class LocalKaos:
    class Process (line 38) | class Process:
      method __init__ (line 41) | def __init__(self, process: AsyncioProcess) -> None:
      method pid (line 51) | def pid(self) -> int:
      method returncode (line 55) | def returncode(self) -> int | None:
      method wait (line 58) | async def wait(self) -> int:
      method kill (line 61) | async def kill(self) -> None:
    method pathclass (line 64) | def pathclass(self) -> type[PurePath]:
    method normpath (line 67) | def normpath(self, path: StrOrKaosPath) -> KaosPath:
    method gethome (line 70) | def gethome(self) -> KaosPath:
    method getcwd (line 73) | def getcwd(self) -> KaosPath:
    method chdir (line 76) | async def chdir(self, path: StrOrKaosPath) -> None:
    method stat (line 80) | async def stat(self, path: StrOrKaosPath, *, follow_symlinks: bool = T...
    method iterdir (line 96) | async def iterdir(self, path: StrOrKaosPath) -> AsyncGenerator[KaosPath]:
    method glob (line 101) | async def glob(
    method readbytes (line 111) | async def readbytes(self, path: StrOrKaosPath, n: int | None = None) -...
    method readtext (line 116) | async def readtext(
    method readlines (line 127) | async def readlines(
    method writebytes (line 139) | async def writebytes(self, path: StrOrKaosPath, data: bytes) -> int:
    method writetext (line 144) | async def writetext(
    method mkdir (line 157) | async def mkdir(
    method exec (line 163) | async def exec(self, *args: str, env: Mapping[str, str] | None = None)...

FILE: packages/kaos/src/kaos/path.py
  class KaosPath (line 11) | class KaosPath:
    method __init__ (line 16) | def __init__(self, *args: str) -> None:
    method unsafe_from_local_path (line 20) | def unsafe_from_local_path(cls, path: Path) -> KaosPath:
    method unsafe_to_local_path (line 27) | def unsafe_to_local_path(self) -> Path:
    method __lt__ (line 34) | def __lt__(self, other: KaosPath) -> bool:
    method __le__ (line 37) | def __le__(self, other: KaosPath) -> bool:
    method __gt__ (line 40) | def __gt__(self, other: KaosPath) -> bool:
    method __ge__ (line 43) | def __ge__(self, other: KaosPath) -> bool:
    method __eq__ (line 46) | def __eq__(self, other: Any) -> bool:
    method __repr__ (line 51) | def __repr__(self) -> str:
    method __str__ (line 54) | def __str__(self) -> str:
    method name (line 58) | def name(self) -> str:
    method parent (line 63) | def parent(self) -> KaosPath:
    method is_absolute (line 67) | def is_absolute(self) -> bool:
    method joinpath (line 71) | def joinpath(self, *other: str) -> KaosPath:
    method __truediv__ (line 75) | def __truediv__(self, other: str | KaosPath) -> KaosPath:
    method canonical (line 82) | def canonical(self) -> KaosPath:
    method relative_to (line 95) | def relative_to(self, other: KaosPath) -> KaosPath:
    method home (line 101) | def home(cls) -> KaosPath:
    method cwd (line 106) | def cwd(cls) -> KaosPath:
    method expanduser (line 110) | def expanduser(self) -> KaosPath:
    method stat (line 121) | async def stat(self, follow_symlinks: bool = True) -> kaos.StatResult:
    method exists (line 125) | async def exists(self, *, follow_symlinks: bool = True) -> bool:
    method is_file (line 133) | async def is_file(self, *, follow_symlinks: bool = True) -> bool:
    method is_dir (line 141) | async def is_dir(self, *, follow_symlinks: bool = True) -> bool:
    method iterdir (line 149) | def iterdir(self) -> AsyncGenerator[KaosPath]:
    method glob (line 153) | def glob(self, pattern: str, *, case_sensitive: bool = True) -> AsyncG...
    method read_bytes (line 157) | async def read_bytes(self, n: int | None = None) -> bytes:
    method read_text (line 161) | async def read_text(
    method read_lines (line 170) | def read_lines(
    method write_bytes (line 179) | async def write_bytes(self, data: bytes) -> int:
    method write_text (line 183) | async def write_text(
    method append_text (line 199) | async def append_text(
    method mkdir (line 215) | async def mkdir(self, parents: bool = False, exist_ok: bool = False) -...

FILE: packages/kaos/src/kaos/ssh.py
  function type_check (line 26) | def type_check(ssh: SSHKaos) -> None:
  function _build_st_mode (line 41) | def _build_st_mode(attrs: asyncssh.SFTPAttrs) -> int:
  function _sec_with_nanos (line 55) | def _sec_with_nanos(sec: int, ns: int | None) -> float:
  class SSHKaos (line 61) | class SSHKaos:
    class Process (line 68) | class Process:
      method __init__ (line 71) | def __init__(self, process: asyncssh.SSHClientProcess[bytes]) -> None:
      method pid (line 78) | def pid(self) -> int:
      method returncode (line 83) | def returncode(self) -> int | None:
      method wait (line 86) | async def wait(self) -> int:
      method kill (line 93) | async def kill(self) -> None:
    method create (line 97) | async def create(
    method __init__ (line 140) | def __init__(
    method host (line 156) | def host(self) -> str:
    method pathclass (line 159) | def pathclass(self) -> type[PurePath]:
    method normpath (line 162) | def normpath(self, path: StrOrKaosPath) -> KaosPath:
    method gethome (line 165) | def gethome(self) -> KaosPath:
    method getcwd (line 168) | def getcwd(self) -> KaosPath:
    method chdir (line 171) | async def chdir(self, path: StrOrKaosPath) -> None:
    method stat (line 175) | async def stat(
    method iterdir (line 199) | async def iterdir(self, path: StrOrKaosPath) -> AsyncGenerator[KaosPath]:
    method glob (line 207) | async def glob(
    method readbytes (line 220) | async def readbytes(self, path: StrOrKaosPath, n: int | None = None) -...
    method readtext (line 224) | async def readtext(
    method readlines (line 234) | async def readlines(
    method writebytes (line 246) | async def writebytes(self, path: StrOrKaosPath, data: bytes) -> int:
    method writetext (line 250) | async def writetext(
    method mkdir (line 262) | async def mkdir(
    method exec (line 276) | async def exec(self, *args: str, env: Mapping[str, str] | None = None)...
    method unsafe_close (line 291) | async def unsafe_close(self) -> None:

FILE: packages/kaos/tests/test_kaos_path.py
  function kaos_cwd (line 15) | def kaos_cwd(tmp_path: Path) -> Generator[KaosPath]:
  function test_join_and_parent (line 27) | def test_join_and_parent(kaos_cwd: KaosPath):
  function test_home_and_cwd (line 37) | def test_home_and_cwd(kaos_cwd: KaosPath):
  function test_expanduser (line 42) | def test_expanduser(kaos_cwd: KaosPath):
  function test_canonical_and_relative_to (line 48) | def test_canonical_and_relative_to(kaos_cwd: KaosPath):
  function test_exists_and_file_ops (line 58) | async def test_exists_and_file_ops(kaos_cwd: KaosPath):
  function test_iterdir_and_glob_from_kaos_path (line 76) | async def test_iterdir_and_glob_from_kaos_path(kaos_cwd: KaosPath):
  function test_read_write_bytes (line 91) | async def test_read_write_bytes(kaos_cwd: KaosPath):

FILE: packages/kaos/tests/test_local_kaos.py
  function local_kaos (line 17) | def local_kaos(tmp_path: Path) -> Generator[LocalKaos]:
  function test_pathclass_gethome_and_getcwd (line 30) | def test_pathclass_gethome_and_getcwd(local_kaos: LocalKaos):
  function test_chdir_and_stat (line 41) | async def test_chdir_and_stat(local_kaos: LocalKaos):
  function test_iterdir_and_glob (line 55) | async def test_iterdir_and_glob(local_kaos: LocalKaos):
  function test_read_write_and_append_text (line 69) | async def test_read_write_and_append_text(local_kaos: LocalKaos):
  function test_mkdir_with_parents (line 84) | async def test_mkdir_with_parents(local_kaos: LocalKaos):
  function test_read_write_bytes (line 92) | async def test_read_write_bytes(local_kaos: LocalKaos):
  function _python_code_args (line 99) | def _python_code_args(code: str) -> tuple[str, str, str]:
  function test_exec_runs_command_and_streams (line 103) | async def test_exec_runs_command_and_streams(local_kaos: LocalKaos):
  function test_exec_runs_command_wait_before_read (line 118) | async def test_exec_runs_command_wait_before_read(local_kaos: LocalKaos):
  function test_exec_non_zero_exit (line 133) | async def test_exec_non_zero_exit(local_kaos: LocalKaos):
  function test_exec_wait_timeout (line 140) | async def test_exec_wait_timeout(local_kaos: LocalKaos):

FILE: packages/kaos/tests/test_local_kaos_cmd.py
  function local_kaos (line 24) | def local_kaos(tmp_path: Path) -> Generator[LocalKaos]:
  function run_cmd (line 37) | async def run_cmd(command: str) -> tuple[int, str, str]:
  function test_simple_command (line 50) | async def test_simple_command():
  function test_command_with_error (line 59) | async def test_command_with_error():
  function test_command_chaining (line 68) | async def test_command_chaining():
  function test_file_operations (line 77) | async def test_file_operations():

FILE: packages/kaos/tests/test_local_kaos_sh.py
  function local_kaos (line 24) | def local_kaos(tmp_path: Path) -> Generator[LocalKaos]:
  function run_sh (line 37) | async def run_sh(
  function test_simple_command (line 68) | async def test_simple_command():
  function test_command_with_error (line 76) | async def test_command_with_error():
  function test_command_chaining (line 84) | async def test_command_chaining():
  function test_command_sequential (line 95) | async def test_command_sequential():
  function test_command_conditional (line 106) | async def test_command_conditional():
  function test_command_pipe (line 114) | async def test_command_pipe():
  function test_multiple_pipes (line 122) | async def test_multiple_pipes():
  function test_command_with_timeout (line 130) | async def test_command_with_timeout():
  function test_command_timeout_expires (line 138) | async def test_command_timeout_expires():
  function test_environment_variables (line 144) | async def test_environment_variables():
  function test_file_operations (line 154) | async def test_file_operations():
  function test_text_processing (line 169) | async def test_text_processing():
  function test_command_substitution (line 177) | async def test_command_substitution():
  function test_arithmetic_substitution (line 185) | async def test_arithmetic_substitution():
  function test_very_long_output (line 193) | async def test_very_long_output():
  function test_command_reads_stdin (line 204) | async def test_command_reads_stdin():
  function test_command_reads_multiple_lines_from_stdin (line 215) | async def test_command_reads_multiple_lines_from_stdin():

FILE: packages/kaos/tests/test_ssh_kaos.py
  function ssh_kaos_config (line 27) | def ssh_kaos_config() -> dict[str, Any]:
  function ssh_kaos (line 54) | async def ssh_kaos(ssh_kaos_config: dict[str, Any]) -> AsyncGenerator[SS...
  function remote_base (line 68) | async def remote_base(ssh_kaos: SSHKaos) -> AsyncGenerator[str]:
  function bind_current_kaos (line 84) | def bind_current_kaos(ssh_kaos: SSHKaos):
  function test_pathclass_home_and_cwd (line 93) | async def test_pathclass_home_and_cwd(ssh_kaos: SSHKaos):
  function test_chdir_updates_real_path (line 105) | async def test_chdir_updates_real_path(ssh_kaos: SSHKaos, remote_base: s...
  function test_exec_respects_cwd (line 117) | async def test_exec_respects_cwd(ssh_kaos: SSHKaos, remote_base: str):
  function test_exec_wait_before_read (line 128) | async def test_exec_wait_before_read(ssh_kaos: SSHKaos):
  function test_mkdir_respects_exist_ok (line 138) | async def test_mkdir_respects_exist_ok(ssh_kaos: SSHKaos, remote_base: s...
  function test_stat_reports_directory_and_file_metadata (line 149) | async def test_stat_reports_directory_and_file_metadata(ssh_kaos: SSHKao...
  function test_kaospath_roundtrip (line 163) | async def test_kaospath_roundtrip(bind_current_kaos: SSHKaos, remote_bas...
  function test_iterdir_lists_child_entries (line 193) | async def test_iterdir_lists_child_entries(ssh_kaos: SSHKaos, remote_bas...
  function test_glob_is_case_sensitive (line 205) | async def test_glob_is_case_sensitive(ssh_kaos: SSHKaos, remote_base: str):
  function test_exec_streams_stdout_and_stderr (line 217) | async def test_exec_streams_stdout_and_stderr(ssh_kaos: SSHKaos):
  function test_exec_rejects_empty_command (line 228) | async def test_exec_rejects_empty_command(ssh_kaos: SSHKaos):
  function test_process_kill_updates_returncode (line 233) | async def test_process_kill_updates_returncode(ssh_kaos: SSHKaos):

FILE: packages/kosong/src/kosong/__init__.py
  function step (line 104) | async def step(
  class StepResult (line 184) | class StepResult:
    method tool_results (line 200) | async def tool_results(self) -> list[ToolResult]:

FILE: packages/kosong/src/kosong/__main__.py
  class BashToolParams (line 17) | class BashToolParams(BaseModel):
  class BashTool (line 22) | class BashTool(CallableTool2[BashToolParams]):
    method __call__ (line 27) | async def __call__(self, params: BashToolParams) -> ToolReturnValue:
  function agent_loop (line 47) | async def agent_loop(chat_provider: ChatProvider, toolset: Toolset):
  function tool_result_to_message (line 86) | def tool_result_to_message(result: ToolResult) -> Message:
  function main (line 94) | async def main():

FILE: packages/kosong/src/kosong/_generate.py
  function generate (line 17) | async def generate(
  class GenerateResult (line 85) | class GenerateResult:
  function _message_append (line 96) | def _message_append(message: Message, part: StreamedMessagePart) -> None:

FILE: packages/kosong/src/kosong/chat_provider/__init__.py
  class ChatProvider (line 11) | class ChatProvider(Protocol):
    method model_name (line 20) | def model_name(self) -> str:
    method thinking_effort (line 27) | def thinking_effort(self) -> "ThinkingEffort | None":
    method generate (line 33) | async def generate(
    method with_thinking (line 50) | def with_thinking(self, effort: "ThinkingEffort") -> Self:
  class RetryableChatProvider (line 59) | class RetryableChatProvider(Protocol):
    method on_retryable_error (line 62) | def on_retryable_error(self, error: BaseException) -> bool:
  class StreamedMessage (line 76) | class StreamedMessage(Protocol):
    method __aiter__ (line 79) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method id (line 84) | def id(self) -> str | None:
    method usage (line 89) | def usage(self) -> "TokenUsage | None":
  class TokenUsage (line 94) | class TokenUsage(BaseModel):
    method total (line 107) | def total(self) -> int:
    method input (line 112) | def input(self) -> int:
  class ChatProviderError (line 121) | class ChatProviderError(Exception):
    method __init__ (line 124) | def __init__(self, message: str):
  class APIConnectionError (line 128) | class APIConnectionError(ChatProviderError):
  class APITimeoutError (line 132) | class APITimeoutError(ChatProviderError):
  class APIStatusError (line 136) | class APIStatusError(ChatProviderError):
    method __init__ (line 141) | def __init__(self, status_code: int, message: str):
  class APIEmptyResponseError (line 146) | class APIEmptyResponseError(ChatProviderError):

FILE: packages/kosong/src/kosong/chat_provider/chaos.py
  function type_check (line 24) | def type_check(
  class ChaosConfig (line 31) | class ChaosConfig(BaseModel):
    method from_env (line 41) | def from_env(cls) -> "ChaosConfig":
  class ChaosTransport (line 57) | class ChaosTransport(httpx.AsyncBaseTransport):
    method __init__ (line 60) | def __init__(self, wrapped_transport: httpx.AsyncBaseTransport, config...
    method handle_async_request (line 65) | async def handle_async_request(self, request: httpx.Request) -> httpx....
    method _should_inject_error (line 72) | def _should_inject_error(self) -> bool:
    method _create_error_response (line 75) | def _create_error_response(self, request: httpx.Request, status_code: ...
  class ChaosChatProvider (line 104) | class ChaosChatProvider:
    method __init__ (line 107) | def __init__(self, provider: ChatProvider, chaos_config: ChaosConfig |...
    method generate (line 113) | async def generate(
    method _monkey_patch_client (line 122) | def _monkey_patch_client(self):
    method _find_transport_owner (line 145) | def _find_transport_owner(self) -> Any:
    method model_name (line 172) | def model_name(self) -> str:
    method thinking_effort (line 181) | def thinking_effort(self) -> ThinkingEffort | None:
    method on_retryable_error (line 184) | def on_retryable_error(self, error: BaseException) -> bool:
    method with_thinking (line 192) | def with_thinking(self, effort: ThinkingEffort) -> "ChaosChatProvider":
    method for_kimi (line 196) | def for_kimi(
  class ChaosStreamedMessage (line 205) | class ChaosStreamedMessage:
    method __init__ (line 208) | def __init__(self, wrapped: StreamedMessage, config: ChaosConfig):
    method __aiter__ (line 214) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 217) | async def __anext__(self) -> StreamedMessagePart:
    method id (line 222) | def id(self) -> str | None:
    method usage (line 226) | def usage(self) -> TokenUsage | None:
    method _should_corrupt_tool_call (line 229) | def _should_corrupt_tool_call(self) -> bool:
    method _maybe_corrupt_tool_call (line 233) | def _maybe_corrupt_tool_call(self, part: StreamedMessagePart) -> Strea...
    method _corrupt_tool_call (line 242) | def _corrupt_tool_call(self, tool_call: ToolCall) -> StreamedMessagePart:
    method _corrupt_tool_call_part (line 250) | def _corrupt_tool_call_part(self, part: ToolCallPart) -> StreamedMessa...
  function _dev_main_anthropic (line 261) | async def _dev_main_anthropic():

FILE: packages/kosong/src/kosong/chat_provider/echo/dsl.py
  function parse_echo_script (line 18) | def parse_echo_script(
  function _parse_part (line 50) | def _parse_part(kind: str, payload: str, lineno: int, raw_line: str) -> ...
  function _parse_usage (line 75) | def _parse_usage(payload: str) -> TokenUsage:
  function _parse_url_payload (line 95) | def _parse_url_payload(payload: str, kind: str) -> tuple[str, str | None]:
  function _parse_tool_call (line 111) | def _parse_tool_call(payload: str, lineno: int, raw_line: str) -> ToolCall:
  function _parse_tool_call_part (line 143) | def _parse_tool_call_part(payload: str) -> ToolCallPart:
  function _parse_mapping (line 155) | def _parse_mapping(raw: str, *, context: str) -> dict[str, Any]:
  function _parse_value (line 180) | def _parse_value(raw: str) -> Any:
  function _strip_quotes (line 193) | def _strip_quotes(value: str) -> str:

FILE: packages/kosong/src/kosong/chat_provider/echo/echo.py
  function type_check (line 21) | def type_check(echo: EchoChatProvider):
  class EchoChatProvider (line 25) | class EchoChatProvider:
    method model_name (line 63) | def model_name(self) -> str:
    method thinking_effort (line 67) | def thinking_effort(self) -> ThinkingEffort | None:
    method generate (line 70) | async def generate(
    method with_thinking (line 87) | def with_thinking(self, effort: ThinkingEffort) -> Self:
  class EchoStreamedMessage (line 93) | class EchoStreamedMessage(StreamedMessage):
    method __init__ (line 96) | def __init__(
    method __aiter__ (line 107) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 110) | async def __anext__(self) -> StreamedMessagePart:
    method _to_stream (line 113) | async def _to_stream(
    method id (line 120) | def id(self) -> str | None:
    method usage (line 124) | def usage(self) -> TokenUsage | None:

FILE: packages/kosong/src/kosong/chat_provider/echo/scripted_echo.py
  function type_check (line 23) | def type_check(scripted: ScriptedEchoChatProvider):
  class ScriptedEchoChatProvider (line 27) | class ScriptedEchoChatProvider:
    method __init__ (line 34) | def __init__(self, scripts: Iterable[str], *, trace: bool = False):
    method model_name (line 40) | def model_name(self) -> str:
    method thinking_effort (line 44) | def thinking_effort(self) -> ThinkingEffort | None:
    method generate (line 47) | async def generate(
    method with_thinking (line 65) | def with_thinking(self, effort: ThinkingEffort) -> Self:
  class ScriptedEchoStreamedMessage (line 71) | class ScriptedEchoStreamedMessage(StreamedMessage):
    method __init__ (line 74) | def __init__(
    method __aiter__ (line 85) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 88) | async def __anext__(self) -> StreamedMessagePart:
    method _to_stream (line 91) | async def _to_stream(
    method id (line 98) | def id(self) -> str | None:
    method usage (line 102) | def usage(self) -> TokenUsage | None:

FILE: packages/kosong/src/kosong/chat_provider/kimi.py
  function type_check (line 48) | def type_check(kimi: "Kimi"):
  class ThinkingConfig (line 53) | class ThinkingConfig(TypedDict, total=True):
  class ExtraBody (line 57) | class ExtraBody(TypedDict, total=False, extra_items=Any):
  class Kimi (line 61) | class Kimi:
    class GenerationKwargs (line 78) | class GenerationKwargs(TypedDict, total=False):
    method __init__ (line 95) | def __init__(
    method model_name (line 129) | def model_name(self) -> str:
    method thinking_effort (line 133) | def thinking_effort(self) -> ThinkingEffort | None:
    method generate (line 147) | async def generate(
    method on_retryable_error (line 177) | def on_retryable_error(self, error: BaseException) -> bool:
    method with_thinking (line 187) | def with_thinking(self, effort: ThinkingEffort) -> Self:
    method with_generation_kwargs (line 205) | def with_generation_kwargs(self, **kwargs: Unpack[GenerationKwargs]) -...
    method with_extra_body (line 217) | def with_extra_body(self, extra_body: ExtraBody) -> Self:
    method model_parameters (line 232) | def model_parameters(self) -> dict[str, Any]:
    method files (line 244) | def files(self) -> "KimiFiles":
  class KimiFiles (line 248) | class KimiFiles:
    method __init__ (line 249) | def __init__(self, client: AsyncOpenAI) -> None:
    method upload_video (line 252) | async def upload_video(self, *, data: bytes, mime_type: str) -> VideoU...
    method _upload_file (line 259) | async def _upload_file(self, *, data: bytes, mime_type: str, purpose: ...
  class KimiFileObject (line 276) | class KimiFileObject(BaseModel):
  function _guess_filename (line 283) | def _guess_filename(mime_type: str) -> str:
  function _convert_message (line 288) | def _convert_message(message: Message) -> ChatCompletionMessageParam:
  function _convert_tool (line 304) | def _convert_tool(tool: Tool) -> ChatCompletionToolParam:
  class KimiStreamedMessage (line 321) | class KimiStreamedMessage:
    method __init__ (line 324) | def __init__(self, response: ChatCompletion | AsyncStream[ChatCompleti...
    method __aiter__ (line 332) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 335) | async def __anext__(self) -> StreamedMessagePart:
    method id (line 339) | def id(self) -> str | None:
    method usage (line 343) | def usage(self) -> TokenUsage | None:
    method _convert_non_stream_response (line 365) | async def _convert_non_stream_response(
    method _convert_stream_response (line 388) | async def _convert_stream_response(
  function extract_usage_from_chunk (line 437) | def extract_usage_from_chunk(chunk: ChatCompletionChunk) -> CompletionUs...
  function _dev_main (line 453) | async def _dev_main():

FILE: packages/kosong/src/kosong/chat_provider/mock.py
  function type_check (line 17) | def type_check(mock: "MockChatProvider"):
  class MockChatProvider (line 21) | class MockChatProvider(ChatProvider):
    method __init__ (line 28) | def __init__(
    method model_name (line 36) | def model_name(self) -> str:
    method thinking_effort (line 40) | def thinking_effort(self) -> ThinkingEffort | None:
    method generate (line 43) | async def generate(
    method with_thinking (line 52) | def with_thinking(self, effort: ThinkingEffort) -> Self:
  class MockStreamedMessage (line 56) | class MockStreamedMessage(StreamedMessage):
    method __init__ (line 59) | def __init__(self, message_parts: list[StreamedMessagePart]):
    method __aiter__ (line 62) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 65) | async def __anext__(self) -> StreamedMessagePart:
    method _to_stream (line 68) | async def _to_stream(
    method id (line 75) | def id(self) -> str:
    method usage (line 79) | def usage(self) -> TokenUsage | None:

FILE: packages/kosong/src/kosong/chat_provider/openai_common.py
  function create_openai_client (line 22) | def create_openai_client(
  function _drain_awaitable (line 31) | async def _drain_awaitable(awaitable: Awaitable[object]) -> None:
  function close_openai_client (line 38) | def close_openai_client(client: AsyncOpenAI) -> None:
  function close_replaced_openai_client (line 57) | def close_replaced_openai_client(client: AsyncOpenAI, *, client_kwargs: ...
  function convert_error (line 73) | def convert_error(error: OpenAIError | httpx.HTTPError) -> ChatProviderE...
  function thinking_effort_to_reasoning_effort (line 91) | def thinking_effort_to_reasoning_effort(effort: ThinkingEffort) -> Reaso...
  function reasoning_effort_to_thinking_effort (line 103) | def reasoning_effort_to_thinking_effort(effort: ReasoningEffort) -> Thin...
  function tool_to_openai (line 115) | def tool_to_openai(tool: Tool) -> ChatCompletionToolParam:

FILE: packages/kosong/src/kosong/contrib/chat_provider/anthropic.py
  function type_check (line 91) | def type_check(anthropic: "Anthropic"):
  class Anthropic (line 100) | class Anthropic:
    class GenerationKwargs (line 107) | class GenerationKwargs(TypedDict, total=False):
    method __init__ (line 120) | def __init__(
    method model_name (line 145) | def model_name(self) -> str:
    method thinking_effort (line 149) | def thinking_effort(self) -> "ThinkingEffort | None":
    method generate (line 164) | async def generate(
    method _use_adaptive_thinking (line 235) | def _use_adaptive_thinking(self) -> bool:
    method with_thinking (line 240) | def with_thinking(self, effort: "ThinkingEffort") -> Self:
    method with_generation_kwargs (line 270) | def with_generation_kwargs(self, **kwargs: Unpack[GenerationKwargs]) -...
    method model_parameters (line 283) | def model_parameters(self) -> dict[str, Any]:
    method _convert_message (line 294) | def _convert_message(self, message: Message) -> MessageParam:
  class AnthropicStreamedMessage (line 360) | class AnthropicStreamedMessage:
    method __init__ (line 361) | def __init__(self, response: AnthropicMessage | AsyncStream[RawMessage...
    method __aiter__ (line 369) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 372) | async def __anext__(self) -> StreamedMessagePart:
    method id (line 376) | def id(self) -> str | None:
    method usage (line 380) | def usage(self) -> TokenUsage | None:
    method _update_usage (line 390) | def _update_usage(self, delta_usage: MessageDeltaUsage) -> None:
    method _convert_non_stream_response (line 400) | async def _convert_non_stream_response(
    method _convert_stream_response (line 424) | async def _convert_stream_response(
  function _convert_tool (line 476) | def _convert_tool(tool: Tool) -> ToolParam:
  function _tool_result_message_to_block (line 484) | def _tool_result_message_to_block(
  function _image_url_part_to_anthropic (line 515) | def _image_url_part_to_anthropic(part: ImageURLPart) -> ImageBlockParam:
  function _convert_error (line 542) | def _convert_error(error: AnthropicError) -> ChatProviderError:

FILE: packages/kosong/src/kosong/contrib/chat_provider/google_genai.py
  function type_check (line 60) | def type_check(google_genai: "GoogleGenAI"):
  class GoogleGenAI (line 64) | class GoogleGenAI:
    class GenerationKwargs (line 71) | class GenerationKwargs(TypedDict, total=False):
    method __init__ (line 83) | def __init__(
    method model_name (line 105) | def model_name(self) -> str:
    method thinking_effort (line 109) | def thinking_effort(self) -> "ThinkingEffort | None":
    method generate (line 139) | async def generate(
    method with_thinking (line 169) | def with_thinking(self, effort: "ThinkingEffort") -> Self:
    method with_generation_kwargs (line 202) | def with_generation_kwargs(self, **kwargs: Unpack[GenerationKwargs]) -...
    method model_parameters (line 215) | def model_parameters(self) -> dict[str, Any]:
  class GoogleGenAIStreamedMessage (line 228) | class GoogleGenAIStreamedMessage:
    method __init__ (line 229) | def __init__(self, response: GenerateContentResponse | AsyncIterator[G...
    method __aiter__ (line 237) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 240) | async def __anext__(self) -> StreamedMessagePart:
    method id (line 244) | def id(self) -> str | None:
    method usage (line 248) | def usage(self) -> TokenUsage | None:
    method _convert_non_stream_response (line 258) | async def _convert_non_stream_response(
    method _convert_stream_response (line 278) | async def _convert_stream_response(
    method _process_part (line 303) | def _process_part(self, part: Part):
    method _process_part_async (line 347) | async def _process_part_async(self, part: Part) -> AsyncIterator[Strea...
  function tool_to_google_genai (line 353) | def tool_to_google_genai(tool: KosongTool) -> Tool:
  function _image_url_part_to_google_genai (line 370) | def _image_url_part_to_google_genai(part: ImageURLPart) -> Part:
  function _audio_url_part_to_google_genai (line 401) | def _audio_url_part_to_google_genai(part: AudioURLPart) -> Part:
  function _tool_result_to_response_and_parts (line 443) | def _tool_result_to_response_and_parts(
  function _tool_call_id_to_name (line 465) | def _tool_call_id_to_name(tool_call_id: str, tool_name_by_id: dict[str, ...
  function _tool_message_to_function_response_part (line 473) | def _tool_message_to_function_response_part(
  function _tool_messages_to_google_genai_content (line 494) | def _tool_messages_to_google_genai_content(
  function messages_to_google_genai_contents (line 556) | def messages_to_google_genai_contents(messages: Sequence[Message]) -> li...
  function message_to_google_genai (line 618) | def message_to_google_genai(message: Message) -> Content:
  function _convert_error (line 676) | def _convert_error(error: Exception) -> ChatProviderError:
  function main (line 706) | async def main():

FILE: packages/kosong/src/kosong/contrib/chat_provider/openai_legacy.py
  function type_check (line 38) | def type_check(openai_legacy: "OpenAILegacy"):
  class OpenAILegacy (line 43) | class OpenAILegacy:
    class GenerationKwargs (line 56) | class GenerationKwargs(TypedDict, extra_items=Any, total=False):
    method __init__ (line 71) | def __init__(
    method model_name (line 105) | def model_name(self) -> str:
    method thinking_effort (line 109) | def thinking_effort(self) -> ThinkingEffort | None:
    method generate (line 114) | async def generate(
    method on_retryable_error (line 143) | def on_retryable_error(self, error: BaseException) -> bool:
    method with_thinking (line 153) | def with_thinking(self, effort: ThinkingEffort) -> Self:
    method with_generation_kwargs (line 158) | def with_generation_kwargs(self, **kwargs: Unpack[GenerationKwargs]) -...
    method model_parameters (line 171) | def model_parameters(self) -> dict[str, Any]:
    method _convert_message (line 183) | def _convert_message(self, message: Message) -> ChatCompletionMessageP...
  class OpenAILegacyStreamedMessage (line 212) | class OpenAILegacyStreamedMessage:
    method __init__ (line 213) | def __init__(
    method __aiter__ (line 224) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 227) | async def __anext__(self) -> StreamedMessagePart:
    method id (line 231) | def id(self) -> str | None:
    method usage (line 235) | def usage(self) -> TokenUsage | None:
    method _convert_non_stream_response (line 252) | async def _convert_non_stream_response(
    method _convert_stream_response (line 276) | async def _convert_stream_response(
  function _dev_main (line 328) | async def _dev_main():

FILE: packages/kosong/src/kosong/contrib/chat_provider/openai_responses.py
  function type_check (line 62) | def type_check(openai_responses: "OpenAIResponses"):
  function get_openai_models_set (line 67) | def get_openai_models_set() -> set[str]:
  function is_openai_model (line 86) | def is_openai_model(model_name: str) -> bool:
  class OpenAIResponses (line 91) | class OpenAIResponses:
    class GenerationKwargs (line 109) | class GenerationKwargs(TypedDict, total=False):
    method __init__ (line 118) | def __init__(
    method model_name (line 142) | def model_name(self) -> str:
    method thinking_effort (line 146) | def thinking_effort(self) -> ThinkingEffort | None:
    method generate (line 152) | async def generate(
    method on_retryable_error (line 192) | def on_retryable_error(self, error: BaseException) -> bool:
    method with_thinking (line 202) | def with_thinking(self, effort: ThinkingEffort) -> Self:
    method with_generation_kwargs (line 206) | def with_generation_kwargs(self, **kwargs: Unpack[GenerationKwargs]) -...
    method model_parameters (line 219) | def model_parameters(self) -> dict[str, Any]:
    method _convert_message (line 230) | def _convert_message(self, message: Message) -> list[ResponseInputItem...
  function _convert_tool (line 343) | def _convert_tool(tool: Tool) -> ToolParam:
  function _content_parts_to_input_items (line 354) | def _content_parts_to_input_items(parts: list[ContentPart]) -> ResponseI...
  function _content_parts_to_output_items (line 381) | def _content_parts_to_output_items(parts: list[ContentPart]) -> list[Res...
  function _message_content_to_function_output_items (line 394) | def _message_content_to_function_output_items(
  function _map_audio_url_to_input_item (line 421) | def _map_audio_url_to_input_item(url: str) -> ResponseInputFileParam | N...
  function _map_audio_url_to_file_content (line 444) | def _map_audio_url_to_file_content(url: str) -> ResponseInputFileContent...
  class OpenAIResponsesStreamedMessage (line 458) | class OpenAIResponsesStreamedMessage:
    method __init__ (line 459) | def __init__(self, response: Response | AsyncStream[ResponseStreamEven...
    method __aiter__ (line 467) | def __aiter__(self) -> AsyncIterator[StreamedMessagePart]:
    method __anext__ (line 470) | async def __anext__(self) -> StreamedMessagePart:
    method id (line 474) | def id(self) -> str | None:
    method usage (line 478) | def usage(self) -> TokenUsage | None:
    method _convert_non_stream_response (line 492) | async def _convert_non_stream_response(
    method _convert_stream_response (line 518) | async def _convert_stream_response(
  function _dev_main (line 556) | async def _dev_main():

FILE: packages/kosong/src/kosong/contrib/context/linear.py
  class LinearContext (line 9) | class LinearContext:
    method __init__ (line 14) | def __init__(self, storage: "LinearStorage"):
    method history (line 18) | def history(self) -> list[Message]:
    method token_count (line 22) | def token_count(self) -> int:
    method add_message (line 25) | async def add_message(self, message: Message):
    method mark_token_count (line 28) | async def mark_token_count(self, token_count: int):
  class LinearStorage (line 33) | class LinearStorage(Protocol):
    method messages (line 35) | def messages(self) -> list[Message]:
    method token_count (line 42) | def token_count(self) -> int:
    method append_message (line 49) | async def append_message(self, message: Message) -> None: ...
    method mark_token_count (line 50) | async def mark_token_count(self, token_count: int) -> None: ...
  class MemoryLinearStorage (line 53) | class MemoryLinearStorage:
    method __init__ (line 58) | def __init__(self):
    method messages (line 63) | def messages(self) -> list[Message]:
    method token_count (line 67) | def token_count(self) -> int:
    method append_message (line 70) | async def append_message(self, message: Message):
    method mark_token_count (line 73) | async def mark_token_count(self, token_count: int):
  class JsonlLinearStorage (line 77) | class JsonlLinearStorage(MemoryLinearStorage):
    method __init__ (line 82) | def __init__(self, path: Path | str):
    method restore (line 87) | async def restore(self):
    method _get_file (line 108) | def _get_file(self) -> IO[str]:
    method __del__ (line 113) | def __del__(self):
    method append_message (line 117) | async def append_message(self, message: Message):
    method mark_token_count (line 132) | async def mark_token_count(self, token_count: int):

FILE: packages/kosong/src/kosong/message.py
  class MergeableMixin (line 10) | class MergeableMixin:
    method merge_in_place (line 11) | def merge_in_place(self, other: Any) -> bool:
  class ContentPart (line 16) | class ContentPart(BaseModel, ABC, MergeableMixin):
    method __init_subclass__ (line 33) | def __init_subclass__(cls, **kwargs: Any) -> None:
    method __get_pydantic_core_schema__ (line 47) | def __get_pydantic_core_schema__(
  class TextPart (line 74) | class TextPart(ContentPart):
    method merge_in_place (line 84) | def merge_in_place(self, other: Any) -> bool:
  class ThinkPart (line 91) | class ThinkPart(ContentPart):
    method merge_in_place (line 103) | def merge_in_place(self, other: Any) -> bool:
  class ImageURLPart (line 114) | class ImageURLPart(ContentPart):
    class ImageURL (line 122) | class ImageURL(BaseModel):
  class AudioURLPart (line 134) | class AudioURLPart(ContentPart):
    class AudioURL (line 142) | class AudioURL(BaseModel):
  class VideoURLPart (line 154) | class VideoURLPart(ContentPart):
    class VideoURL (line 162) | class VideoURL(BaseModel):
  class ToolCall (line 174) | class ToolCall(BaseModel, MergeableMixin):
    class FunctionBody (line 185) | class FunctionBody(BaseModel):
    method merge_in_place (line 203) | def merge_in_place(self, other: Any) -> bool:
  class ToolCallPart (line 213) | class ToolCallPart(BaseModel, MergeableMixin):
    method merge_in_place (line 220) | def merge_in_place(self, other: Any) -> bool:
  class Message (line 243) | class Message(BaseModel):
    method _serialize_content (line 266) | def _serialize_content(self, content: list[ContentPart]) -> str | list...
    method _coerce_none_content (line 273) | def _coerce_none_content(cls, value: Any) -> Any:
    method __init__ (line 280) | def __init__(
    method extract_text (line 301) | def extract_text(self, sep: str = "") -> str:

FILE: packages/kosong/src/kosong/tooling/__init__.py
  class Tool (line 18) | class Tool(BaseModel):
    method _validate_parameters (line 31) | def _validate_parameters(self) -> Self:
  class DisplayBlock (line 36) | class DisplayBlock(BaseModel, ABC):
    method __init_subclass__ (line 52) | def __init_subclass__(cls, **kwargs: Any) -> None:
    method __get_pydantic_core_schema__ (line 66) | def __get_pydantic_core_schema__(
  class UnknownDisplayBlock (line 98) | class UnknownDisplayBlock(DisplayBlock):
  class BriefDisplayBlock (line 105) | class BriefDisplayBlock(DisplayBlock):
  class ToolReturnValue (line 112) | class ToolReturnValue(BaseModel):
    method brief (line 132) | def brief(self) -> str:
  class ToolOk (line 140) | class ToolOk(ToolReturnValue):
    method __init__ (line 143) | def __init__(
  class ToolError (line 158) | class ToolError(ToolReturnValue):
    method __init__ (line 161) | def __init__(
  class CallableTool (line 172) | class CallableTool(Tool, ABC):
    method base (line 183) | def base(self) -> Tool:
    method call (line 187) | async def call(self, arguments: JsonType) -> ToolReturnValue:
    method __call__ (line 210) | async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue:
  class _GenerateJsonSchemaNoTitles (line 219) | class _GenerateJsonSchemaNoTitles(GenerateJsonSchema):
    method field_title_should_be_set (line 223) | def field_title_should_be_set(self, schema) -> bool:  # type: ignore[r...
    method _update_class_schema (line 227) | def _update_class_schema(self, json_schema, cls, config) -> None:  # t...
  class CallableTool2 (line 232) | class CallableTool2[Params: BaseModel](ABC):
    method __init__ (line 247) | def __init__(
    method base (line 288) | def base(self) -> Tool:
    method call (line 292) | async def call(self, arguments: JsonType) -> ToolReturnValue:
    method __call__ (line 310) | async def __call__(self, params: Params) -> ToolReturnValue:
  class ToolResult (line 319) | class ToolResult(BaseModel):
  class Toolset (line 333) | class Toolset(Protocol):
    method tools (line 339) | def tools(self) -> list[Tool]:
    method handle (line 343) | def handle(self, tool_call: ToolCall) -> HandleResult:

FILE: packages/kosong/src/kosong/tooling/empty.py
  function type_check (line 9) | def type_check(empty: "EmptyToolset"):
  class EmptyToolset (line 13) | class EmptyToolset:
    method tools (line 17) | def tools(self) -> list[Tool]:
    method handle (line 20) | def handle(self, tool_call: ToolCall) -> HandleResult:

FILE: packages/kosong/src/kosong/tooling/error.py
  class ToolNotFoundError (line 4) | class ToolNotFoundError(ToolError):
    method __init__ (line 7) | def __init__(self, tool_name: str):
  class ToolParseError (line 14) | class ToolParseError(ToolError):
    method __init__ (line 17) | def __init__(self, message: str):
  class ToolValidateError (line 24) | class ToolValidateError(ToolError):
    method __init__ (line 27) | def __init__(self, message: str):
  class ToolRuntimeError (line 34) | class ToolRuntimeError(ToolError):
    method __init__ (line 37) | def __init__(self, message: str):

FILE: packages/kosong/src/kosong/tooling/mcp.py
  function convert_mcp_content (line 6) | def convert_mcp_content(part: mcp.types.ContentBlock) -> kosong.message....

FILE: packages/kosong/src/kosong/tooling/simple.py
  function type_check (line 26) | def type_check(
  class SimpleToolset (line 36) | class SimpleToolset:
    method __init__ (line 41) | def __init__(self, tools: Iterable[ToolType] | None = None):
    method __iadd__ (line 48) | def __iadd__(self, tool: ToolType) -> Self:
    method __add__ (line 82) | def __add__(self, tool: ToolType) -> "SimpleToolset":
    method add (line 92) | def add(self, tool: ToolType) -> None:
    method remove (line 99) | def remove(self, tool_name: str) -> None:
    method tools (line 109) | def tools(self) -> list[Tool]:
    method handle (line 112) | def handle(self, tool_call: ToolCall) -> HandleResult:

FILE: packages/kosong/src/kosong/utils/aio.py
  function callback (line 8) | async def callback[**Params, Return](

FILE: packages/kosong/src/kosong/utils/jsonschema.py
  function deref_json_schema (line 11) | def deref_json_schema(schema: JsonDict) -> JsonDict:

FILE: packages/kosong/tests/api_snapshot_tests/common.py
  function make_anthropic_response (line 25) | def make_anthropic_response(model: str = "claude-sonnet-4-20250514") -> ...
  function make_chat_completion_response (line 38) | def make_chat_completion_response(model: str = "test-model") -> dict[str...
  class Case (line 88) | class Case(TypedDict, total=False):
  function capture_request (line 219) | async def capture_request(
  function run_test_cases (line 235) | async def run_test_cases(

FILE: packages/kosong/tests/api_snapshot_tests/test_anthropic.py
  function test_anthropic_message_conversion (line 70) | async def test_anthropic_message_conversion():
  function test_anthropic_generation_kwargs (line 474) | async def test_anthropic_generation_kwargs():
  function test_anthropic_with_thinking (line 492) | async def test_anthropic_with_thinking():
  function test_anthropic_opus_46_adaptive_thinking (line 508) | async def test_anthropic_opus_46_adaptive_thinking():
  function test_anthropic_opus_46_thinking_off (line 528) | async def test_anthropic_opus_46_thinking_off():
  function test_anthropic_metadata (line 545) | async def test_anthropic_metadata():
  function test_anthropic_metadata_omitted_when_none (line 563) | async def test_anthropic_metadata_omitted_when_none():
  function test_anthropic_opus_46_thinking_effort_property (line 580) | async def test_anthropic_opus_46_thinking_effort_property():

FILE: packages/kosong/tests/api_snapshot_tests/test_google_genai.py
  function make_response (line 24) | def make_response() -> dict[str, Any]:
  function test_google_genai_message_conversion (line 63) | async def test_google_genai_message_conversion():
  function test_google_genai_vertexai_message_conversion (line 286) | async def test_google_genai_vertexai_message_conversion():
  function test_google_genai_generation_kwargs (line 509) | async def test_google_genai_generation_kwargs():
  function test_google_genai_with_thinking (line 525) | async def test_google_genai_with_thinking():

FILE: packages/kosong/tests/api_snapshot_tests/test_kimi.py
  function test_kimi_message_conversion (line 42) | async def test_kimi_message_conversion():
  function test_kimi_generation_kwargs (line 292) | async def test_kimi_generation_kwargs():
  function test_kimi_with_thinking (line 307) | async def test_kimi_with_thinking():

FILE: packages/kosong/tests/api_snapshot_tests/test_openai_legacy.py
  function test_openai_legacy_message_conversion (line 16) | async def test_openai_legacy_message_conversion():
  function test_openai_legacy_reasoning_content (line 245) | async def test_openai_legacy_reasoning_content():
  function test_openai_legacy_generation_kwargs (line 281) | async def test_openai_legacy_generation_kwargs():
  function test_openai_legacy_with_thinking (line 296) | async def test_openai_legacy_with_thinking():

FILE: packages/kosong/tests/api_snapshot_tests/test_openai_responses.py
  function make_response (line 15) | def make_response() -> dict[str, Any]:
  function test_openai_responses_message_conversion (line 52) | async def test_openai_responses_message_conversion():
  function test_openai_responses_generation_kwargs (line 369) | async def test_openai_responses_generation_kwargs():
  function test_openai_responses_omits_reasoning_by_default (line 382) | async def test_openai_responses_omits_reasoning_by_default():
  function test_openai_responses_with_thinking_off_omits_reasoning (line 394) | async def test_openai_responses_with_thinking_off_omits_reasoning():
  function test_openai_responses_with_thinking_low (line 410) | async def test_openai_responses_with_thinking_low():
  function test_openai_responses_with_thinking (line 425) | async def test_openai_responses_with_thinking():

FILE: packages/kosong/tests/test_chat_provider.py
  function test_mock_chat_provider (line 10) | def test_mock_chat_provider():
  function test_chaos_chat_provider (line 26) | async def test_chaos_chat_provider():

FILE: packages/kosong/tests/test_context.py
  function test_linear_context (line 8) | def test_linear_context():
  function test_linear_context_with_jsonl_storage (line 26) | def test_linear_context_with_jsonl_storage():

FILE: packages/kosong/tests/test_echo_chat_provider.py
  function test_echo_chat_provider_streams_parts (line 18) | async def test_echo_chat_provider_streams_parts():
  function test_echo_chat_provider_with_generate_merge_tool_call (line 70) | async def test_echo_chat_provider_with_generate_merge_tool_call():
  function test_echo_chat_provider_rejects_non_string_arguments (line 101) | async def test_echo_chat_provider_rejects_non_string_arguments():
  function test_echo_chat_provider_requires_user_message (line 112) | async def test_echo_chat_provider_requires_user_message():
  function test_echo_chat_provider_requires_dsl_content (line 120) | async def test_echo_chat_provider_requires_dsl_content():

FILE: packages/kosong/tests/test_generate.py
  function test_generate (line 10) | def test_generate():
  function test_generate_with_callbacks (line 44) | def test_generate_with_callbacks():

FILE: packages/kosong/tests/test_json_schema_deref.py
  function test_no_ref (line 14) | def test_no_ref():
  function test_simple_ref (line 37) | def test_simple_ref():
  function test_nested_ref (line 80) | def test_nested_ref():

FILE: packages/kosong/tests/test_kimi_stream_usage.py
  function test_kimi_extracts_choice_usage_in_stream_chunk (line 6) | def test_kimi_extracts_choice_usage_in_stream_chunk() -> None:

FILE: packages/kosong/tests/test_message.py
  function test_plain_text_message (line 14) | def test_plain_text_message():
  function test_message_with_single_part (line 21) | def test_message_with_single_part():
  function test_message_with_tool_calls (line 41) | def test_message_with_tool_calls():
  function test_message_with_no_content (line 66) | def test_message_with_no_content():
  function test_message_with_complex_content (line 90) | def test_message_with_complex_content():
  function test_deserialize_from_json_plain_text (line 140) | def test_deserialize_from_json_plain_text():
  function test_deserialize_from_json_with_content_and_tool_calls (line 149) | def test_deserialize_from_json_with_content_and_tool_calls():
  function test_deserialize_from_json_none_content_with_tool_calls (line 181) | def test_deserialize_from_json_none_content_with_tool_calls():
  function test_deserialize_from_json_with_content_but_no_tool_calls (line 207) | def test_deserialize_from_json_with_content_but_no_tool_calls():
  function test_message_with_empty_list_content (line 223) | def test_message_with_empty_list_content():
  function test_message_extract_text (line 281) | def test_message_extract_text():

FILE: packages/kosong/tests/test_openai_common.py
  function test_create_openai_client_does_not_inject_max_retries (line 11) | def test_create_openai_client_does_not_inject_max_retries(monkeypatch: p...
  function test_retry_recovery_does_not_close_shared_http_client (line 33) | async def test_retry_recovery_does_not_close_shared_http_client() -> None:

FILE: packages/kosong/tests/test_scripted_echo_chat_provider.py
  function test_scripted_echo_chat_provider_streams_parts (line 18) | async def test_scripted_echo_chat_provider_streams_parts():
  function test_scripted_echo_chat_provider_exhausted (line 83) | async def test_scripted_echo_chat_provider_exhausted():
  function test_scripted_echo_chat_provider_with_generate_merge_tool_call (line 92) | async def test_scripted_echo_chat_provider_with_generate_merge_tool_call():
  function test_scripted_echo_chat_provider_rejects_non_string_arguments (line 123) | async def test_scripted_echo_chat_provider_rejects_non_string_arguments():
  function test_scripted_echo_chat_provider_requires_dsl_content (line 133) | async def test_scripted_echo_chat_provider_requires_dsl_content():

FILE: packages/kosong/tests/test_step.py
  function test_step (line 12) | def test_step():

FILE: packages/kosong/tests/test_tool_call.py
  function test_callable_tool_int_argument (line 30) | def test_callable_tool_int_argument():
  function test_callable_tool_list_argument (line 46) | def test_callable_tool_list_argument():
  function test_callable_tool_dict_argument (line 65) | def test_callable_tool_dict_argument():
  function test_simple_toolset (line 87) | def test_simple_toolset():
  function test_callable_tool_2 (line 244) | def test_callable_tool_2():
  function test_simple_toolset_sub (line 279) | def test_simple_toolset_sub():
  function test_simple_toolset_with_real_type_annotation_callable_tool (line 304) | def test_simple_toolset_with_real_type_annotation_callable_tool():
  function test_simple_toolset_with_string_annotation_callable_tool (line 328) | def test_simple_toolset_with_string_annotation_callable_tool():
  function test_simple_toolset_with_invalid_string_annotation_rejected (line 352) | def test_simple_toolset_with_invalid_string_annotation_rejected():
  function test_simple_toolset_with_real_type_annotation_callable_tool2 (line 380) | def test_simple_toolset_with_real_type_annotation_callable_tool2():
  function test_simple_toolset_with_string_annotation_callable_tool2 (line 404) | def test_simple_toolset_with_string_annotation_callable_tool2():
  function _test_handle_async_with_string_annotation (line 428) | async def _test_handle_async_with_string_annotation():
  function test_simple_toolset_with_string_annotation_handle (line 465) | def test_simple_toolset_with_string_annotation_handle():

FILE: packages/kosong/tests/test_tool_result.py
  function test_tool_return_value (line 14) | def test_tool_return_value():
  function test_tool_ok (line 50) | def test_tool_ok():
  function test_tool_error (line 67) | def test_tool_error():
  function test_tool_ok_with_content_parts (line 84) | def test_tool_ok_with_content_parts():
  function test_tool_error_subclass (line 113) | def test_tool_error_subclass():
  function test_unknown_display_block (line 127) | def test_unknown_display_block():

FILE: scripts/build_vis.py
  function has_required_vis_type_files (line 22) | def has_required_vis_type_files() -> bool:
  function resolve_npm (line 26) | def resolve_npm() -> str | None:
  function check_node_version (line 37) | def check_node_version() -> bool:
  function run_npm (line 59) | def run_npm(npm: str, args: list[str]) -> int:
  function main (line 71) | def main() -> int:

FILE: scripts/build_web.py
  function read_pyproject_version (line 24) | def read_pyproject_version() -> str:
  function find_version_in_dist (line 30) | def find_version_in_dist(version: str) -> bool:
  function resolve_npm (line 50) | def resolve_npm() -> str | None:
  function run_npm (line 61) | def run_npm(npm: str, args: list[str]) -> int:
  function has_required_web_type_files (line 73) | def has_required_web_type_files() -> bool:
  function main (line 77) | def main() -> int:

FILE: scripts/check_kimi_dependency_versions.py
  function load_project_table (line 10) | def load_project_table(pyproject_path: Path) -> dict:
  function load_project_version (line 21) | def load_project_version(pyproject_path: Path) -> str:
  function find_pinned_dependency (line 29) | def find_pinned_dependency(deps: list[str], name: str) -> str | None:
  function main (line 43) | def main() -> int:

FILE: scripts/check_version_tag.py
  function load_project_version (line 10) | def load_project_version(pyproject_path: Path) -> str:
  function main (line 25) | def main() -> int:

FILE: scripts/cleanup_tmp_sessions.py
  function is_tmp_path (line 38) | def is_tmp_path(path: str) -> bool:
  function work_dir_hash (line 45) | def work_dir_hash(path: str, kaos: str = "local") -> str:
  function dir_total_size (line 50) | def dir_total_size(d: Path) -> int:
  function main (line 54) | def main() -> None:

FILE: sdks/kimi-sdk/tests/test_smoke.py
  function _chat_completion_response (line 9) | def _chat_completion_response() -> dict[str, object]:
  function test_generate_smoke (line 27) | async def test_generate_smoke() -> None:

FILE: src/kimi_cli/__init__.py
  class _LazyLogger (line 6) | class _LazyLogger:
    method __init__ (line 9) | def __init__(self) -> None:
    method _get (line 12) | def _get(self) -> Any:
    method __getattr__ (line 23) | def __getattr__(self, name: str) -> Any:

FILE: src/kimi_cli/__main__.py
  function _prog_name (line 8) | def _prog_name() -> str:
  function main (line 12) | def main(argv: Sequence[str] | None = None) -> int | str | None:

FILE: src/kimi_cli/acp/__init__.py
  function acp_main (line 1) | def acp_main() -> None:

FILE: src/kimi_cli/acp/convert.py
  function acp_blocks_to_content_parts (line 17) | def acp_blocks_to_content_parts(prompt: list[ACPContentBlock]) -> list[C...
  function display_block_to_acp_content (line 53) | def display_block_to_acp_content(
  function tool_result_to_acp_content (line 67) | def tool_result_to_acp_content(

FILE: src/kimi_cli/acp/kaos.py
  class _NullWritable (line 18) | class _NullWritable:
    method can_write_eof (line 19) | def can_write_eof(self) -> bool:
    method close (line 22) | def close(self) -> None:
    method drain (line 25) | async def drain(self) -> None:
    method is_closing (line 28) | def is_closing(self) -> bool:
    method wait_closed (line 31) | async def wait_closed(self) -> None:
    method write (line 34) | def write(self, data: bytes) -> None:
    method writelines (line 37) | def writelines(self, data: Iterable[bytes], /) -> None:
    method write_eof (line 40) | def write_eof(self) -> None:
  class ACPProcess (line 44) | class ACPProcess:
    method __init__ (line 47) | def __init__(
    method pid (line 69) | def pid(self) -> int:
    method returncode (line 73) | def returncode(self) -> int | None:
    method wait (line 76) | async def wait(self) -> int:
    method kill (line 79) | async def kill(self) -> None:
    method _feed_output (line 82) | def _feed_output(self, output_response: acp.schema.TerminalOutputRespo...
    method _normalize_exit_code (line 97) | def _normalize_exit_code(exit_code: int | None) -> int:
    method _poll_output (line 100) | async def _poll_output(self) -> None:
  class ACPKaos (line 144) | class ACPKaos:
    method __init__ (line 149) | def __init__(
    method pathclass (line 169) | def pathclass(self):
    method normpath (line 172) | def normpath(self, path: StrOrKaosPath) -> KaosPath:
    method gethome (line 175) | def gethome(self) -> KaosPath:
    method getcwd (line 178) | def getcwd(self) -> KaosPath:
    method chdir (line 181) | async def chdir(self, path: StrOrKaosPath) -> None:
    method stat (line 184) | async def stat(self, path: StrOrKaosPath, *, follow_symlinks: bool = T...
    method iterdir (line 187) | def iterdir(self, path: StrOrKaosPath) -> AsyncGenerator[KaosPath]:
    method glob (line 190) | def glob(
    method readbytes (line 195) | async def readbytes(self, path: StrOrKaosPath, n: int | None = None) -...
    method readtext (line 198) | async def readtext(
    method readlines (line 211) | async def readlines(
    method writebytes (line 222) | async def writebytes(self, path: StrOrKaosPath, data: bytes) -> int:
    method writetext (line 225) | async def writetext(
    method mkdir (line 260) | async def mkdir(
    method exec (line 265) | async def exec(self, *args: str, env: Mapping[str, str] | None = None)...
    method _abs_path (line 268) | def _abs_path(self, path: StrOrKaosPath) -> str:

FILE: src/kimi_cli/acp/mcp.py
  function acp_mcp_servers_to_mcp_config (line 13) | def acp_mcp_servers_to_mcp_config(mcp_servers: list[MCPServer]) -> MCPCo...
  function _convert_acp_mcp_server (line 25) | def _convert_acp_mcp_server(server: MCPServer) -> dict[str, Any]:

FILE: src/kimi_cli/acp/server.py
  class ACPServer (line 29) | class ACPServer:
    method __init__ (line 30) | def __init__(self) -> None:
    method on_connect (line 37) | def on_connect(self, conn: acp.Client) -> None:
    method initialize (line 41) | async def initialize(
    method _check_auth (line 110) | def _check_auth(self) -> None:
    method new_session (line 135) | async def new_session(
    method _setup_session (line 198) | async def _setup_session(
    method load_session (line 238) | async def load_session(
    method resume_session (line 253) | async def resume_session(
    method fork_session (line 280) | async def fork_session(
    method list_sessions (line 285) | async def list_sessions(
    method set_session_mode (line 306) | async def set_session_mode(self, mode_id: str, session_id: str, **kwar...
    method set_session_model (line 309) | async def set_session_model(self, model_id: str, session_id: str, **kw...
    method authenticate (line 356) | async def authenticate(self, method_id: str, **kwargs: Any) -> acp.Aut...
    method prompt (line 380) | async def prompt(
    method cancel (line 390) | async def cancel(self, session_id: str, **kwargs: Any) -> None:
    method ext_method (line 398) | async def ext_method(self, method: str, params: dict[str, Any]) -> dic...
    method ext_notification (line 401) | async def ext_notification(self, method: str, params: dict[str, Any]) ...
  class _ModelIDConv (line 405) | class _ModelIDConv(NamedTuple):
    method from_acp_model_id (line 410) | def from_acp_model_id(cls, model_id: str) -> _ModelIDConv:
    method to_acp_model_id (line 415) | def to_acp_model_id(self) -> str:
  function _expand_llm_models (line 421) | def _expand_llm_models(models: dict[str, LLMModel]) -> list[acp.schema.M...

FILE: src/kimi_cli/acp/session.py
  function get_current_acp_tool_call_id_or_none (line 52) | def get_current_acp_tool_call_id_or_none() -> str | None:
  function register_terminal_tool_call_id (line 65) | def register_terminal_tool_call_id(tool_call_id: str) -> None:
  function should_hide_terminal_output (line 71) | def should_hide_terminal_output(tool_call_id: str) -> bool:
  class _ToolCallState (line 76) | class _ToolCallState:
    method __init__ (line 79) | def __init__(self, tool_call: ToolCall):
    method acp_tool_call_id (line 87) | def acp_tool_call_id(self) -> str:
    method append_args_part (line 96) | def append_args_part(self, args_part: str) -> None:
    method get_title (line 101) | def get_title(self) -> str:
  class _TurnState (line 110) | class _TurnState:
    method __init__ (line 111) | def __init__(self):
  class ACPSession (line 120) | class ACPSession:
    method __init__ (line 121) | def __init__(
    method id (line 135) | def id(self) -> str:
    method cli (line 140) | def cli(self) -> KimiCLI:
    method prompt (line 144) | async def prompt(self, prompt: list[ACPContentBlock]) -> acp.PromptRes...
    method cancel (line 227) | async def cancel(self) -> None:
    method _send_thinking (line 234) | async def _send_thinking(self, think: str):
    method _send_text (line 247) | async def _send_text(self, text: str):
    method _send_notification (line 260) | async def _send_notification(self, notification: Notification):
    method _send_tool_call (line 268) | async def _send_tool_call(self, tool_call: ToolCall):
    method _send_tool_call_part (line 296) | async def _send_tool_call_part(self, part: ToolCallPart):
    method _send_tool_result (line 329) | async def _send_tool_result(self, result: ToolResult):
    method _handle_approval_request (line 363) | async def _handle_approval_request(self, request: ApprovalRequest):
    method _send_plan_update (line 450) | async def _send_plan_update(self, block: TodoDisplayBlock) -> None:

FILE: src/kimi_cli/acp/tools.py
  function replace_tools (line 18) | def replace_tools(
  class HideOutputDisplayBlock (line 42) | class HideOutputDisplayBlock(DisplayBlock):
  class Terminal (line 48) | class Terminal(CallableTool2[ShellParams]):
    method __init__ (line 49) | def __init__(
    method __call__ (line 63) | async def __call__(self, params: ShellParams) -> ToolReturnValue:

FILE: src/kimi_cli/acp/version.py
  class ACPVersionSpec (line 7) | class ACPVersionSpec:
  function negotiate_version (line 28) | def negotiate_version(client_protocol_version: int) -> ACPVersionSpec:

FILE: src/kimi_cli/agentspec.py
  function get_agents_dir (line 16) | def get_agents_dir() -> Path:
  class Inherit (line 24) | class Inherit(NamedTuple):
  class AgentSpec (line 31) | class AgentSpec(BaseModel):
  class SubagentSpec (line 51) | class SubagentSpec(BaseModel):
  class ResolvedAgentSpec (line 59) | class ResolvedAgentSpec:
  function load_agent_spec (line 70) | def load_agent_spec(agent_file: Path) -> ResolvedAgentSpec:
  function _load_agent_spec (line 100) | def _load_agent_spec(agent_file: Path) -> AgentSpec:

FILE: src/kimi_cli/app.py
  function enable_logging (line 36) | def enable_logging(debug: bool = False, *, redirect_stderr: bool = True)...
  class KimiCLI (line 55) | class KimiCLI:
    method create (line 57) | async def create(
    method __init__ (line 214) | def __init__(
    method soul (line 225) | def soul(self) -> KimiSoul:
    method session (line 230) | def session(self) -> Session:
    method shutdown_background_tasks (line 234) | def shutdown_background_tasks(self) -> None:
    method _env (line 243) | async def _env(self) -> AsyncGenerator[None]:
    method run (line 254) | async def run(
    method run_shell (line 309) | async def run_shell(self, command: str | None = None) -> bool:
    method run_print (line 387) | async def run_print(
    method run_acp (line 408) | async def run_acp(self) -> None:
    method run_wire_stdio (line 416) | async def run_wire_stdio(self) -> None:

FILE: src/kimi_cli/auth/oauth.py
  class OAuthError (line 56) | class OAuthError(RuntimeError):
  class OAuthUnauthorized (line 60) | class OAuthUnauthorized(OAuthError):
  class OAuthDeviceExpired (line 64) | class OAuthDeviceExpired(OAuthError):
  class OAuthEvent (line 72) | class OAuthEvent:
    method __str__ (line 77) | def __str__(self) -> str:
    method json (line 81) | def json(self) -> str:
  class OAuthToken (line 89) | class OAuthToken:
    method from_response (line 97) | def from_response(cls, payload: dict[str, Any]) -> OAuthToken:
    method to_dict (line 107) | def to_dict(self) -> dict[str, Any]:
    method from_dict (line 117) | def from_dict(cls, payload: dict[str, Any]) -> OAuthToken:
  class DeviceAuthorization (line 129) | class DeviceAuthorization:
  function _oauth_host (line 138) | def _oauth_host() -> str:
  function _device_id_path (line 142) | def _device_id_path() -> Path:
  function _ensure_private_file (line 146) | def _ensure_private_file(path: Path) -> None:
  function _device_model (line 151) | def _device_model() -> str:
  function get_device_id (line 185) | def get_device_id() -> str:
  function _ascii_header_value (line 195) | def _ascii_header_value(value: str, *, fallback: str = "unknown") -> str:
  function _common_headers (line 204) | def _common_headers() -> dict[str, str]:
  function _credentials_dir (line 218) | def _credentials_dir() -> Path:
  function _credentials_path (line 224) | def _credentials_path(key: str) -> Path:
  function _load_from_keyring (line 229) | def _load_from_keyring(key: str) -> OAuthToken | None:
  function _delete_from_keyring (line 247) | def _delete_from_keyring(key: str) -> None:
  function _load_from_file (line 254) | def _load_from_file(key: str) -> OAuthToken | None:
  function _save_to_file (line 268) | def _save_to_file(key: str, token: OAuthToken) -> None:
  function _delete_from_file (line 274) | def _delete_from_file(key: str) -> None:
  function load_tokens (line 280) | def load_tokens(ref: OAuthRef) -> OAuthToken | None:
  function save_tokens (line 299) | def save_tokens(ref: OAuthRef, token: OAuthToken) -> OAuthRef:
  function delete_tokens (line 307) | def delete_tokens(ref: OAuthRef) -> None:
  function request_device_authorization (line 313) | async def request_device_authorization() -> DeviceAuthorization:
  function _request_device_token (line 336) | async def _request_device_token(auth: DeviceAuthorization) -> tuple[int,...
  function refresh_token (line 362) | async def refresh_token(refresh_token: str) -> OAuthToken:
  function _select_default_model_and_thinking (line 384) | def _select_default_model_and_thinking(models: list[ModelInfo]) -> tuple...
  function _apply_kimi_code_config (line 393) | def _apply_kimi_code_config(
  function login_kimi_code (line 444) | async def login_kimi_code(
  function logout_kimi_code (line 550) | async def logout_kimi_code(config: Config) -> AsyncIterator[OAuthEvent]:
  class OAuthManager (line 584) | class OAuthManager:
    method __init__ (line 585) | def __init__(self, config: Config) -> None:
    method _iter_oauth_refs (line 593) | def _iter_oauth_refs(self) -> list[OAuthRef]:
    method _migrate_oauth_storage (line 606) | def _migrate_oauth_storage(self) -> None:
    method _load_initial_tokens (line 634) | def _load_initial_tokens(self) -> None:
    method _cache_access_token (line 640) | def _cache_access_token(self, ref: OAuthRef, token: OAuthToken) -> None:
    method common_headers (line 646) | def common_headers(self) -> dict[str, str]:
    method resolve_api_key (line 649) | def resolve_api_key(self, api_key: SecretStr, oauth: OAuthRef | None) ...
    method _kimi_code_ref (line 661) | def _kimi_code_ref(self) -> OAuthRef | None:
    method ensure_fresh (line 674) | async def ensure_fresh(self, runtime: Runtime) -> None:
    method refreshing (line 686) | async def refreshing(self, runtime: Runtime) -> AsyncIterator[None]:
    method _refresh_tokens (line 720) | async def _refresh_tokens(
    method _apply_access_token (line 775) | def _apply_access_token(self, runtime: Runtime, access_token: str) -> ...

FILE: src/kimi_cli/auth/platforms.py
  class ModelInfo (line 16) | class ModelInfo(BaseModel):
    method capabilities (line 26) | def capabilities(self) -> set[ModelCapability]:
  class Platform (line 43) | class Platform(NamedTuple):
  function _kimi_code_base_url (line 52) | def _kimi_code_base_url() -> str:
  function get_platform_by_id (line 84) | def get_platform_by_id(platform_id: str) -> Platform | None:
  function get_platform_by_name (line 88) | def get_platform_by_name(name: str) -> Platform | None:
  function managed_provider_key (line 95) | def managed_provider_key(platform_id: str) -> str:
  function managed_model_key (line 99) | def managed_model_key(platform_id: str, model_id: str) -> str:
  function parse_managed_provider_key (line 103) | def parse_managed_provider_key(provider_key: str) -> str | None:
  function is_managed_provider_key (line 109) | def is_managed_provider_key(provider_key: str) -> bool:
  function get_platform_name_for_provider (line 113) | def get_platform_name_for_provider(provider_key: str) -> str | None:
  function refresh_managed_models (line 121) | async def refresh_managed_models(config: Config) -> bool:
  function list_models (line 180) | async def list_models(platform: Platform, api_key: str) -> list[ModelInfo]:
  function _list_models (line 193) | async def _list_models(
  function _apply_models (line 231) | def _apply_models(

FILE: src/kimi_cli/background/ids.py
  function generate_task_id (line 14) | def generate_task_id(kind: TaskKind) -> str:

FILE: src/kimi_cli/background/manager.py
  class BackgroundTaskManager (line 31) | class BackgroundTaskManager:
    method __init__ (line 32) | def __init__(
    method store (line 47) | def store(self) -> BackgroundTaskStore:
    method role (line 51) | def role(self) -> str:
    method copy_for_role (line 54) | def copy_for_role(self, role: str) -> BackgroundTaskManager:
    method _ensure_root (line 62) | def _ensure_root(self) -> None:
    method _ensure_local_backend (line 66) | def _ensure_local_backend(self) -> None:
    method _active_task_count (line 70) | def _active_task_count(self) -> int:
    method _worker_command (line 75) | def _worker_command(self, task_dir: Path) -> list[str]:
    method _launch_worker (line 104) | def _launch_worker(self, task_dir: Path) -> int:
    method create_bash_task (line 119) | def create_bash_task(
    method list_tasks (line 175) | def list_tasks(
    method get_task (line 188) | def get_task(self, task_id: str) -> TaskView | None:
    method read_output (line 194) | def read_output(
    method tail_output (line 209) | def tail_output(
    method wait (line 223) | async def wait(self, task_id: str, *, timeout_s: int = 30) -> TaskView:
    method _best_effort_kill (line 233) | def _best_effort_kill(self, runtime: TaskRuntime) -> None:
    method kill (line 257) | def kill(self, task_id: str, *, reason: str = "Killed by user") -> Tas...
    method kill_all_active (line 274) | def kill_all_active(self, *, reason: str = "CLI session ended") -> lis...
    method recover (line 290) | def recover(self) -> None:
    method reconcile (line 334) | def reconcile(self, *, limit: int | None = None) -> list[str]:
    method publish_terminal_notifications (line 338) | def publish_terminal_notifications(self, *, limit: int | None = None) ...

FILE: src/kimi_cli/background/models.py
  function is_terminal_status (line 15) | def is_terminal_status(status: TaskStatus) -> bool:
  class TaskSpec (line 19) | class TaskSpec(BaseModel):
  class TaskRuntime (line 40) | class TaskRuntime(BaseModel):
  class TaskControl (line 57) | class TaskControl(BaseModel):
  class TaskConsumerState (line 65) | class TaskConsumerState(BaseModel):
  class TaskView (line 72) | class TaskView(BaseModel):
  class TaskOutputChunk (line 81) | class TaskOutputChunk(BaseModel):

FILE: src/kimi_cli/background/store.py
  function _validate_task_id (line 22) | def _validate_task_id(task_id: str) -> None:
  class BackgroundTaskStore (line 27) | class BackgroundTaskStore:
    method __init__ (line 34) | def __init__(self, root: Path):
    method root (line 38) | def root(self) -> Path:
    method _ensure_root (line 41) | def _ensure_root(self) -> Path:
    method task_dir (line 46) | def task_dir(self, task_id: str) -> Path:
    method task_path (line 52) | def task_path(self, task_id: str) -> Path:
    method spec_path (line 56) | def spec_path(self, task_id: str) -> Path:
    method runtime_path (line 59) | def runtime_path(self, task_id: str) -> Path:
    method control_path (line 62) | def control_path(self, task_id: str) -> Path:
    method consumer_path (line 65) | def consumer_path(self, task_id: str) -> Path:
    method output_path (line 68) | def output_path(self, task_id: str) -> Path:
    method create_task (line 71) | def create_task(self, spec: TaskSpec) -> None:
    method list_task_ids (line 82) | def list_task_ids(self) -> list[str]:
    method write_spec (line 94) | def write_spec(self, spec: TaskSpec) -> None:
    method read_spec (line 97) | def read_spec(self, task_id: str) -> TaskSpec:
    method write_runtime (line 100) | def write_runtime(self, task_id: str, runtime: TaskRuntime) -> None:
    method read_runtime (line 103) | def read_runtime(self, task_id: str) -> TaskRuntime:
    method write_control (line 109) | def write_control(self, task_id: str, control: TaskControl) -> None:
    method read_control (line 112) | def read_control(self, task_id: str) -> TaskControl:
    method write_consumer (line 118) | def write_consumer(self, task_id: str, consumer: TaskConsumerState) ->...
    method read_consumer (line 121) | def read_consumer(self, task_id: str) -> TaskConsumerState:
    method merged_view (line 127) | def merged_view(self, task_id: str) -> TaskView:
    method list_views (line 135) | def list_views(self) -> list[TaskView]:
    method read_output (line 143) | def read_output(
    method tail_output (line 179) | def tail_output(self, task_id: str, max_bytes: int, max_lines: int) ->...

FILE: src/kimi_cli/background/summary.py
  function list_task_views (line 7) | def list_task_views(
  function format_task (line 19) | def format_task(view: TaskView, *, include_command: bool = False) -> str:
  function format_task_list (line 35) | def format_task_list(
  function build_active_task_snapshot (line 51) | def build_active_task_snapshot(manager: BackgroundTaskManager, *, limit:...

FILE: src/kimi_cli/background/worker.py
  function terminate_process_tree_windows (line 19) | def terminate_process_tree_windows(pid: int, *, force: bool) -> None:
  function run_background_task_worker (line 31) | async def run_background_task_worker(

FILE: src/kimi_cli/cli/__init__.py
  class Reload (line 11) | class Reload(Exception):
    method __init__ (line 14) | def __init__(self, session_id: str | None = None):
  class SwitchToWeb (line 19) | class SwitchToWeb(Exception):
    method __init__ (line 22) | def __init__(self, session_id: str | None = None):
  function _version_callback (line 42) | def _version_callback(value: bool) -> None:
  function kimi (line 51) | def kimi(
  function login (line 683) | def login(
  function logout (line 742) | def logout(
  function term (line 786) | def term(
  function acp (line 796) | def acp():
  function background_task_worker (line 804) | def background_task_worker(
  function web_worker (line 832) | def web_worker(session_id: str) -> None:

FILE: src/kimi_cli/cli/_lazy_group.py
  class LazySubcommandGroup (line 13) | class LazySubcommandGroup(typer.core.TyperGroup):
    method list_commands (line 33) | def list_commands(self, ctx: click.Context) -> list[str]:
    method get_command (line 40) | def get_command(self, ctx: click.Context, cmd_name: str) -> click.Comm...
    method format_help (line 55) | def format_help(self, ctx: click.Context, formatter: HelpFormatter) ->...
    method format_commands (line 193) | def format_commands(self, ctx: click.Context, formatter: HelpFormatter...

FILE: src/kimi_cli/cli/export.py
  function _find_session_by_id (line 15) | def _find_session_by_id(session_id: str) -> Path | None:
  function export (line 34) | def export(

FILE: src/kimi_cli/cli/info.py
  class InfoData (line 10) | class InfoData(TypedDict):
  function _collect_info (line 17) | def _collect_info() -> InfoData:
  function _emit_info (line 30) | def _emit_info(json_output: bool) -> None:
  function info (line 52) | def info(

FILE: src/kimi_cli/cli/mcp.py
  function get_global_mcp_config_file (line 10) | def get_global_mcp_config_file() -> Path:
  function _load_mcp_config (line 17) | def _load_mcp_config() -> dict[str, Any]:
  function _save_mcp_config (line 38) | def _save_mcp_config(config: dict[str, Any]) -> None:
  function _get_mcp_server (line 44) | def _get_mcp_server(name: str, *, require_remote: bool = False) -> dict[...
  function _parse_key_value_pairs (line 58) | def _parse_key_value_pairs(
  function mcp_add (line 98) | def mcp_add(
  function mcp_remove (line 197) | def mcp_remove(
  function _has_oauth_tokens (line 211) | def _has_oauth_tokens(server_url: str) -> bool:
  function mcp_list (line 229) | def mcp_list():
  function mcp_auth (line 258) | def mcp_auth(
  function mcp_reset_auth (line 292) | def mcp_reset_auth(
  function mcp_test (line 316) | def mcp_test(

FILE: src/kimi_cli/cli/plugin.py
  function _parse_git_url (line 15) | def _parse_git_url(target: str) -> tuple[str, str | None, str | None]:
  function _resolve_source (line 59) | def _resolve_source(target: str) -> tuple[Path, Path | None]:
  function install_cmd (line 187) | def install_cmd(
  function list_cmd (line 244) | def list_cmd() -> None:
  function remove_cmd (line 259) | def remove_cmd(
  function info_cmd (line 275) | def info_cmd(

FILE: src/kimi_cli/cli/toad.py
  function _default_acp_command (line 11) | def _default_acp_command() -> list[str]:
  function _default_toad_command (line 26) | def _default_toad_command() -> list[str]:
  function _extract_project_dir (line 39) | def _extract_project_dir(extra_args: list[str]) -> Path | None:
  function run_term (line 61) | def run_term(ctx: typer.Context) -> None:

FILE: src/kimi_cli/cli/vis.py
  function vis (line 11) | def vis(

FILE: src/kimi_cli/cli/web.py
  function web (line 11) | def web(

FILE: src/kimi_cli/config.py
  class OAuthRef (line 25) | class OAuthRef(BaseModel):
  class LLMProvider (line 34) | class LLMProvider(BaseModel):
    method dump_secret (line 51) | def dump_secret(self, v: SecretStr):
  class LLMModel (line 55) | class LLMModel(BaseModel):
  class LoopControl (line 68) | class LoopControl(BaseModel):
  class BackgroundConfig (line 91) | class BackgroundConfig(BaseModel):
  class NotificationConfig (line 108) | class NotificationConfig(BaseModel):
  class MoonshotSearchConfig (line 114) | class MoonshotSearchConfig(BaseModel):
    method dump_secret (line 127) | def dump_secret(self, v: SecretStr):
  class MoonshotFetchConfig (line 131) | class MoonshotFetchConfig(BaseModel):
    method dump_secret (line 144) | def dump_secret(self, v: SecretStr):
  class Services (line 148) | class Services(BaseModel):
  class MCPClientConfig (line 157) | class MCPClientConfig(BaseModel):
  class MCPConfig (line 164) | class MCPConfig(BaseModel):
  class Config (line 172) | class Config(BaseModel):
    method validate_model (line 207) | def validate_model(self) -> Self:
  function get_config_file (line 216) | def get_config_file() -> Path:
  function get_default_config (line 221) | def get_default_config() -> Config:
  function load_config (line 231) | def load_config(config_file: Path | None = None) -> Config:
  function load_config_from_string (line 282) | def load_config_from_string(config_string: str) -> Config:
  function save_config (line 322) | def save_config(config: Config, config_file: Path | None = None):
  function _migrate_json_config_to_toml (line 341) | def _migrate_json_config_to_toml() -> None:

FILE: src/kimi_cli/constant.py
  function get_version (line 14) | def get_version() -> str:
  function get_user_agent (line 21) | def get_user_agent() -> str:
  function __getattr__ (line 25) | def __getattr__(name: str) -> str:

FILE: src/kimi_cli/exception.py
  class KimiCLIException (line 4) | class KimiCLIException(Exception):
  class ConfigError (line 10) | class ConfigError(KimiCLIException, ValueError):
  class AgentSpecError (line 16) | class AgentSpecError(KimiCLIException, ValueError):
  class InvalidToolError (line 22) | class InvalidToolError(KimiCLIException, ValueError):
  class SystemPromptTemplateError (line 28) | class SystemPromptTemplateError(KimiCLIException, ValueError):
  class MCPConfigError (line 34) | class MCPConfigError(KimiCLIException, ValueError):
  class MCPRuntimeError (line 40) | class MCPRuntimeError(KimiCLIException, RuntimeError):

FILE: src/kimi_cli/llm.py
  class LLM (line 36) | class LLM:
    method model_name (line 44) | def model_name(self) -> str:
  function model_display_name (line 48) | def model_display_name(model_name: str | None) -> str:
  function augment_provider_with_env_vars (line 56) | def augment_provider_with_env_vars(provider: LLMProvider, model: LLMMode...
  function _kimi_default_headers (line 97) | def _kimi_default_headers(provider: LLMProvider, oauth: OAuthManager | N...
  function create_llm (line 106) | def create_llm(
  function derive_model_capabilities (line 240) | def derive_model_capabilities(model: LLMModel) -> set[ModelCapability]:
  function _load_scripted_echo_scripts (line 251) | def _load_scripted_echo_scripts() -> list[str]:

FILE: src/kimi_cli/metadata.py
  function get_metadata_file (line 17) | def get_metadata_file() -> Path:
  class WorkDirMeta (line 21) | class WorkDirMeta(BaseModel):
    method sessions_dir (line 34) | def sessions_dir(self) -> Path:
  class Metadata (line 43) | class Metadata(BaseModel):
    method get_work_dir_meta (line 51) | def get_work_dir_meta(self, path: KaosPath) -> WorkDirMeta | None:
    method new_work_dir_meta (line 58) | def new_work_dir_meta(self, path: KaosPath) -> WorkDirMeta:
  function load_metadata (line 65) | def load_metadata() -> Metadata:
  function save_metadata (line 76) | def save_metadata(metadata: Metadata):

FILE: src/kimi_cli/notifications/llm.py
  function build_notification_message (line 19) | def build_notification_message(view: NotificationView, runtime: Runtime)...
  function extract_notification_ids (line 60) | def extract_notification_ids(history: Sequence[Message]) -> set[str]:
  function is_notification_message (line 73) | def is_notification_message(message: Message) -> bool:

FILE: src/kimi_cli/notifications/manager.py
  class NotificationManager (line 20) | class NotificationManager:
    method __init__ (line 21) | def __init__(self, root: Path, config: NotificationConfig) -> None:
    method store (line 26) | def store(self) -> NotificationStore:
    method new_id (line 29) | def new_id(self) -> str:
    method _initial_delivery (line 32) | def _initial_delivery(self, event: NotificationEvent) -> NotificationD...
    method find_by_dedupe_key (line 35) | def find_by_dedupe_key(self, dedupe_key: str) -> NotificationView | None:
    method publish (line 41) | def publish(self, event: NotificationEvent) -> NotificationView:
    method recover (line 50) | def recover(self) -> None:
    method claim_for_sink (line 67) | def claim_for_sink(self, sink: str, *, limit: int = 8) -> list[Notific...
    method deliver_pending (line 87) | async def deliver_pending(
    method ack (line 120) | def ack(self, sink: str, notification_id: str) -> NotificationView:
    method ack_ids (line 132) | def ack_ids(self, sink: str, notification_ids: set[str]) -> None:

FILE: src/kimi_cli/notifications/models.py
  class NotificationEvent (line 14) | class NotificationEvent(BaseModel):
  class NotificationSinkState (line 32) | class NotificationSinkState(BaseModel):
  class NotificationDelivery (line 40) | class NotificationDelivery(BaseModel):
  class NotificationView (line 46) | class NotificationView(BaseModel):

FILE: src/kimi_cli/notifications/notifier.py
  class NotificationWatcher (line 10) | class NotificationWatcher:
    method __init__ (line 11) | def __init__(
    method poll_once (line 26) | async def poll_once(self) -> list[NotificationView]:
    method run_forever (line 33) | async def run_forever(self) -> None:

FILE: src/kimi_cli/notifications/store.py
  function _validate_notification_id (line 13) | def _validate_notification_id(notification_id: str) -> None:
  class NotificationStore (line 18) | class NotificationStore:
    method __init__ (line 22) | def __init__(self, root: Path):
    method root (line 26) | def root(self) -> Path:
    method _ensure_root (line 29) | def _ensure_root(self) -> Path:
    method notification_dir (line 34) | def notification_dir(self, notification_id: str) -> Path:
    method notification_path (line 40) | def notification_path(self, notification_id: str) -> Path:
    method event_path (line 44) | def event_path(self, notification_id: str) -> Path:
    method delivery_path (line 47) | def delivery_path(self, notification_id: str) -> Path:
    method create_notification (line 50) | def create_notification(
    method list_notification_ids (line 59) | def list_notification_ids(self) -> list[str]:
    method read_event (line 71) | def read_event(self, notification_id: str) -> NotificationEvent:
    method write_event (line 76) | def write_event(self, event: NotificationEvent) -> None:
    method read_delivery (line 79) | def read_delivery(self, notification_id: str) -> NotificationDelivery:
    method write_delivery (line 85) | def write_delivery(self, notification_id: str, delivery: NotificationD...
    method merged_view (line 88) | def merged_view(self, notification_id: str) -> NotificationView:
    method list_views (line 94) | def list_views(self) -> list[NotificationView]:

FILE: src/kimi_cli/notifications/wire.py
  function to_wire_notification (line 8) | def to_wire_notification(view: NotificationView) -> Notification:

FILE: src/kimi_cli/plugin/__init__.py
  class PluginError (line 10) | class PluginError(Exception):
  class PluginRuntime (line 14) | class PluginRuntime(BaseModel):
  class PluginToolSpec (line 21) | class PluginToolSpec(BaseModel):
  class PluginSpec (line 30) | class PluginSpec(BaseModel):
  function parse_plugin_json (line 47) | def parse_plugin_json(path: Path) -> PluginSpec:
  function inject_config (line 67) | def inject_config(plugin_dir: Path, spec: PluginSpec, values: dict[str, ...
  function write_runtime (line 100) | def write_runtime(plugin_dir: Path, runtime: PluginRuntime) -> None:
  function _set_nested (line 114) | def _set_nested(obj: dict[str, Any], dotted_path: str, value: object) ->...

FILE: src/kimi_cli/plugin/manager.py
  function get_plugins_dir (line 26) | def get_plugins_dir() -> Path:
  function collect_host_values (line 31) | def collect_host_values(config: Config, oauth: OAuthManager) -> dict[str...
  function _validate_name (line 54) | def _validate_name(name: str, plugins_dir: Path) -> Path:
  function install_plugin (line 62) | def install_plugin(
  function refresh_plugin_configs (line 111) | def refresh_plugin_configs(plugins_dir: Path, host_values: dict[str, str...
  function list_plugins (line 132) | def list_plugins(plugins_dir: Path) -> list[PluginSpec]:
  function remove_plugin (line 148) | def remove_plugin(name: str, plugins_dir: Path) -> None:

FILE: src/kimi_cli/plugin/tool.py
  function _get_host_values (line 24) | def _get_host_values(config: Config) -> dict[str, str]:
  class PluginTool (line 37) | class PluginTool(CallableTool):
    method __init__ (line 46) | def __init__(
    method _build_env (line 68) | def _build_env(self) -> dict[str, str]:
    method __call__ (line 80) | async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue:
  function load_plugin_tools (line 133) | def load_plugin_tools(

FILE: src/kimi_cli/session.py
  class Session (line 23) | class Session:
    method dir (line 49) | def dir(self) -> Path:
    method is_empty (line 55) | def is_empty(self) -> bool:
    method save_state (line 75) | def save_state(self) -> None:
    method delete (line 79) | async def delete(self) -> None:
    method refresh (line 86) | async def refresh(self) -> None:
    method create (line 107) | async def create(
    method find (line 161) | async def find(work_dir: KaosPath, session_id: str) -> Session | None:
    method list (line 204) | async def list(work_dir: KaosPath) -> builtins.list[Session]:
    method continue_ (line 255) | async def continue_(work_dir: KaosPath) -> Session | None:
  function _migrate_session_context_file (line 276) | def _migrate_session_context_file(work_dir_meta: WorkDirMeta, session_id...

FILE: src/kimi_cli/session_state.py
  class ApprovalStateData (line 14) | class ApprovalStateData(BaseModel):
  class DynamicSubagentSpec (line 19) | class DynamicSubagentSpec(BaseModel):
  function _default_dynamic_subagents (line 24) | def _default_dynamic_subagents() -> list[DynamicSubagentSpec]:
  class SessionState (line 28) | class SessionState(BaseModel):
  function load_session_state (line 38) | def load_session_state(session_dir: Path) -> SessionState:
  function save_session_state (line 50) | def save_session_state(state: SessionState, session_dir: Path) -> None:

FILE: src/kimi_cli/share.py
  function get_share_dir (line 7) | def get_share_dir() -> Path:

FILE: src/kimi_cli/skill/__init__.py
  function get_builtin_skills_dir (line 23) | def get_builtin_skills_dir() -> Path:
  function get_user_skills_dir_candidates (line 30) | def get_user_skills_dir_candidates() -> tuple[KaosPath, ...]:
  function get_project_skills_dir_candidates (line 43) | def get_project_skills_dir_candidates(work_dir: KaosPath) -> tuple[KaosP...
  function _supports_builtin_skills (line 55) | def _supports_builtin_skills() -> bool:
  function find_first_existing_dir (line 61) | async def find_first_existing_dir(candidates: Iterable[KaosPath]) -> Kao...
  function find_user_skills_dir (line 71) | async def find_user_skills_dir() -> KaosPath | None:
  function find_project_skills_dir (line 78) | async def find_project_skills_dir(work_dir: KaosPath) -> KaosPath | None:
  function resolve_skills_roots (line 85) | async def resolve_skills_roots(
  function normalize_skill_name (line 115) | def normalize_skill_name(name: str) -> str:
  function index_skills (line 120) | def index_skills(skills: Iterable[Skill]) -> dict[str, Skill]:
  function discover_skills_from_roots (line 125) | async def discover_skills_from_roots(skills_dirs: Iterable[KaosPath]) ->...
  function read_skill_text (line 136) | async def read_skill_text(skill: Skill) -> str | None:
  class Skill (line 149) | class Skill(BaseModel):
    method skill_md_file (line 161) | def skill_md_file(self) -> KaosPath:
  function discover_skills (line 166) | async def discover_skills(skills_dir: KaosPath) -> list[Skill]:
  function parse_skill_text (line 199) | def parse_skill_text(content: str, *, dir_path: KaosPath) -> Skill:
  function _parse_flow_from_skill (line 228) | def _parse_flow_from_skill(content: str) -> Flow:
  function _parse_flow_block (line 237) | def _parse_flow_block(parser: Callable[[str], Flow], code: str) -> Flow:
  function _iter_fenced_codeblocks (line 244) | def _iter_fenced_codeblocks(content: str) -> Iterator[tuple[str, str]]:
  function _normalize_code_lang (line 273) | def _normalize_code_lang(info: str) -> str:
  function _parse_fence_open (line 282) | def _parse_fence_open(line: str) -> tuple[str, str, str] | None:
  function _is_fence_close (line 299) | def _is_fence_close(line: str, fence_char: str, fence_len: int) -> bool:

FILE: src/kimi_cli/skill/flow/__init__.py
  class FlowError (line 12) | class FlowError(ValueError):
  class FlowParseError (line 16) | class FlowParseError(FlowError):
  class FlowValidationError (line 20) | class FlowValidationError(FlowError):
  class FlowNode (line 25) | class FlowNode:
  class FlowEdge (line 32) | class FlowEdge:
  class Flow (line 39) | class Flow:
  function parse_choice (line 49) | def parse_choice(text: str) -> str | None:
  function validate_flow (line 56) | def validate_flow(

FILE: src/kimi_cli/skill/flow/d2.py
  class _NodeDef (line 49) | class _NodeDef:
  function parse_d2_flowchart (line 54) | def parse_d2_flowchart(text: str) -> Flow:
  function _normalize_markdown_blocks (line 75) | def _normalize_markdown_blocks(text: str) -> str:
  function _strip_unquoted_comment (line 132) | def _strip_unquoted_comment(text: str) -> str:
  function _dedent_block (line 154) | def _dedent_block(lines: list[str]) -> list[str]:
  function _escape_quoted_line (line 168) | def _escape_quoted_line(line: str) -> str:
  function _iter_top_level_statements (line 172) | def _iter_top_level_statements(text: str) -> Iterable[tuple[int, str]]:
  function _has_unquoted_token (line 258) | def _has_unquoted_token(text: str, token: str) -> bool:
  function _parse_edge_statement (line 263) | def _parse_edge_statement(
  function _parse_node_statement (line 301) | def _parse_node_statement(statement: str, line_no: int, nodes: dict[str,...
  function _parse_node_id (line 316) | def _parse_node_id(text: str, line_no: int, *, allow_inline_label: bool)...
  function _is_property_path (line 328) | def _is_property_path(node_id: str) -> bool:
  function _parse_label (line 338) | def _parse_label(text: str, line_no: int) -> str:
  function _parse_quoted_label (line 347) | def _parse_quoted_label(text: str, line_no: int) -> str:
  function _split_on_token (line 373) | def _split_on_token(text: str, token: str) -> list[str]:
  function _split_unquoted_once (line 405) | def _split_unquoted_once(text: str, token: str) -> tuple[str, str | None]:
  function _add_node (line 427) | def _add_node(
  function _infer_decision_nodes (line 465) | def _infer_decision_nodes(
  function _line_error (line 481) | def _line_error(line_no: int, message: str) -> str:

FILE: src/kimi_cli/skill/flow/mermaid.py
  class _NodeSpec (line 17) | class _NodeSpec:
  class _NodeDef (line 23) | class _NodeDef:
  function parse_mermaid_flowchart (line 41) | def parse_mermaid_flowchart(text: str) -> Flow:
  function _try_parse_edge_line (line 78) | def _try_parse_edge_line(line: str, line_no: int) -> tuple[_NodeSpec, st...
  function _parse_node_token (line 105) | def _parse_node_token(line: str, idx: int, line_no: int) -> tuple[_NodeS...
  function _parse_label (line 121) | def _parse_label(line: str, idx: int, close_char: str, line_no: int) -> ...
  function _skip_ws (line 160) | def _skip_ws(line: str, idx: int) -> int:
  function _add_node (line 166) | def _add_node(nodes: dict[str, _NodeDef], spec: _NodeSpec, line_no: int)...
  function _line_error (line 199) | def _line_error(line_no: int, message: str) -> str:
  function _strip_comment (line 203) | def _strip_comment(line: str) -> str:
  function _is_style_line (line 209) | def _is_style_line(line: str) -> bool:
  function _strip_style_tokens (line 226) | def _strip_style_tokens(line: str) -> str:
  function _try_parse_node_line (line 230) | def _try_parse_node_line(line: str, line_no: int) -> _NodeSpec | None:
  function _normalize_edge_line (line 238) | def _normalize_edge_line(line: str) -> tuple[str, str | None]:
  function _infer_decision_nodes (line 253) | def _infer_decision_nodes(

FILE: src/kimi_cli/soul/__init__.py
  class LLMNotSet (line 22) | class LLMNotSet(Exception):
    method __init__ (line 25) | def __init__(self) -> None:
  class LLMNotSupported (line 29) | class LLMNotSupported(Exception):
    method __init__ (line 32) | def __init__(self, llm: LLM, capabilities: list[ModelCapability]):
  class MaxStepsReached (line 42) | class MaxStepsReached(Exception):
    method __init__ (line 48) | def __init__(self, n_steps: int):
  function format_token_count (line 53) | def format_token_count(n: int) -> str:
  function format_context_status (line 70) | def format_context_status(
  class StatusSnapshot (line 85) | class StatusSnapshot:
  class Soul (line 101) | class Soul(Protocol):
    method name (line 103) | def name(self) -> str:
    method model_name (line 108) | def model_name(self) -> str:
    method model_capabilities (line 113) | def model_capabilities(self) -> set[ModelCapability] | None:
    method thinking (line 118) | def thinking(self) -> bool | None:
    method status (line 126) | def status(self) -> StatusSnapshot:
    method available_slash_commands (line 131) | def available_slash_commands(self) -> list[SlashCommand[Any]]:
    method run (line 135) | async def run(self, user_input: str | list[ContentPart]):
  class RunCancelled (line 157) | class RunCancelled(Exception):
  function run_soul (line 161) | async def run_soul(
  function get_wire_or_none (line 238) | def get_wire_or_none() -> Wire | None:
  function wire_send (line 246) | def wire_send(msg: WireMessage) -> None:
  function _pump_notifications_to_wire (line 257) | async def _pump_notifications_to_wire(runtime: Runtime | None, wire: Wir...
  function _deliver_notifications_to_wire_once (line 268) | async def _deliver_notifications_to_wire_once(runtime: Runtime | None, w...

FILE: src/kimi_cli/soul/agent.py
  class BuiltinSystemPromptArgs (line 37) | class BuiltinSystemPromptArgs:
  function load_agents_md (line 54) | async def load_agents_md(work_dir: KaosPath) -> str | None:
  class Runtime (line 68) | class Runtime:
    method create (line 87) | async def create(
    method copy_for_fixed_subagent (line 196) | def copy_for_fixed_subagent(self) -> Runtime:
    method copy_for_dynamic_subagent (line 216) | def copy_for_dynamic_subagent(self) -> Runtime:
  class Agent (line 238) | class Agent:
  class LaborMarket (line 248) | class LaborMarket:
    method __init__ (line 249) | def __init__(self):
    method subagents (line 255) | def subagents(self) -> Mapping[str, Agent]:
    method add_fixed_subagent (line 259) | def add_fixed_subagent(self, name: str, agent: Agent, description: str):
    method add_dynamic_subagent (line 264) | def add_dynamic_subagent(self, name: str, agent: Agent):
  function load_agent (line 269) | async def load_agent(
  function _load_system_prompt (line 383) | def _load_system_prompt(

FILE: src/kimi_cli/soul/approval.py
  class Request (line 16) | class Request:
  class ApprovalState (line 28) | class ApprovalState:
    method __init__ (line 29) | def __init__(
    method notify_change (line 40) | def notify_change(self) -> None:
  class Approval (line 45) | class Approval:
    method __init__ (line 46) | def __init__(self, yolo: bool = False, *, state: ApprovalState | None ...
    method share (line 51) | def share(self) -> Approval:
    method set_yolo (line 55) | def set_yolo(self, yolo: bool) -> None:
    method is_yolo (line 59) | def is_yolo(self) -> bool:
    method request (line 62) | async def request(
    method fetch_request (line 114) | async def fetch_request(self) -> Request:
    method resolve_request (line 130) | def resolve_request(self, request_id: str, response: Response) -> None:

FILE: src/kimi_cli/soul/compaction.py
  class CompactionResult (line 18) | class CompactionResult(NamedTuple):
    method estimated_token_count (line 23) | def estimated_token_count(self) -> int:
  function estimate_text_tokens (line 44) | def estimate_text_tokens(messages: Sequence[Message]) -> int:
  function should_auto_compact (line 56) | def should_auto_compact(
  class Compaction (line 76) | class Compaction(Protocol):
    method compact (line 77) | async def compact(
  function type_check (line 99) | def type_check(simple: SimpleCompaction):
  class SimpleCompaction (line 103) | class SimpleCompaction:
    method __init__ (line 104) | def __init__(self, max_preserved_messages: int = 2) -> None:
    method compact (line 107) | async def compact(
    class PrepareResult (line 141) | class PrepareResult(NamedTuple):
    method prepare (line 145) | def prepare(

FILE: src/kimi_cli/soul/context.py
  class Context (line 16) | class Context:
    method __init__ (line 17) | def __init__(self, file_backend: Path):
    method restore (line 25) | async def restore(self) -> bool:
    method history (line 57) | def history(self) -> Sequence[Message]:
    method token_count (line 61) | def token_count(self) -> int:
    method n_checkpoints (line 65) | def n_checkpoints(self) -> int:
    method system_prompt (line 69) | def system_prompt(self) -> str | None:
    method file_backend (line 73) | def file_backend(self) -> Path:
    method write_system_prompt (line 76) | async def write_system_prompt(self, prompt: str) -> None:
    method checkpoint (line 103) | async def checkpoint(self, add_user_message: bool):
    method revert_to (line 115) | async def revert_to(self, checkpoint_id: int):
    method clear (line 172) | async def clear(self):
    method append_message (line 201) | async def append_message(self, message: Message | Sequence[Message]):
    method update_token_count (line 210) | async def update_token_count(self, token_count: int):

FILE: src/kimi_cli/soul/denwarenji.py
  class DMail (line 6) | class DMail(BaseModel):
  class DenwaRenjiError (line 12) | class DenwaRenjiError(Exception):
  class DenwaRenji (line 16) | class DenwaRenji:
    method __init__ (line 17) | def __init__(self):
    method send_dmail (line 21) | def send_dmail(self, dmail: DMail):
    method set_n_checkpoints (line 31) | def set_n_checkpoints(self, n_checkpoints: int):
    method fetch_pending_dmail (line 35) | def fetch_pending_dmail(self) -> DMail | None:

FILE: src/kimi_cli/soul/dynamic_injection.py
  class DynamicInjection (line 17) | class DynamicInjection:
  class DynamicInjectionProvider (line 24) | class DynamicInjectionProvider(ABC):
    method get_injections (line 33) | async def get_injections(
  function normalize_history (line 40) | def normalize_history(history: Sequence[Message]) -> list[Message]:

FILE: src/kimi_cli/soul/dynamic_injections/plan_mode.py
  class PlanModeInjectionProvider (line 19) | class PlanModeInjectionProvider(DynamicInjectionProvider):
    method __init__ (line 27) | def __init__(self) -> None:
    method get_injections (line 30) | async def get_injections(
  function _has_plan_reminder (line 95) | def _has_plan_reminder(msg: Message) -> bool:
  function _full_reminder (line 111) | def _full_reminder(
  function _sparse_reminder (line 187) | def _sparse_reminder(plan_file_path: str | None = None) -> str:
  function _reentry_reminder (line 210) | def _reentry_reminder(plan_file_path: str | None = None) -> str:

FILE: src/kimi_cli/soul/kimisoul.py
  function type_check (line 83) | def type_check(soul: KimiSoul):
  class StepOutcome (line 96) | class StepOutcome:
  class TurnOutcome (line 105) | class TurnOutcome:
  class KimiSoul (line 111) | class KimiSoul:
    method __init__ (line 114) | def __init__(
    method name (line 166) | def name(self) -> str:
    method model_name (line 170) | def model_name(self) -> str:
    method model_capabilities (line 174) | def model_capabilities(self) -> set[ModelCapability] | None:
    method plan_mode (line 180) | def plan_mode(self) -> bool:
    method add_injection_provider (line 184) | def add_injection_provider(self, provider: DynamicInjectionProvider) -...
    method _collect_injections (line 188) | async def _collect_injections(self) -> list[DynamicInjection]:
    method _bind_plan_mode_tools (line 203) | def _bind_plan_mode_tools(self) -> None:
    method _ensure_plan_session_id (line 241) | def _ensure_plan_session_id(self) -> None:
    method _set_plan_mode (line 255) | def _set_plan_mode(self, enabled: bool, *, source: Literal["manual", "...
    method get_plan_file_path (line 273) | def get_plan_file_path(self) -> Path | None:
    method read_current_plan (line 281) | def read_current_plan(self) -> str | None:
    method clear_current_plan (line 289) | def clear_current_plan(self) -> None:
    method toggle_plan_mode (line 295) | async def toggle_plan_mode(self) -> bool:
    method toggle_plan_mode_from_manual (line 304) | async def toggle_plan_mode_from_manual(self) -> bool:
    method set_plan_mode_from_manual (line 308) | async def set_plan_mode_from_manual(self, enabled: bool) -> bool:
    method consume_pending_plan_activation_injection (line 316) | def consume_pending_plan_activation_injection(self) -> bool:
    method thinking (line 324) | def thinking(self) -> bool | None:
    method status (line 333) | def status(self) -> StatusSnapshot:
    method agent (line 346) | def agent(self) -> Agent:
    method runtime (line 350) | def runtime(self) -> Runtime:
    method context (line 354) | def context(self) -> Context:
    method _context_usage (line 358) | def _context_usage(self) -> float:
    method wire_file (line 364) | def wire_file(self) -> WireFile:
    method _mcp_status_snapshot (line 367) | def _mcp_status_snapshot(self):
    method start_background_mcp_loading (line 372) | async def start_background_mcp_loading(self) -> bool:
    method wait_for_background_mcp_loading (line 378) | async def wait_for_background_mcp_loading(self) -> None:
    method _checkpoint (line 384) | async def _checkpoint(self):
    method steer (line 387) | def steer(self, content: str | list[ContentPart]) -> None:
    method _consume_pending_steers (line 391) | async def _consume_pending_steers(self) -> bool:
    method _inject_steer (line 404) | async def _inject_steer(self, content: str | list[ContentPart]) -> None:
    method available_slash_commands (line 418) | def available_slash_commands(self) -> list[SlashCommand[Any]]:
    method run (line 421) | async def run(self, user_input: str | list[ContentPart]):
    method _turn (line 449) | async def _turn(self, user_message: Message) -> TurnOutcome:
    method _build_slash_commands (line 461) | def _build_slash_commands(self) -> list[SlashCommand[Any]]:
    method _index_slash_commands (line 512) | def _index_slash_commands(
    method _find_slash_command (line 522) | def _find_slash_command(self, name: str) -> SlashCommand[Any] | None:
    method _make_skill_runner (line 525) | def _make_skill_runner(self, skill: Skill) -> Callable[[KimiSoul, str]...
    method _agent_loop (line 541) | async def _agent_loop(self) -> TurnOutcome:
    method _step (line 646) | async def _step(self) -> StepOutcome | None:
    method _grow_context (line 765) | async def _grow_context(self, result: StepResult, tool_results: list[T...
    method compact_context (line 788) | async def compact_context(self, custom_instruction: str = "") -> None:
    method _is_retryable_error (line 850) | def _is_retryable_error(exception: BaseException) -> bool:
    method _run_with_connection_recovery (line 862) | async def _run_with_connection_recovery(
    method _retry_log (line 897) | def _retry_log(name: str, retry_state: RetryCallState):
  class BackToTheFuture (line 908) | class BackToTheFuture(Exception):
    method __init__ (line 914) | def __init__(self, checkpoint_id: int, messages: Sequence[Message]):
  class FlowRunner (line 919) | class FlowRunner:
    method __init__ (line 920) | def __init__(
    method ralph_loop (line 932) | def ralph_loop(
    method run (line 971) | async def run(self, soul: KimiSoul, args: str) -> None:
    method _execute_flow_node (line 1007) | async def _execute_flow_node(
    method _build_flow_prompt (line 1055) | def _build_flow_prompt(node: FlowNode, edges: list[FlowEdge]) -> str |...
    method _match_flow_edge (line 1075) | def _match_flow_edge(edges: list[FlowEdge], choice: str | None) -> str...
    method _flow_turn (line 1084) | async def _flow_turn(

FILE: src/kimi_cli/soul/message.py
  function system (line 19) | def system(message: str) -> ContentPart:
  function system_reminder (line 23) | def system_reminder(message: str) -> TextPart:
  function is_system_reminder_message (line 27) | def is_system_reminder_message(message: Message) -> bool:
  function tool_result_to_message (line 35) | def tool_result_to_message(tool_result: ToolResult) -> Message:
  function _output_to_content_parts (line 61) | def _output_to_content_parts(
  function check_message (line 76) | def check_message(

FILE: src/kimi_cli/soul/slash.py
  function init (line 37) | async def init(soul: KimiSoul, args: str):
  function compact (line 56) | async def compact(soul: KimiSoul, args: str):
  function clear (line 76) | async def clear(soul: KimiSoul, args: str):
  function yolo (line 93) | async def yolo(soul: KimiSoul, args: str):
  function plan (line 104) | async def plan(soul: KimiSoul, args: str):
  function add_dir (line 145) | async def add_dir(soul: KimiSoul, args: str):
  function export (line 220) | async def export(soul: KimiSoul, args: str):
  function import_context (line 248) | async def import_context(soul: KimiSoul, args: str):

FILE: src/kimi_cli/soul/toolset.py
  function get_current_tool_call_or_none (line 56) | def get_current_tool_call_or_none() -> ToolCall | None:
  function type_check (line 69) | def type_check(kimi_toolset: KimiToolset):
  class KimiToolset (line 73) | class KimiToolset:
    method __init__ (line 74) | def __init__(self) -> None:
    method add (line 81) | def add(self, tool: ToolType) -> None:
    method hide (line 84) | def hide(self, tool_name: str) -> bool:
    method unhide (line 91) | def unhide(self, tool_name: str) -> None:
    method find (line 96) | def find(self, tool_name_or_type: str) -> ToolType | None: ...
    method find (line 98) | def find[T: ToolType](self, tool_name_or_type: type[T]) -> T | None: ...
    method find (line 99) | def find(self, tool_name_or_type: str | type[ToolType]) -> ToolType | ...
    method tools (line 109) | def tools(self) -> list[Tool]:
    method handle (line 114) | def handle(self, tool_call: ToolCall) -> HandleResult:
    method register_external_tool (line 143) | def register_external_tool(
    method mcp_servers (line 165) | def mcp_servers(self) -> dict[str, MCPServerInfo]:
    method mcp_status_snapshot (line 169) | def mcp_status_snapshot(self) -> MCPStatusSnapshot | None:
    method defer_mcp_tool_loading (line 190) | def defer_mcp_tool_loading(self, mcp_configs: list[MCPConfig], runtime...
    method has_deferred_mcp_tools (line 194) | def has_deferred_mcp_tools(self) -> bool:
    method start_deferred_mcp_tool_loading (line 198) | async def start_deferred_mcp_tool_loading(self) -> bool:
    method load_tools (line 211) | def load_tools(self, tool_paths: list[str], dependencies: dict[type[An...
    method _load_tool (line 238) | def _load_tool(tool_path: str, dependencies: dict[type[Any], Any]) -> ...
    method load_mcp_tools (line 262) | async def load_mcp_tools(
    method has_pending_mcp_tools (line 386) | def has_pending_mcp_tools(self) -> bool:
    method wait_for_mcp_tools (line 390) | async def wait_for_mcp_tools(self) -> None:
    method cleanup (line 401) | async def cleanup(self) -> None:
  class MCPServerInfo (line 413) | class MCPServerInfo:
  class MCPTool (line 419) | class MCPTool[T: ClientTransport](CallableTool):
    method __init__ (line 420) | def __init__(
    method __call__ (line 444) | async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue:
  class WireExternalTool (line 472) | class WireExternalTool(CallableTool):
    method __init__ (line 473) | def __init__(self, *, name: str, description: str, parameters: dict[st...
    method __call__ (line 480) | async def __call__(self, *args: Any, **kwargs: Any) -> ToolReturnValue:
  function convert_mcp_tool_result (line 514) | def convert_mcp_tool_result(result: CallToolResult) -> ToolReturnValue:

FILE: src/kimi_cli/tools/__init__.py
  class SkipThisTool (line 11) | class SkipThisTool(Exception):
  function extract_key_argument (line 17) | def extract_key_argument(json_content: str | streamingjson.Lexer, tool_n...
  function _normalize_path (line 105) | def _normalize_path(path: str) -> str:

FILE: src/kimi_cli/tools/ask_user/__init__.py
  class QuestionOptionParam (line 24) | class QuestionOptionParam(BaseModel):
  class QuestionParam (line 34) | class QuestionParam(BaseModel):
  class Params (line 53) | class Params(BaseModel):
  class AskUserQuestion (line 61) | class AskUserQuestion(CallableTool2[Params]):
    method __call__ (line 67) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/background/__init__.py
  function _ensure_root (line 18) | def _ensure_root(runtime: Runtime) -> ToolError | None:
  function _task_display (line 27) | def _task_display(runtime: Runtime, task_id: str) -> BackgroundTaskDispl...
  function _format_task_output (line 37) | def _format_task_output(
  class TaskOutputParams (line 106) | class TaskOutputParams(BaseModel):
  class TaskStopParams (line 120) | class TaskStopParams(BaseModel):
  class TaskListParams (line 128) | class TaskListParams(BaseModel):
  class TaskList (line 141) | class TaskList(CallableTool2[TaskListParams]):
    method __init__ (line 146) | def __init__(self, runtime: Runtime):
    method __call__ (line 151) | async def __call__(self, params: TaskListParams) -> ToolReturnValue:
  class TaskOutput (line 177) | class TaskOutput(CallableTool2[TaskOutputParams]):
    method __init__ (line 182) | def __init__(self, runtime: Runtime):
    method _render_output_preview (line 186) | def _render_output_preview(
    method __call__ (line 215) | async def __call__(self, params: TaskOutputParams) -> ToolReturnValue:
  class TaskStop (line 276) | class TaskStop(CallableTool2[TaskStopParams]):
    method __init__ (line 281) | def __init__(self, runtime: Runtime, approval: Approval):
    method __call__ (line 287) | async def __call__(self, params: TaskStopParams) -> ToolReturnValue:

FILE: src/kimi_cli/tools/display.py
  class DiffDisplayBlock (line 7) | class DiffDisplayBlock(DisplayBlock):
  class TodoDisplayItem (line 16) | class TodoDisplayItem(BaseModel):
  class TodoDisplayBlock (line 21) | class TodoDisplayBlock(DisplayBlock):
  class ShellDisplayBlock (line 28) | class ShellDisplayBlock(DisplayBlock):
  class BackgroundTaskDisplayBlock (line 36) | class BackgroundTaskDisplayBlock(DisplayBlock):

FILE: src/kimi_cli/tools/dmail/__init__.py
  class SendDMail (line 12) | class SendDMail(CallableTool2[DMail]):
    method __init__ (line 17) | def __init__(self, denwa_renji: DenwaRenji) -> None:
    method __call__ (line 22) | async def __call__(self, params: DMail) -> ToolReturnValue:

FILE: src/kimi_cli/tools/file/__init__.py
  class FileOpsWindow (line 4) | class FileOpsWindow:
  class FileActions (line 10) | class FileActions(StrEnum):

FILE: src/kimi_cli/tools/file/glob.py
  class Params (line 17) | class Params(BaseModel):
  class Glob (line 31) | class Glob(CallableTool2[Params]):
    method __init__ (line 41) | def __init__(self, runtime: Runtime) -> None:
    method _validate_pattern (line 46) | async def _validate_pattern(self, pattern: str) -> ToolError | None:
    method _validate_directory (line 63) | async def _validate_directory(self, directory: KaosPath) -> ToolError ...
    method __call__ (line 80) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/file/grep_local.py
  class Params (line 28) | class Params(BaseModel):
  function _rg_binary_name (line 121) | def _rg_binary_name() -> str:
  function _find_existing_rg (line 125) | def _find_existing_rg(bin_name: str) -> Path | None:
  function _detect_target (line 142) | def _detect_target() -> str | None:
  function _download_and_install_rg (line 167) | async def _download_and_install_rg(bin_name: str) -> Path:
  function _ensure_rg_path (line 228) | async def _ensure_rg_path() -> str:
  class Grep (line 243) | class Grep(CallableTool2[Params]):
    method __call__ (line 249) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/file/plan_mode.py
  class PlanEditTarget (line 12) | class PlanEditTarget:
  function inspect_plan_edit_target (line 18) | def inspect_plan_edit_target(

FILE: src/kimi_cli/tools/file/read.py
  class Params (line 18) | class Params(BaseModel):
  class ReadFile (line 45) | class ReadFile(CallableTool2[Params]):
    method __init__ (line 49) | def __init__(self, runtime: Runtime) -> None:
    method _validate_path (line 63) | async def _validate_path(self, path: KaosPath) -> ToolError | None:
    method __call__ (line 83) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/file/read_media.py
  function _to_data_url (line 22) | def _to_data_url(mime_type: str, data: bytes) -> str:
  function _extract_image_size (line 27) | def _extract_image_size(data: bytes) -> tuple[int, int] | None:
  class Params (line 40) | class Params(BaseModel):
  class ReadMediaFile (line 49) | class ReadMediaFile(CallableTool2[Params]):
    method __init__ (line 53) | def __init__(self, runtime: Runtime) -> None:
    method _validate_path (line 72) | async def _validate_path(self, path: KaosPath) -> ToolError | None:
    method _read_media (line 91) | async def _read_media(self, path: KaosPath, file_type: FileType) -> To...
    method __call__ (line 150) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/file/replace.py
  class Edit (line 21) | class Edit(BaseModel):
  class Params (line 27) | class Params(BaseModel):
  class StrReplaceFile (line 42) | class StrReplaceFile(CallableTool2[Params]):
    method __init__ (line 47) | def __init__(self, runtime: Runtime, approval: Approval):
    method bind_plan_mode (line 55) | def bind_plan_mode(
    method _validate_path (line 62) | async def _validate_path(self, path: KaosPath) -> ToolError | None:
    method _apply_edit (line 80) | def _apply_edit(self, content: str, edit: Edit) -> str:
    method __call__ (line 88) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/file/utils.py
  class FileType (line 173) | class FileType:
  function _sniff_ftyp_brand (line 178) | def _sniff_ftyp_brand(header: bytes) -> str | None:
  function sniff_media_from_magic (line 185) | def sniff_media_from_magic(data: bytes) -> FileType | None:
  function detect_file_type (line 223) | def detect_file_type(path: str | PurePath, header: bytes | None = None) ...

FILE: src/kimi_cli/tools/file/write.py
  class Params (line 21) | class Params(BaseModel):
  class WriteFile (line 39) | class WriteFile(CallableTool2[Params]):
    method __init__ (line 44) | def __init__(self, runtime: Runtime, approval: Approval):
    method bind_plan_mode (line 52) | def bind_plan_mode(
    method _validate_path (line 59) | async def _validate_path(self, path: KaosPath) -> ToolError | None:
    method __call__ (line 78) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/multiagent/create.py
  class Params (line 12) | class Params(BaseModel):
  class CreateSubagent (line 24) | class CreateSubagent(CallableTool2[Params]):
    method __init__ (line 29) | def __init__(self, toolset: KimiToolset, runtime: Runtime):
    method __call__ (line 34) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/multiagent/task.py
  class Params (line 39) | class Params(BaseModel):
  class Task (line 53) | class Task(CallableTool2[Params]):
    method __init__ (line 57) | def __init__(self, runtime: Runtime):
    method _get_subagent_context_file (line 72) | async def _get_subagent_context_file(self) -> Path:
    method __call__ (line 84) | async def __call__(self, params: Params) -> ToolReturnValue:
    method _run_subagent (line 102) | async def _run_subagent(self, agent: Agent, prompt: str) -> ToolReturn...

FILE: src/kimi_cli/tools/plan/__init__.py
  class PlanOption (line 27) | class PlanOption(BaseModel):
    method label_not_reserved (line 43) | def label_not_reserved(cls, v: str) -> str:
  class Params (line 52) | class Params(BaseModel):
    method options_labels_unique (line 66) | def options_labels_unique(cls, v: list[PlanOption] | None) -> list[Pla...
  class ExitPlanMode (line 75) | class ExitPlanMode(CallableTool2[Params]):
    method __init__ (line 80) | def __init__(self) -> None:
    method bind (line 86) | def bind(
    method __call__ (line 98) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/plan/enter.py
  class Params (line 26) | class Params(BaseModel):
  class EnterPlanMode (line 30) | class EnterPlanMode(CallableTool2[Params]):
    method __init__ (line 35) | def __init__(self) -> None:
    method bind (line 41) | def bind(
    method __call__ (line 53) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/plan/heroes.py
  function seed_slug_cache (line 244) | def seed_slug_cache(session_id: str, slug: str) -> None:
  function get_or_create_slug (line 249) | def get_or_create_slug(session_id: str) -> str:
  function get_plan_file_path (line 267) | def get_plan_file_path(session_id: str) -> Path:
  function read_plan_file (line 272) | def read_plan_file(session_id: str) -> str | None:

FILE: src/kimi_cli/tools/shell/__init__.py
  class Params (line 24) | class Params(BaseModel):
    method _validate_background_fields (line 47) | def _validate_background_fields(self) -> Self:
  class Shell (line 58) | class Shell(CallableTool2[Params]):
    method __init__ (line 62) | def __init__(self, approval: Approval, environment: Environment, runti...
    method __call__ (line 76) | async def __call__(self, params: Params) -> ToolReturnValue:
    method _run_in_background (line 124) | async def _run_in_background(self, params: Params) -> ToolReturnValue:
    method _background_ok (line 161) | def _background_ok(self, view: TaskView) -> ToolReturnValue:
    method _run_shell_command (line 192) | async def _run_shell_command(
    method _shell_args (line 225) | def _shell_args(self, command: str) -> tuple[str, ...]:

FILE: src/kimi_cli/tools/test.py
  class PlusParams (line 8) | class PlusParams(BaseModel):
  class Plus (line 13) | class Plus(CallableTool2[PlusParams]):
    method __call__ (line 19) | async def __call__(self, params: PlusParams) -> ToolReturnValue:
  class CompareParams (line 23) | class CompareParams(BaseModel):
  class Compare (line 28) | class Compare(CallableTool2[CompareParams]):
    method __call__ (line 34) | async def __call__(self, params: CompareParams) -> ToolReturnValue:
  class PanicParams (line 43) | class PanicParams(BaseModel):
  class Panic (line 47) | class Panic(CallableTool2[PanicParams]):
    method __call__ (line 53) | async def __call__(self, params: PanicParams) -> ToolReturnValue:

FILE: src/kimi_cli/tools/think/__init__.py
  class Params (line 10) | class Params(BaseModel):
  class Think (line 14) | class Think(CallableTool2[Params]):
    method __call__ (line 20) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/todo/__init__.py
  class Todo (line 11) | class Todo(BaseModel):
  class Params (line 16) | class Params(BaseModel):
  class SetTodoList (line 20) | class SetTodoList(CallableTool2[Params]):
    method __call__ (line 26) | async def __call__(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/utils.py
  class _KeepPlaceholderUndefined (line 9) | class _KeepPlaceholderUndefined(Undefined):
    method __str__ (line 10) | def __str__(self) -> str:
  function load_desc (line 18) | def load_desc(path: Path, context: dict[str, object] | None = None) -> str:
  function truncate_line (line 33) | def truncate_line(line: str, max_length: int, marker: str = "...") -> str:
  class ToolResultBuilder (line 54) | class ToolResultBuilder:
    method __init__ (line 59) | def __init__(
    method is_full (line 77) | def is_full(self) -> bool:
    method n_chars (line 82) | def n_chars(self) -> int:
    method n_lines (line 87) | def n_lines(self) -> int:
    method write (line 91) | def write(self, text: str) -> int:
    method display (line 130) | def display(self, *blocks: DisplayBlock) -> None:
    method extras (line 134) | def extras(self, **extras: JsonType) -> None:
    method ok (line 140) | def ok(self, message: str = "", *, brief: str = "") -> ToolReturnValue:
    method error (line 161) | def error(self, message: str, *, brief: str) -> ToolReturnValue:
  class ToolRejectedError (line 182) | class ToolRejectedError(ToolError):
    method __init__ (line 183) | def __init__(self, message: str | None = None, brief: str = "Rejected ...

FILE: src/kimi_cli/tools/web/fetch.py
  class Params (line 18) | class Params(BaseModel):
  class FetchURL (line 22) | class FetchURL(CallableTool2[Params]):
    method __init__ (line 27) | def __init__(self, config: Config, runtime: Runtime):
    method __call__ (line 33) | async def __call__(self, params: Params) -> ToolReturnValue:
    method fetch_with_http_get (line 43) | async def fetch_with_http_get(params: Params) -> ToolReturnValue:
    method _fetch_with_service (line 110) | async def _fetch_with_service(self, params: Params) -> ToolReturnValue:

FILE: src/kimi_cli/tools/web/search.py
  class Params (line 16) | class Params(BaseModel):
  class SearchWeb (line 39) | class SearchWeb(CallableTool2[Params]):
    method __init__ (line 44) | def __init__(self, config: Config, runtime: Runtime):
    method __call__ (line 55) | async def __call__(self, params: Params) -> ToolReturnValue:
  class SearchResult (line 120) | class SearchResult(BaseModel):
  class Response (line 131) | class Response(BaseModel):

FILE: src/kimi_cli/ui/acp/__init__.py
  class ACPServerSingleSession (line 17) | class ACPServerSingleSession:
    method __init__ (line 18) | def __init__(self, soul: Soul):
    method on_connect (line 21) | def on_connect(self, conn: acp.Client) -> None:
    method _raise (line 24) | def _raise(self) -> NoReturn:
    method initialize (line 28) | async def initialize(
    method new_session (line 37) | async def new_session(
    method load_session (line 42) | async def load_session(
    method resume_session (line 47) | async def resume_session(
    method fork_session (line 52) | async def fork_session(
    method list_sessions (line 57) | async def list_sessions(
    method set_session_mode (line 62) | async def set_session_mode(
    method set_session_model (line 67) | async def set_session_model(
    method authenticate (line 72) | async def authenticate(self, method_id: str, **kwargs: Any) -> acp.Aut...
    method prompt (line 75) | async def prompt(
    method cancel (line 80) | async def cancel(self, session_id: str, **kwargs: Any) -> None:
    method ext_method (line 83) | async def ext_method(self, method: str, params: dict[str, Any]) -> dic...
    method ext_notification (line 86) | async def ext_notification(self, method: str, params: dict[str, Any]) ...
  class ACP (line 90) | class ACP:
    method __init__ (line 93) | def __init__(self, soul: Soul):
    method run (line 96) | async def run(self):

FILE: src/kimi_cli/ui/print/__init__.py
  class Print (line 28) | class Print:
    method __init__ (line 40) | def __init__(
    method run (line 55) | async def run(self, command: str | None = None) -> bool:
    method _read_next_command (line 120) | def _read_next_command(self) -> str | None:

FILE: src/kimi_cli/ui/print/visualize.py
  class Printer (line 22) | class Printer(Protocol):
    method feed (line 23) | def feed(self, msg: WireMessage) -> None: ...
    method flush (line 24) | def flush(self) -> None: ...
  function _merge_content (line 27) | def _merge_content(buffer: list[ContentPart], part: ContentPart) -> None:
  class TextPrinter (line 32) | class TextPrinter(Printer):
    method feed (line 33) | def feed(self, msg: WireMessage) -> None:
    method flush (line 36) | def flush(self) -> None:
  class JsonPrinter (line 40) | class JsonPrinter(Printer):
    method __init__ (line 41) | def __init__(self) -> None:
    method feed (line 50) | def feed(self, msg: WireMessage) -> None:
    method _flush_assistant_message (line 80) | def _flush_assistant_message(self) -> None:
    method _emit_notification (line 95) | def _emit_notification(self, notification: Notification) -> None:
    method _flush_notifications (line 98) | def _flush_notifications(self) -> None:
    method flush (line 103) | def flush(self) -> None:
  class FinalOnlyTextPrinter (line 108) | class FinalOnlyTextPrinter(Printer):
    method __init__ (line 109) | def __init__(self) -> None:
    method feed (line 112) | def feed(self, msg: WireMessage) -> None:
    method flush (line 121) | def flush(self) -> None:
  class FinalOnlyJsonPrinter (line 131) | class FinalOnlyJsonPrinter(Printer):
    method __init__ (line 132) | def __init__(self) -> None:
    method feed (line 135) | def feed(self, msg: WireMessage) -> None:
    method flush (line 144) | def flush(self) -> None:
  function visualize (line 155) | async def visualize(output_format: OutputFormat, final_only: bool, wire:...

FILE: src/kimi_cli/ui/shell/__init__.py
  class _PromptEvent (line 48) | class _PromptEvent:
  class Shell (line 53) | class Shell:
    method __init__ (line 54) | def __init__(self, soul: Soul, welcome_info: list[WelcomeInfoItem] | N...
    method available_slash_commands (line 69) | def available_slash_commands(self) -> dict[str, SlashCommand[Any]]:
    method _should_exit_input (line 74) | def _should_exit_input(user_input: UserInput) -> bool:
    method _agent_slash_command_call (line 78) | def _agent_slash_command_call(user_input: UserInput) -> SlashCommandCa...
    method _should_echo_agent_input (line 90) | def _should_echo_agent_input(user_input: UserInput) -> bool:
    method _echo_agent_input (line 98) | def _echo_agent_input(user_input: UserInput) -> None:
    method _bind_running_input (line 101) | def _bind_running_input(
    method _unbind_running_input (line 109) | def _unbind_running_input(self) -> None:
    method _route_prompt_events (line 113) | async def _route_prompt_events(
    method run (line 163) | async def run(self, command: str | None = None) -> bool:
    method _run_shell_command (line 347) | async def _run_shell_command(self, command: str) -> None:
    method _run_slash_command (line 404) | async def _run_slash_command(self, command_call: SlashCommandCall) -> ...
    method run_soul_command (line 443) | async def run_soul_command(self, user_input: str | list[ContentPart]) ...
    method _auto_update (line 517) | async def _auto_update(self) -> None:
    method _start_background_task (line 530) | def _start_background_task(self, coro: Coroutine[Any, Any, Any]) -> as...
    method _cancel_background_tasks (line 546) | def _cancel_background_tasks(self) -> None:
  class WelcomeInfoItem (line 563) | class WelcomeInfoItem:
    class Level (line 564) | class Level(Enum):
  function _print_welcome_info (line 574) | def _print_welcome_info(name: str, info_items: list[WelcomeInfoItem]) ->...

FILE: src/kimi_cli/ui/shell/debug.py
  function _format_content_part (line 30) | def _format_content_part(part: ContentPart) -> Text | Panel | Group:
  function _format_tool_call (line 69) | def _format_tool_call(tool_call: ToolCall) -> Panel:
  function _format_message (line 93) | def _format_message(msg: Message, index: int) -> Panel:
  function debug (line 148) | def debug(app: Shell, args: str):

FILE: src/kimi_cli/ui/shell/echo.py
  function render_user_echo (line 10) | def render_user_echo(message: Message) -> Text:
  function render_user_echo_text (line 15) | def render_user_echo_text(text: str) -> Text:

FILE: src/kimi_cli/ui/shell/export_import.py
  function export (line 25) | async def export(app: Shell, args: str):
  function import_context (line 62) | async def import_context(app: Shell, args: str):

FILE: src/kimi_cli/ui/shell/keyboard.py
  class KeyEvent (line 13) | class KeyEvent(Enum):
  class KeyboardListener (line 31) | class KeyboardListener:
    method __init__ (line 32) | def __init__(self) -> None:
    method start (line 40) | async def start(self) -> None:
    method stop (line 58) | async def stop(self) -> None:
    method _pause_sync (line 64) | def _pause_sync(self) -> None:
    method pause (line 68) | async def pause(self) -> None:
    method _resume_sync (line 71) | def _resume_sync(self) -> None:
    method resume (line 76) | async def resume(self) -> None:
    method get (line 79) | async def get(self) -> KeyEvent:
  function listen_for_keyboard (line 83) | async def listen_for_keyboard() -> AsyncGenerator[KeyEvent]:
  function _listen_for_keyboard_thread (line 94) | def _listen_for_keyboard_thread(
  function _listen_for_keyboard_unix (line 106) | def _listen_for_keyboard_unix(
  function _listen_for_keyboard_windows (line 207) | def _listen_for_keyboard_windows(
  function dev_main (line 296) | async def dev_main():

FILE: src/kimi_cli/ui/shell/mcp_status.py
  function render_mcp_console (line 16) | def render_mcp_console(snapshot: MCPStatusSnapshot) -> RenderableType:
  function render_mcp_prompt (line 45) | def render_mcp_prompt(snapshot: MCPStatusSnapshot, *, now: float | None ...
  function _spinner_frame (line 72) | def _spinner_frame(now: float | None = None) -> str:
  function _status_color (line 77) | def _status_color(status: str) -> str:
  function _prompt_status_style (line 87) | def _prompt_status_style(status: str) -> str:
  function _prompt_server_detail (line 97) | def _prompt_server_detail(server: MCPServerSnapshot) -> str:

FILE: src/kimi_cli/ui/shell/oauth.py
  function _login_kimi_code (line 22) | async def _login_kimi_code(soul: KimiSoul) -> bool:
  function _current_model_key (line 51) | def _current_model_key(soul: KimiSoul) -> str | None:
  function login (line 62) | async def login(app: Shell, args: str) -> None:
  function logout (line 82) | async def logout(app: Shell, args: str) -> None:

FILE: src/kimi_cli/ui/shell/placeholders.py
  function sanitize_surrogates (line 37) | def sanitize_surrogates(text: str) -> str:
  function normalize_pasted_text (line 48) | def normalize_pasted_text(text: str) -> str:
  function count_text_lines (line 53) | def count_text_lines(text: str) -> int:
  function should_placeholderize_pasted_text (line 59) | def should_placeholderize_pasted_text(text: str) -> bool:
  function build_pasted_text_placeholder (line 67) | def build_pasted_text_placeholder(paste_id: int, text: str) -> str:
  function _guess_image_mime (line 74) | def _guess_image_mime(path: Path) -> str:
  function _build_image_part (line 81) | def _build_image_part(image_bytes: bytes, mime_type: str) -> ImageURLPart:
  class CachedAttachment (line 94) | class CachedAttachment:
  class AttachmentCache (line 100) | class AttachmentCache:
    method __init__ (line 103) | def __init__(
    method _dir_for (line 114) | def _dir_for(self, kind: CachedAttachmentKind, *, root: Path | None = ...
    method _ensure_dir (line 117) | def _ensure_dir(self, kind: CachedAttachmentKind) -> Path | None:
    method _reserve_id (line 130) | def _reserve_id(self, dir_path: Path, suffix: str) -> str:
    method store_bytes (line 137) | def store_bytes(
    method store_image (line 168) | def store_image(self, image: Image.Image) -> CachedAttachment | None:
    method _candidate_paths (line 173) | def _candidate_paths(self, kind: CachedAttachmentKind, attachment_id: ...
    method load_bytes (line 177) | def load_bytes(
    method load_content_parts (line 194) | def load_content_parts(
  function parse_attachment_kind (line 208) | def parse_attachment_kind(raw_kind: str) -> CachedAttachmentKind | None:
  class PlaceholderTokenMatch (line 218) | class PlaceholderTokenMatch:
  class PlaceholderHandler (line 226) | class PlaceholderHandler(Protocol):
    method find_next (line 227) | def find_next(self, text: str, start: int = 0) -> PlaceholderTokenMatc...
    method resolve_content (line 229) | def resolve_content(self, match: PlaceholderTokenMatch) -> list[Conten...
    method expand_text (line 231) | def expand_text(self, match: PlaceholderTokenMatch) -> str | None: ...
    method serialize_for_history (line 233) | def serialize_for_history(self, match: PlaceholderTokenMatch) -> str |...
    method expand_for_editor (line 235) | def expand_for_editor(self, match: PlaceholderTokenMatch) -> str | Non...
  class PastedTextEntry (line 239) | class PastedTextEntry:
    method token (line 244) | def token(self) -> str:
  class PastedTextPlaceholderHandler (line 248) | class PastedTextPlaceholderHandler:
    method __init__ (line 249) | def __init__(self) -> None:
    method create_placeholder (line 253) | def create_placeholder(self, text: str) -> str:
    method maybe_placeholderize (line 260) | def maybe_placeholderize(self, text: str) -> str:
    method entry_for_id (line 266) | def entry_for_id(self, paste_id: int) -> PastedTextEntry | None:
    method iter_entries_for_command (line 269) | def iter_entries_for_command(
    method find_next (line 282) | def find_next(self, text: str, start: int = 0) -> PlaceholderTokenMatc...
    method resolve_content (line 294) | def resolve_content(self, match: PlaceholderTokenMatch) -> list[Conten...
    method expand_text (line 301) | def expand_text(self, match: PlaceholderTokenMatch) -> str | None:
    method serialize_for_history (line 306) | def serialize_for_history(self, match: PlaceholderTokenMatch) -> str |...
    method expand_for_editor (line 309) | def expand_for_editor(self, match: PlaceholderTokenMatch) -> str | None:
    method refold_after_editor (line 312) | def refold_after_editor(self, edited_text: str, original_command: str)...
    method _expanded_text_and_intervals (line 337) | def _expanded_text_and_intervals(
    method _map_interval (line 359) | def _map_interval(
  class ImagePlaceholderHandler (line 389) | class ImagePlaceholderHandler:
    method __init__ (line 390) | def __init__(self, attachment_cache: AttachmentCache) -> None:
    method create_placeholder (line 393) | def create_placeholder(self, image: Image.Image) -> str | None:
    method find_next (line 399) | def find_next(self, text: str, start: int = 0) -> PlaceholderTokenMatc...
    method resolve_content (line 411) | def resolve_content(self, match: PlaceholderTokenMatch) -> list[Conten...
    method expand_text (line 417) | def expand_text(self, match: PlaceholderTokenMatch) -> str | None:
    method serialize_for_history (line 420) | def serialize_for_history(self, match: PlaceholderTokenMatch) -> str |...
    method expand_for_editor (line 423) | def expand_for_editor(self, match: PlaceholderTokenMatch) -> str | None:
  class ResolvedPromptCommand (line 428) | class ResolvedPromptCommand:
  class PromptPlaceholderManager (line 434) | class PromptPlaceholderManager:
    method __init__ (line 435) | def __init__(self, attachment_cache: AttachmentCache | None = None) ->...
    method attachment_cache (line 445) | def attachment_cache(self) -> AttachmentCache:
    method maybe_placeholderize_pasted_text (line 448) | def maybe_placeholderize_pasted_text(self, text: str) -> str:
    method create_image_placeholder (line 451) | def create_image_placeholder(self, image: Image.Image) -> str | None:
    method resolve_command (line 454) | def resolve_command(self, command: str) -> ResolvedPromptCommand:
    method serialize_for_history (line 487) | def serialize_for_history(self, command: str) -> str:
    method expand_for_editor (line 493) | def expand_for_editor(self, command: str) -> str:
    method refold_after_editor (line 499) | def refold_after_editor(self, edited_text: str, original_command: str)...
    method _find_next_match (line 502) | def _find_next_match(self, text: str, start: int = 0) -> PlaceholderTo...
    method _rewrite_command (line 512) | def _rewrite_command(

FILE: src/kimi_cli/ui/shell/prompt.py
  class SlashCommandCompleter (line 82) | class SlashCommandCompleter(Completer):
    method __init__ (line 90) | def __init__(self, available_commands: Sequence[SlashCommand[Any]]) ->...
    method should_complete (line 114) | def should_complete(document: Document) -> bool:
    method get_completions (line 128) | def get_completions(
  function _truncate_to_width (line 161) | def _truncate_to_width(text: str, width: int) -> str:
  function _wrap_to_width (line 194) | def _wrap_to_width(text: str, width: int, *, max_lines: int | None = Non...
  function _find_prompt_float_container (line 253) | def _find_prompt_float_container(layout_container: object) -> FloatConta...
  function _extract_float_container (line 264) | def _extract_float_container(container: object) -> FloatContainer | None:
  class SlashCommandMenuControl (line 275) | class SlashCommandMenuControl(UIControl):
    method __init__ (line 280) | def __init__(
    method has_focus (line 289) | def has_focus(self) -> bool:
    method preferred_width (line 292) | def preferred_width(self, max_available_width: int) -> int | None:
    method preferred_height (line 295) | def preferred_height(
    method create_content (line 323) | def create_content(self, width: int, height: int) -> UIContent:
    method _selected_meta_lines (line 414) | def _selected_meta_lines(self, text: str, meta_width: int) -> list[str]:
    method _visible_window_bounds (line 422) | def _visible_window_bounds(
    method _command_column_width (line 447) | def _command_column_width(
    method _render_single_line_item (line 462) | def _render_single_line_item(
    method _render_selected_item_lines (line 508) | def _render_selected_item_lines(
  class LocalFileMentionCompleter (line 558) | class LocalFileMentionCompleter(Completer):
    method __init__ (line 636) | def __init__(
    method _is_ignored (line 665) | def _is_ignored(cls, name: str) -> bool:
    method _get_paths (line 672) | def _get_paths(self) -> list[str]:
    method _get_top_level_paths (line 678) | def _get_top_level_paths(self) -> list[str]:
    method _get_deep_paths (line 699) | def _get_deep_paths(self) -> list[str]:
    method _extract_fragment (line 743) | def _extract_fragment(text: str) -> str | None:
    method _is_completed_file (line 762) | def _is_completed_file(self, fragment: str) -> bool:
    method get_completions (line 772) | def get_completions(
  class _HistoryEntry (line 808) | class _HistoryEntry(BaseModel):
  function _load_history_entries (line 812) | def _load_history_entries(history_file: Path) -> list[_HistoryEntry]:
  class PromptMode (line 850) | class PromptMode(Enum):
    method toggle (line 854) | def toggle(self) -> PromptMode:
    method __str__ (line 857) | def __str__(self) -> str:
  class UserInput (line 861) | class UserInput(BaseModel):
    method __str__ (line 870) | def __str__(self) -> str:
    method __bool__ (line 873) | def __bool__(self) -> bool:
  class _GitBranchState (line 888) | class _GitBranchState:
  class _GitStatusState (line 895) | class _GitStatusState:
  function _get_git_branch (line 909) | def _get_git_branch() -> str | None:
  function _get_git_status (line 950) | def _get_git_status() -> tuple[bool, int, int]:
  function _format_git_badge (line 1003) | def _format_git_badge(branch: str, dirty: bool, ahead: int, behind: int)...
  function _shorten_cwd (line 1020) | def _shorten_cwd(path: str) -> str:
  function _display_width (line 1030) | def _display_width(text: str) -> int:
  function _truncate_left (line 1035) | def _truncate_left(text: str, max_cols: int) -> str:
  function _truncate_right (line 1054) | def _truncate_right(text: str, max_cols: int) -> str:
  class _ToastEntry (line 1074) | class _ToastEntry:
  class RunningPromptDelegate (line 1081) | class RunningPromptDelegate(Protocol):
    method render_running_prompt_body (line 1082) | def render_running_prompt_body(self, columns: int) -> AnyFormattedText...
    method running_prompt_placeholder (line 1084) | def running_prompt_placeholder(self) -> AnyFormattedText | None: ...
    method should_handle_running_prompt_key (line 1086) | def should_handle_running_prompt_key(self, key: str) -> bool: ...
    method handle_running_prompt_key (line 1088) | def handle_running_prompt_key(self, key: str, event: KeyPressEvent) ->...
  function toast (line 1098) | def toast(
  function _current_toast (line 1119) | def _current_toast(position: Literal["left", "right"] = "left") -> _Toas...
  function _build_toolbar_tips (line 1129) | def _build_toolbar_tips(clipboard_available: bool) -> list[str]:
  class CustomPromptSession (line 1145) | class CustomPromptSession:
    method __init__ (line 1146) | def __init__(
    method _install_slash_completion_menu (line 1439) | def _install_slash_completion_menu(self) -> None:
    method _should_show_slash_completion_menu (line 1485) | def _should_show_slash_completion_menu(self) -> bool:
    method _slash_menu_left_padding (line 1489) | def _slash_menu_left_padding(self) -> int:
    method _render_message (line 1497) | def _render_message(self) -> FormattedText:
    method _render_shell_prompt_message (line 1502) | def _render_shell_prompt_message(self) -> FormattedText:
    method _open_in_external_editor (line 1517) | def _open_in_external_editor(self, event: KeyPressEvent) -> None:
    method _apply_mode (line 1545) | def _apply_mode(self, event: KeyPressEvent | None = None) -> None:
    method _sync_erase_when_done (line 1560) | def _sync_erase_when_done(self) -> None:
    method _should_handle_running_prompt_key (line 1565) | def _should_handle_running_prompt_key(self, key: str) -> bool:
    method _handle_running_prompt_key (line 1569) | def _handle_running_prompt_key(self, key: str, event: KeyPressEvent) -...
    method invalidate (line 1576) | def invalidate(self) -> None:
    method _render_agent_prompt_message (line 1581) | def _render_agent_prompt_message(self) -> FormattedText:
    method _render_agent_prompt_body (line 1596) | def _render_agent_prompt_body(self, columns: int) -> FormattedText:
    method _render_status_block (line 1602) | def _render_status_block(self, columns: int) -> FormattedText:
    method _render_agent_prompt_label (line 1611) | def _render_agent_prompt_label(self) -> FormattedText:
    method __enter__ (line 1618) | def __enter__(self) -> CustomPromptSession:
    method __exit__ (line 1653) | def __exit__(self, *_) -> None:
    method _get_placeholder_manager (line 1658) | def _get_placeholder_manager(self) -> PromptPlaceholderManager:
    method _insert_pasted_text (line 1667) | def _insert_pasted_text(self, buffer: Buffer, text: str) -> None:
    method _handle_bracketed_paste (line 1675) | def _handle_bracketed_paste(self, event: KeyPressEvent) -> None:
    method _try_paste_media (line 1679) | def _try_paste_media(self, event: KeyPressEvent) -> bool:
    method prompt_next (line 1725) | async def prompt_next(self) -> UserInput:
    method last_submission_was_running (line 1729) | def last_submission_was_running(self) -> bool:
    method attach_running_prompt (line 1732) | def attach_running_prompt(self, delegate: RunningPromptDelegate) -> None:
    method detach_running_prompt (line 1743) | def detach_running_prompt(self, delegate: RunningPromptDelegate) -> None:
    method _prompt_once (line 1754) | async def _prompt_once(self, *, append_history: bool | None) -> UserIn...
    method _build_user_input (line 1772) | def _build_user_input(self, command: str) -> UserInput:
    method _append_history_entry (line 1782) | def _append_history_entry(self, text: str) -> None:
    method _render_bottom_toolbar (line 1804) | def _render_bottom_toolbar(self) -> FormattedText:
    method _get_two_rotating_tips (line 1912) | def _get_two_rotating_tips(self) -> str | None:
    method _get_one_rotating_tip (line 1924) | def _get_one_rotating_tip(self) -> str | None:
    method _render_right_span (line 1931) | def _render_right_span(status: StatusSnapshot) -> str:

FILE: src/kimi_cli/ui/shell/replay.py
  class _ReplayTurn (line 38) | class _ReplayTurn:
  function replay_recent_history (line 44) | async def replay_recent_history(
  function _build_replay_turns_from_wire (line 81) | async def _build_replay_turns_from_wire(wire_file: WireFile | None) -> l...
  function _message_from_user_input (line 133) | def _message_from_user_input(user_input: str | list[ContentPart]) -> Mes...
  function _same_user_turns (line 141) | def _same_user_turns(lhs: Sequence[_ReplayTurn], rhs: Sequence[_ReplayTu...
  function _is_clear_command_input (line 147) | def _is_clear_command_input(user_input: str | list[ContentPart]) -> bool:
  function _is_user_message (line 158) | def _is_user_message(message: Message) -> bool:
  function _find_replay_start (line 167) | def _find_replay_start(history: Sequence[Message]) -> int | None:
  function _build_replay_turns_from_history (line 175) | def _build_replay_turns_from_history(history: Sequence[Message]) -> list...

FILE: src/kimi_cli/ui/shell/setup.py
  function select_platform (line 36) | async def select_platform() -> Platform | None:
  function setup_platform (line 52) | async def setup_platform(platform: Platform) -> bool:
  class _SetupResult (line 68) | class _SetupResult(NamedTuple):
  function _setup_platform (line 76) | async def _setup_platform(platform: Platform) -> _SetupResult | None:
  function _apply_setup_result (line 142) | def _apply_setup_result(result: _SetupResult) -> None:
  function _prompt_choice (line 180) | async def _prompt_choice(*, header: str, choices: list[str]) -> str | None:
  function _prompt_text (line 194) | async def _prompt_text(prompt: str, *, is_password: bool = False) -> str...
  function reload (line 208) | def reload(app: Shell, args: str):

FILE: src/kimi_cli/ui/shell/slash.py
  function ensure_kimi_soul (line 39) | def ensure_kimi_soul(app: Shell) -> KimiSoul | None:
  function exit (line 48) | def exit(app: Shell, args: str):
  function help (line 69) | def help(app: Shell, args: str):
  function version (line 138) | def version(app: Shell, args: str):
  function model (line 146) | async def model(app: Shell, args: str):
  function editor (line 269) | async def editor(app: Shell, args: str):
  function changelog (line 366) | def changelog(app: Shell, args: str):
  function feedback (line 397) | def feedback(app: Shell, args: str):
  function clear (line 408) | async def clear(app: Shell, args: str):
  function new (line 417) | async def new(app: Shell, args: str):
  function list_sessions (line 435) | async def list_sessions(app: Shell, args: str):
  function task (line 480) | async def task(app: Shell, args: str):
  function web (line 496) | def web(app: Shell, args: str):
  function mcp (line 504) | async def mcp(app: Shell, args: str):

FILE: src/kimi_cli/ui/shell/startup.py
  class ShellStartupProgress (line 8) | class ShellStartupProgress:
    method __init__ (line 11) | def __init__(self, *, enabled: bool | None = None) -> None:
    method update (line 15) | def update(self, message: str) -> None:
    method stop (line 27) | def stop(self) -> None:

FILE: src/kimi_cli/ui/shell/task_browser.py
  function format_task_choice (line 34) | def format_task_choice(view: TaskView, *, now: float | None = None) -> str:
  class TaskBrowserModel (line 48) | class TaskBrowserModel:
    method manager (line 58) | def manager(self):
    method config (line 62) | def config(self):
    method refresh (line 65) | def refresh(self, selected_task_id: str | None = None) -> tuple[list[t...
    method view_for (line 94) | def view_for(self, task_id: str | None) -> TaskView | None:
    method set_message (line 102) | def set_message(self, text: str, *, duration_s: float = _FLASH_MESSAGE...
    method current_message (line 106) | def current_message(self) -> str | None:
    method summary_fragments (line 117) | def summary_fragments(self) -> StyleAndTextTuples:
    method detail_text (line 141) | def detail_text(self, task_id: str | None) -> str:
    method preview_text (line 167) | def preview_text(self, task_id: str | None) -> str:
    method full_output (line 181) | def full_output(self, task_id: str | None) -> str:
    method footer_fragments (line 201) | def footer_fragments(self, task_id: str | None) -> StyleAndTextTuples:
  class TaskBrowserApp (line 235) | class TaskBrowserApp:
    method __init__ (line 236) | def __init__(self, soul: KimiSoul):
    method run (line 254) | async def run(self) -> None:
    method _selected_task_id (line 258) | def _selected_task_id(self) -> str | None:
    method _open_output (line 264) | def _open_output(self, app: Application[object], task_id: str) -> None:
    method _show_output_in_terminal (line 267) | async def _show_output_in_terminal(self, task_id: str) -> None:
    method _toggle_filter (line 278) | def _toggle_filter(self) -> None:
    method _refresh_views (line 287) | def _refresh_views(self) -> None:
    method _request_stop_for_selected_task (line 291) | def _request_stop_for_selected_task(self) -> None:
    method _confirm_stop_request (line 302) | def _confirm_stop_request(self) -> None:
    method _cancel_stop_request (line 317) | def _cancel_stop_request(self) -> None:
    method _build_app (line 321) | def _build_app(self) -> Application[None]:
    method _sync_views (line 429) | def _sync_views(self) -> None:
    method _header_fragments (line 439) | def _header_fragments(self) -> StyleAndTextTuples:
    method _detail_fragments (line 442) | def _detail_fragments(self) -> StyleAndTextTuples:
    method _preview_fragments (line 445) | def _preview_fragments(self) -> StyleAndTextTuples:
    method _footer_fragments (line 448) | def _footer_fragments(self) -> StyleAndTextTuples:
  function _build_full_output_renderable (line 452) | def _build_full_output_renderable(view: TaskView, output: str) -> Panel:
  function _task_sort_key (line 466) | def _task_sort_key(view: TaskView) -> tuple[int, float]:
  function _task_timing_label (line 473) | def _task_timing_label(view: TaskView, *, now: float | None = None) -> s...
  function _task_browser_style (line 483) | def _task_browser_style() -> Style:

FILE: src/kimi_cli/ui/shell/update.py
  class UpdateResult (line 29) | class UpdateResult(Enum):
  function semver_tuple (line 40) | def semver_tuple(version: str) -> tuple[int, int, int]:
  function _detect_target (line 53) | def _detect_target() -> str | None:
  function _get_latest_version (line 73) | async def _get_latest_version(session: aiohttp.ClientSession) -> str | N...
  function do_update (line 84) | async def do_update(*, print: bool = True, check_only: bool = False) -> ...
  function _do_update (line 92) | async def _do_update(*, print: bool, check_only: bool) -> UpdateResult:

FILE: src/kimi_cli/ui/shell/usage.py
  class UsageRow (line 30) | class UsageRow:
  function usage (line 38) | async def usage(app: Shell, args: str):
  function _usage_url (line 79) | def _usage_url(model: LLMModel | None) -> str | None:
  function _fetch_usage (line 92) | async def _fetch_usage(url: str, api_key: str) -> Mapping[str, Any]:
  function _parse_usage_payload (line 104) | def _parse_usage_payload(
  function _to_usage_row (line 139) | def _to_usage_row(data: Mapping[str, Any], *, default_label: str) -> Usa...
  function _limit_label (line 157) | def _limit_label(
  function _reset_hint (line 186) | def _reset_hint(data: Mapping[str, Any]) -> str | None:
  function _format_reset_time (line 199) | def _format_reset_time(val: str) -> str:
  function _to_int (line 221) | def _to_int(value: Any) -> int | None:
  function _build_usage_panel (line 228) | def _build_usage_panel(summary: UsageRow | None, limits: list[UsageRow])...
  function _format_row (line 252) | def _format_row(row: UsageRow, label_width: int) -> RenderableType:
  function _ratio_color (line 273) | def _ratio_color(ratio: float) -> str:

FILE: src/kimi_cli/ui/shell/visualize.py
  function visualize (line 84) | async def visualize(
  class _ContentBlock (line 130) | class _ContentBlock:
    method __init__ (line 131) | def __init__(self, is_think: bool):
    method compose (line 136) | def compose(self) -> RenderableType:
    method compose_final (line 139) | def compose_final(self) -> RenderableType:
    method append (line 148) | def append(self, content: str) -> None:
  class _ToolCallBlock (line 152) | class _ToolCallBlock:
    class FinishedSubCall (line 153) | class FinishedSubCall(NamedTuple):
    method __init__ (line 157) | def __init__(self, tool_call: ToolCall):
    method compose (line 177) | def compose(self) -> RenderableType:
    method finished (line 181) | def finished(self) -> bool:
    method append_args_part (line 184) | def append_args_part(self, args_part: str):
    method finish (line 198) | def finish(self, result: ToolReturnValue):
    method append_sub_tool_call (line 202) | def append_sub_tool_call(self, tool_call: ToolCall):
    method append_sub_tool_call_part (line 206) | def append_sub_tool_call_part(self, tool_call_part: ToolCallPart):
    method finish_sub_tool_call (line 216) | def finish_sub_tool_call(self, tool_result: ToolResult):
    method _compose (line 231) | def _compose(self) -> RenderableType:
    method _extract_full_url (line 298) | def _extract_full_url(arguments: str | None, tool_name: str) -> str | ...
    method _build_headline_text (line 312) | def _build_headline_text(self) -> Text:
    method _render_todo_markdown (line 323) | def _render_todo_markdown(self, block: TodoDisplayBlock) -> str:
  class _ApprovalContentBlock (line 339) | class _ApprovalContentBlock(NamedTuple):
  class _NotificationBlock (line 348) | class _NotificationBlock:
    method __init__ (line 356) | def __init__(self, notification: Notification):
    method compose (line 359) | def compose(self) -> RenderableType:
  class _ApprovalRequestPanel (line 372) | class _ApprovalRequestPanel:
    method __init__ (line 373) | def __init__(self, request: ApprovalRequest):
    method render (line 436) | def render(self) -> RenderableType:
    method _render_block (line 487) | def _render_block(
    method render_full (line 500) | def render_full(self) -> list[RenderableType]:
    method move_up (line 504) | def move_up(self):
    method move_down (line 508) | def move_down(self):
    method get_selected_response (line 512) | def get_selected_response(self) -> ApprovalResponse.Kind:
  function _show_approval_in_pager (line 517) | def _show_approval_in_pager(panel: _ApprovalRequestPanel) -> None:
  class _QuestionRequestPanel (line 538) | class _QuestionRequestPanel:
    method __init__ (line 541) | def __init__(self, request: QuestionRequest):
    method _setup_current_question (line 552) | def _setup_current_question(self) -> None:
    method _recompute_body (line 590) | def _recompute_body(self) -> None:
    method _current_question (line 597) | def _current_question(self):
    method is_other_selected (line 601) | def is_other_selected(self) -> bool:
    method is_multi_select (line 605) | def is_multi_select(self) -> bool:
    method current_question_text (line 609) | def current_question_text(self) -> str:
    method should_prompt_other_input (line 612) | def should_prompt_other_input(self) -> bool:
    method select_index (line 619) | def select_index(self, index: int) -> bool:
    method render (line 626) | def render(self) -> RenderableType:
    method go_to (line 699) | def go_to(self, index: int) -> None:
    method next_tab (line 713) | def next_tab(self) -> None:
    method prev_tab (line 718) | def prev_tab(self) -> None:
    method move_up (line 723) | def move_up(self) -> None:
    method move_down (line 726) | def move_down(self) -> None:
    method toggle_select (line 729) | def toggle_select(self) -> None:
    method submit (line 738) | def submit(self) -> bool:
    method submit_other (line 760) | def submit_other(self, text: str) -> bool:
    method _advance (line 780) | def _advance(self) -> bool:
    method get_answers (line 795) | def get_answers(self) -> dict[str, str]:
    method render_full_body (line 798) | def render_full_body(self) -> list[RenderableType]:
  function _show_question_body_in_pager (line 805) | def _show_question_body_in_pager(panel: _QuestionRequestPanel) -> None:
  function _prompt_other_input (line 814) | async def _prompt_other_input(question_text: str) -> str:
  class _StatusBlock (line 827) | class _StatusBlock:
    method __init__ (line 828) | def __init__(self, initial: StatusUpdate) -> None:
    method render (line 835) | def render(self) -> RenderableType:
    method update (line 838) | def update(self, status: StatusUpdate) -> None:
  function _render_renderable_to_ansi (line 853) | def _render_renderable_to_ansi(renderable: RenderableType, *, columns: i...
  function _keyboard_listener (line 869) | async def _keyboard_listener(
  class _LiveView (line 890) | class _LiveView:
    method __init__ (line 891) | def __init__(self, initial_status: StatusUpdate, cancel_event: asyncio...
    method _reset_live_shape (line 916) | def _reset_live_shape(self, live: Live) -> None:
    method visualize_loop (line 922) | async def visualize_loop(self, wire: WireUISide):
    method refresh_soon (line 989) | def refresh_soon(self) -> None:
    method has_expandable_panel (line 992) | def has_expandable_panel(self) -> bool:
    method _expandable_approval_panel (line 998) | def _expandable_approval_panel(self) -> _ApprovalRequestPanel | None:
    method _expandable_question_panel (line 1004) | def _expandable_question_panel(self) -> _QuestionRequestPanel | None:
    method _show_expandable_panel_content (line 1010) | def _show_expandable_panel_content(self) -> bool:
    method _should_prompt_question_other_for_key (line 1019) | def _should_prompt_question_other_for_key(self, key: KeyEvent) -> bool:
    method _submit_question_other_text (line 1025) | def _submit_question_other_text(self, text: str) -> None:
    method compose (line 1036) | def compose(self, *, include_status: bool = True) -> RenderableType:
    method dispatch_wire_message (line 1061) | def dispatch_wire_message(self, msg: WireMessage) -> None:
    method _try_submit_question (line 1126) | def _try_submit_question(self) -> None:
    method dispatch_keyboard_event (line 1136) | def dispatch_keyboard_event(self, event: KeyEvent) -> None:
    method _submit_approval (line 1218) | def _submit_approval(self) -> None:
    method cleanup (line 1239) | def cleanup(self, is_interrupt: bool) -> None:
    method flush_content (line 1265) | def flush_content(self) -> None:
    method flush_finished_tool_calls (line 1272) | def flush_finished_tool_calls(self) -> None:
    method flush_notifications (line 1286) | def flush_notifications(self) -> None:
    method append_content (line 1293) | def append_content(self, part: ContentPart) -> None:
    method append_tool_call (line 1311) | def append_tool_call(self, tool_call: ToolCall) -> None:
    method append_tool_call_part (line 1317) | def append_tool_call_part(self, part: ToolCallPart) -> None:
    method append_tool_result (line 1325) | def append_tool_result(self, result: ToolResult) -> None:
    method append_notification (line 1331) | def append_notification(self, notification: Notification) -> None:
    method request_approval (line 1337) | def request_approval(self, request: ApprovalRequest) -> None:
    method show_next_approval_request (line 1349) | def show_next_approval_request(self) -> None:
    method request_question (line 1374) | def request_question(self, request: QuestionRequest) -> None:
    method show_next_question_request (line 1380) | def show_next_question_request(self) -> None:
    method handle_subagent_event (line 1401) | def handle_subagent_event(self, event: SubagentEvent) -> None:
  class _PromptLiveView (line 1420) | class _PromptLiveView(_LiveView):
    method __init__ (line 1437) | def __init__(
    method visualize_loop (line 1452) | async def visualize_loop(self, wire: WireUISide):
    method handle_local_input (line 1481) | def handle_local_input(self, user_input: UserInput) -> None:
    method dispatch_wire_message (line 1490) | def dispatch_wire_message(self, msg: WireMessage) -> None:
    method render_running_prompt_body (line 1498) | def render_running_prompt_body(self, columns: int) -> ANSI:
    method running_prompt_placeholder (line 1508) | def running_prompt_placeholder(self) -> str | None:
    method should_handle_running_prompt_key (line 1511) | def should_handle_running_prompt_key(self, key: str) -> bool:
    method handle_running_prompt_key (line 1539) | def handle_running_prompt_key(self, key: str, event: KeyPressEvent) ->...
    method _show_panel_in_pager (line 1576) | async def _show_panel_in_pager(self) -> None:
    method _submit_question_other_input (line 1580) | def _submit_question_other_input(self, buffer: Buffer) -> None:
    method _clear_buffer (line 1593) | def _clear_buffer(buffer: Buffer) -> None:
    method _flush_prompt_refresh (line 1597) | def _flush_prompt_refresh(self) -> None:
    method cleanup (line 1602) | def cleanup(self, is_interrupt: bool) -> None:

FILE: src/kimi_cli/utils/aiohttp.py
  function new_client_session (line 11) | def new_client_session() -> aiohttp.ClientSession:

FILE: src/kimi_cli/utils/aioqueue.py
  class Queue (line 9) | class Queue[T](asyncio.Queue[T]):
    method __init__ (line 25) | def __init__(self) -> None:
    method shutdown (line 29) | def shutdown(self, immediate: bool = False) -> None:
    method _enqueue_shutdown (line 40) | def _enqueue_shutdown(self, count: int) -> None:
    method get (line 48) | async def get(self) -> T:
    method get_nowait (line 56) | def get_nowait(self) -> T:
    method put (line 64) | async def put(self, item: T) -> None:
    method put_nowait (line 69) | def put_nowait(self, item: T) -> None:
  class QueueShutDown (line 14) | class QueueShutDown(Exception):
  class _Shutdown (line 17) | class _Shutdown:
  class Queue (line 22) | class Queue[T](asyncio.Queue[T | _Shutdown]):
    method __init__ (line 25) | def __init__(self) -> None:
    method shutdown (line 29) | def shutdown(self, immediate: bool = False) -> None:
    method _enqueue_shutdown (line 40) | def _enqueue_shutdown(self, count: int) -> None:
    method get (line 48) | async def get(self) -> T:
    method get_nowait (line 56) | def get_nowait(self) -> T:
    method put (line 64) | async def put(self, item: T) -> None:
    method put_nowait (line 69) | def put_nowait(self, item: T) -> None:

FILE: src/kimi_cli/utils/broadcast.py
  class BroadcastQueue (line 6) | class BroadcastQueue[T]:
    method __init__ (line 11) | def __init__(self) -> None:
    method subscribe (line 14) | def subscribe(self) -> Queue[T]:
    method unsubscribe (line 20) | def unsubscribe(self, queue: Queue[T]) -> None:
    method publish (line 24) | async def publish(self, item: T) -> None:
    method publish_nowait (line 28) | def publish_nowait(self, item: T) -> None:
    method shutdown (line 33) | def shutdown(self, immediate: bool = False) -> None:

FILE: src/kimi_cli/utils/changelog.py
  class ReleaseEntry (line 7) | class ReleaseEntry(NamedTuple):
  function parse_changelog (line 12) | def parse_changelog(md_text: str) -> dict[str, ReleaseEntry]:
  function format_release_notes (line 91) | def format_release_notes(changelog: dict[str, ReleaseEntry], include_lib...

FILE: src/kimi_cli/utils/clipboard.py
  class ClipboardResult (line 21) | class ClipboardResult:
  function is_clipboard_available (line 32) | def is_clipboard_available() -> bool:
  function grab_media_from_clipboard (line 41) | def grab_media_from_clipboard() -> ClipboardResult | None:
  function _classify_file_paths (line 83) | def _classify_file_paths(
  function _read_clipboard_file_paths_macos_native (line 120) | def _read_clipboard_file_paths_macos_native() -> list[Path]:

FILE: src/kimi_cli/utils/datetime.py
  function format_relative_time (line 4) | def format_relative_time(timestamp: float) -> str:
  function format_duration (line 22) | def format_duration(seconds: int) -> str:

FILE: src/kimi_cli/utils/diff.py
  function format_unified_diff (line 11) | def format_unified_diff(
  function build_diff_blocks (line 63) | def build_diff_blocks(

FILE: src/kimi_cli/utils/editor.py
  function get_editor_command (line 25) | def get_editor_command(configured: str = "") -> list[str] | None:
  function edit_text_in_editor (line 53) | def edit_text_in_editor(text: str, configured: str = "") -> str | None:

FILE: src/kimi_cli/utils/environment.py
  class Environment (line 11) | class Environment:
    method detect (line 19) | async def detect() -> Environment:

FILE: src/kimi_cli/utils/envvar.py
  function get_env_bool (line 8) | def get_env_bool(name: str, default: bool = False) -> bool:

FILE: src/kimi_cli/utils/export.py
  function _is_checkpoint_message (line 38) | def _is_checkpoint_message(msg: Message) -> bool:
  function _is_internal_user_message (line 46) | def _is_internal_user_message(msg: Message) -> bool:
  function _extract_tool_call_hint (line 51) | def _extract_tool_call_hint(args_json: str) -> str:
  function _format_content_part_md (line 79) | def _format_content_part_md(part: ContentPart) -> str:
  function _format_tool_call_md (line 98) | def _format_tool_call_md(tool_call: ToolCall) -> str:
  function _format_tool_result_md (line 114) | def _format_tool_result_md(msg: Message, tool_name: str, hint: str) -> str:
  function _group_into_turns (line 139) | def _group_into_turns(history: Sequence[Message]) -> list[list[Message]]:
  function _format_turn_md (line 157) | def _format_turn_md(messages: list[Message], turn_number: int) -> str:
  function _build_overview (line 223) | def _build_overview(
  function build_export_markdown (line 251) | def build_export_markdown(
  function is_importable_file (line 370) | def is_importable_file(path_str: str) -> bool:
  function _stringify_content_parts (line 379) | def _stringify_content_parts(parts: Sequence[ContentPart]) -> str:
  function _stringify_tool_calls (line 401) | def _stringify_tool_calls(tool_calls: Sequence[ToolCall]) -> str:
  function stringify_context_history (line 415) | def stringify_context_history(history: Sequence[Message]) -> str:
  function perform_export (line 454) | async def perform_export(
  function is_sensitive_file (line 521) | def is_sensitive_file(filename: str) -> bool:
  function _validate_import_token_budget (line 527) | def _validate_import_token_budget(
  function resolve_import_source (line 553) | async def resolve_import_source(
  function build_import_message (line 637) | def build_import_message(content: str, source_desc: str) -> Message:
  function perform_import (line 654) | async def perform_import(

FILE: src/kimi_cli/utils/frontmatter.py
  function parse_frontmatter (line 9) | def parse_frontmatter(text: str) -> dict[str, Any] | None:
  function read_frontmatter (line 43) | def read_frontmatter(path: Path) -> dict[str, Any] | None:

FILE: src/kimi_cli/utils/io.py
  function atomic_json_write (line 11) | def atomic_json_write(data: Any, path: Path) -> None:

FILE: src/kimi_cli/utils/logging.py
  class StderrRedirector (line 15) | class StderrRedirector:
    method __init__ (line 16) | def __init__(self, level: str = "ERROR") -> None:
    method install (line 25) | def install(self) -> None:
    method uninstall (line 48) | def uninstall(self) -> None:
    method _drain (line 59) | def _drain(self) -> None:
    method _log_line (line 84) | def _log_line(self, line: str) -> None:
    method open_original_stderr_handle (line 90) | def open_original_stderr_handle(self) -> IO[bytes] | None:
  function redirect_stderr_to_logger (line 101) | def redirect_stderr_to_logger(level: str = "ERROR") -> None:
  function restore_stderr (line 108) | def restore_stderr() -> None:
  function open_original_stderr (line 114) | def open_original_stderr() -> Iterator[IO[bytes] | None]:

FILE: src/kimi_cli/utils/media_tags.py
  function _format_tag (line 9) | def _format_tag(tag: str, attrs: Mapping[str, str | None] | None = None)...
  function wrap_media_part (line 22) | def wrap_media_part(

FILE: src/kimi_cli/utils/message.py
  function message_stringify (line 8) | def message_stringify(message: Message) -> str:

FILE: src/kimi_cli/utils/path.py
  function _reserve_rotation_path (line 17) | async def _reserve_rotation_path(path: Path) -> bool:
  function next_available_rotation (line 31) | async def next_available_rotation(path: Path) -> Path | None:
  function list_directory (line 58) | async def list_directory(work_dir: KaosPath) -> str:
  function shorten_home (line 91) | def shorten_home(path: KaosPath) -> KaosPath:
  function sanitize_cli_path (line 103) | def sanitize_cli_path(raw: str) -> str:
  function is_within_directory (line 116) | def is_within_directory(path: KaosPath, directory: KaosPath) -> bool:
  function is_within_workspace (line 130) | def is_within_workspace(

FILE: src/kimi_cli/utils/proctitle.py
  function set_process_title (line 6) | def set_process_title(title: str) -> None:
  function set_terminal_title (line 16) | def set_terminal_title(title: str) -> None:
  function init_process_name (line 30) | def init_process_name(name: str = "Kimi Code") -> None:

FILE: src/kimi_cli/utils/rich/__init__.py
  function enable_character_wrap (line 15) | def enable_character_wrap() -> None:
  function restore_word_wrap (line 26) | def restore_word_wrap() -> None:

FILE: src/kimi_cli/utils/rich/columns.py
  class _ShrinkToWidth (line 10) | class _ShrinkToWidth:
    method __init__ (line 11) | def __init__(self, renderable: RenderableType, max_width: int) -> None:
    method __rich_measure__ (line 15) | def __rich_measure__(self, console: Console, options: ConsoleOptions) ...
    method __rich_console__ (line 19) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
    method _resolve_width (line 24) | def _resolve_width(self, options: ConsoleOptions) -> int:
  function _strip_trailing_spaces (line 28) | def _strip_trailing_spaces(segments: list[Segment]) -> list[Segment]:
  class BulletColumns (line 54) | class BulletColumns:
    method __init__ (line 55) | def __init__(
    method _bullet_renderable (line 68) | def _bullet_renderable(self) -> RenderableType:
    method _available_width (line 73) | def _available_width(self, console: Console, options: ConsoleOptions, ...
    method __rich_measure__ (line 78) | def __rich_measure__(self, console: Console, options: ConsoleOptions) ...
    method __rich_console__ (line 87) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...

FILE: src/kimi_cli/utils/rich/markdown.py
  function _strip_background (line 53) | def _strip_background(text: Text) -> Text:
  class MarkdownElement (line 86) | class MarkdownElement:
    method create (line 90) | def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
    method on_enter (line 102) | def on_enter(self, context: MarkdownContext) -> None:
    method on_text (line 109) | def on_text(self, context: MarkdownContext, text: TextType) -> None:
    method on_leave (line 116) | def on_leave(self, context: MarkdownContext) -> None:
    method on_child_close (line 123) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
    method __rich_console__ (line 137) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class UnknownElement (line 141) | class UnknownElement(MarkdownElement):
  class TextElement (line 150) | class TextElement(MarkdownElement):
    method on_enter (line 155) | def on_enter(self, context: MarkdownContext) -> None:
    method on_text (line 159) | def on_text(self, context: MarkdownContext, text: TextType) -> None:
    method on_leave (line 162) | def on_leave(self, context: MarkdownContext) -> None:
  class Paragraph (line 166) | class Paragraph(TextElement):
    method create (line 173) | def create(cls, markdown: Markdown, token: Token) -> Paragraph:
    method __init__ (line 176) | def __init__(self, justify: JustifyMethod) -> None:
    method __rich_console__ (line 179) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class Heading (line 184) | class Heading(TextElement):
    method create (line 188) | def create(cls, markdown: Markdown, token: Token) -> Heading:
    method on_enter (line 191) | def on_enter(self, context: MarkdownContext) -> None:
    method __init__ (line 195) | def __init__(self, tag: str) -> None:
    method __rich_console__ (line 200) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class CodeBlock (line 214) | class CodeBlock(TextElement):
    method create (line 220) | def create(cls, markdown: Markdown, token: Token) -> CodeBlock:
    method __init__ (line 225) | def __init__(self, lexer_name: str, theme: str | SyntaxTheme) -> None:
    method __rich_console__ (line 229) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class BlockQuote (line 246) | class BlockQuote(TextElement):
    method __init__ (line 251) | def __init__(self) -> None:
    method on_child_close (line 254) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
    method __rich_console__ (line 258) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class HorizontalRule (line 270) | class HorizontalRule(MarkdownElement):
    method __rich_console__ (line 275) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class TableElement (line 280) | class TableElement(MarkdownElement):
    method __init__ (line 283) | def __init__(self) -> None:
    method on_child_close (line 287) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
    method __rich_console__ (line 296) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class TableHeaderElement (line 311) | class TableHeaderElement(MarkdownElement):
    method __init__ (line 314) | def __init__(self) -> None:
    method on_child_close (line 317) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
  class TableBodyElement (line 323) | class TableBodyElement(MarkdownElement):
    method __init__ (line 326) | def __init__(self) -> None:
    method on_child_close (line 329) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
  class TableRowElement (line 335) | class TableRowElement(MarkdownElement):
    method __init__ (line 338) | def __init__(self) -> None:
    method on_child_close (line 341) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
  class TableDataElement (line 347) | class TableDataElement(MarkdownElement):
    method create (line 352) | def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
    method __init__ (line 368) | def __init__(self, justify: JustifyMethod) -> None:
    method on_text (line 372) | def on_text(self, context: MarkdownContext, text: TextType) -> None:
  class ListElement (line 378) | class ListElement(MarkdownElement):
    method create (line 382) | def create(cls, markdown: Markdown, token: Token) -> ListElement:
    method __init__ (line 385) | def __init__(self, list_type: str, list_start: int | None) -> None:
    method on_child_close (line 390) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
    method __rich_console__ (line 395) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class ListItem (line 406) | class ListItem(TextElement):
    method _line_starts_with_list_marker (line 412) | def _line_starts_with_list_marker(text: str) -> bool:
    method create (line 428) | def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
    method __init__ (line 433) | def __init__(self, indent: int = 0) -> None:
    method on_child_close (line 437) | def on_child_close(self, context: MarkdownContext, child: MarkdownElem...
    method render_bullet (line 441) | def render_bullet(self, console: Console, options: ConsoleOptions) -> ...
    method render_number (line 467) | def render_number(
  class Link (line 497) | class Link(TextElement):
    method create (line 499) | def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
    method __init__ (line 503) | def __init__(self, text: str, href: str):
  class ImageItem (line 508) | class ImageItem(TextElement):
    method create (line 514) | def create(cls, markdown: Markdown, token: Token) -> MarkdownElement:
    method __init__ (line 526) | def __init__(self, destination: str, hyperlinks: bool) -> None:
    method on_enter (line 532) | def on_enter(self, context: MarkdownContext) -> None:
    method __rich_console__ (line 537) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...
  class MarkdownContext (line 546) | class MarkdownContext:
    method __init__ (line 549) | def __init__(
    method current_style (line 569) | def current_style(self) -> Style:
    method on_text (line 573) | def on_text(self, text: str, node_type: str) -> None:
    method enter_style (line 584) | def enter_style(self, style_name: str | Style) -> Style:
    method leave_style (line 604) | def leave_style(self) -> Style:
  class Markdown (line 610) | class Markdown(JupyterMixin):
    method __init__ (line 647) | def __init__(
    method _flatten_tokens (line 667) | def _flatten_tokens(self, tokens: Iterable[Token]) -> Iterable[Token]:
    method __rich_console__ (line 677) | def __rich_console__(self, console: Console, options: ConsoleOptions) ...

FILE: src/kimi_cli/utils/rich/syntax.py
  function resolve_code_theme (line 80) | def resolve_code_theme(theme: str | SyntaxTheme) -> str | SyntaxTheme:
  class KimiSyntax (line 86) | class KimiSyntax(Syntax):
    method __init__ (line 87) | def __init__(self, code: str, lexer: str, **kwargs: Any) -> None:

FILE: src/kimi_cli/utils/signals.py
  function install_sigint_handler (line 9) | def install_sigint_handler(

FILE: src/kimi_cli/utils/slashcmd.py
  class SlashCommand (line 8) | class SlashCommand[F: Callable[..., None | Awaitable[None]]]:
    method slash_name (line 14) | def slash_name(self):
  class SlashCommandRegistry (line 21) | class SlashCommandRegistry[F: Callable[..., None | Awaitable[None]]]:
    method __init__ (line 24) | def __init__(self) -> None:
    method command (line 31) | def command(self, func: F, /) -> F: ...
    method command (line 34) | def command(
    method command (line 41) | def command(
    method find_command (line 88) | def find_command(self, name: str) -> SlashCommand[F] | None:
    method list_commands (line 91) | def list_commands(self) -> list[SlashCommand[F]]:
  class SlashCommandCall (line 97) | class SlashCommandCall:
  function parse_slash_command_call (line 103) | def parse_slash_command_call(user_input: str) -> SlashCommandCall | None:

FILE: src/kimi_cli/utils/string.py
  function shorten_middle (line 10) | def shorten_middle(text: str, width: int, remove_newline: bool = True) -...
  function random_string (line 19) | def random_string(length: int = 8) -> str:

FILE: src/kimi_cli/utils/subprocess_env.py
  function get_clean_env (line 23) | def get_clean_env(base_env: dict[str, str] | None = None) -> dict[str, s...

FILE: src/kimi_cli/utils/term.py
  function ensure_new_line (line 10) | def ensure_new_line() -> None:
  function ensure_tty_sane (line 28) | def ensure_tty_sane() -> None:
  function _cursor_position_unix (line 53) | def _cursor_position_unix() -> tuple[int, int] | None:
  function _cursor_column_unix (line 95) | def _cursor_column_unix() -> int | None:
  function _cursor_position_windows (line 100) | def _cursor_position_windows() -> tuple[int, int] | None:
  function _cursor_column_windows (line 142) | def _cursor_column_windows() -> int | None:
  function _write_newline (line 147) | def _write_newline() -> None:
  function get_cursor_row (line 152) | def get_cursor_row() -> int | None:

FILE: src/kimi_cli/utils/typing.py
  function flatten_union (line 5) | def flatten_union(tp: Any) -> tuple[Any, ...]:

FILE: src/kimi_cli/vis/api/sessions.py
  function collect_events (line 28) | def collect_events(
  function _get_imported_root (line 49) | def _get_imported_root() -> Path:
  function _find_session_dir (line 54) | def _find_session_dir(work_dir_hash: str, session_id: str) -> Path | None:
  function get_work_dir_for_hash (line 72) | def get_work_dir_for_hash(hash_dir_name: str) -> str | None:
  function _scan_session_dir (line 90) | def _scan_session_dir(
  function list_sessions (line 174) | def list_sessions() -> list[dict[str, Any]]:
  function get_wire_events (line 208) | async def get_wire_events(work_dir_hash: str, session_id: str) -> dict[s...
  function get_context_messages (line 246) | async def get_context_messages(work_dir_hash: str, session_id: str) -> d...
  function get_session_state (line 276) | async def get_session_state(work_dir_hash: str, session_id: str) -> dict...
  function get_session_summary (line 295) | async def get_session_summary(work_dir_hash: str, session_id: str) -> di...
  function download_session (line 402) | def download_session(work_dir_hash: str, session_id: str) -> StreamingRe...
  function import_session (line 424) | async def import_session(file: UploadFile) -> dict[str, Any]:
  function delete_session (line 491) | def delete_session(work_dir_hash: str, session_id: str) -> dict[str, str]:

FILE: src/kimi_cli/vis/api/statistics.py
  function get_statistics (line 25) | def get_statistics() -> dict[str, Any]:

FILE: src/kimi_cli/vis/api/system.py
  function get_capabilities (line 14) | def get_capabilities() -> dict[str, Any]:

FILE: src/kimi_cli/vis/app.py
  function create_app (line 25) | def create_app() -> FastAPI:
  function find_available_port (line 62) | def find_available_port(host: str, start_port: int, max_attempts: int = ...
  function _print_banner (line 78) | def _print_banner(lines: list[str]) -> None:
  function run_vis_server (line 115) | def run_vis_server(

FILE: src/kimi_cli/web/api/config.py
  class ConfigModel (line 16) | class ConfigModel(LLMModel):
  class GlobalConfig (line 23) | class GlobalConfig(BaseModel):
  class UpdateGlobalConfigRequest (line 31) | class UpdateGlobalConfigRequest(BaseModel):
  class UpdateGlobalConfigResponse (line 44) | class UpdateGlobalConfigResponse(BaseModel):
  class ConfigToml (line 56) | class ConfigToml(BaseModel):
  class UpdateConfigTomlRequest (line 63) | class UpdateConfigTomlRequest(BaseModel):
  class UpdateConfigTomlResponse (line 69) | class UpdateConfigTomlResponse(BaseModel):
  function _build_global_config (line 76) | def _build_global_config() -> GlobalConfig:
  function _get_runner (line 108) | def _get_runner(req: Request) -> KimiCLIRunner:
  function _ensure_sensitive_apis_allowed (line 113) | def _ensure_sensitive_apis_allowed(request: Request) -> None:
  function get_global_config (line 123) | async def get_global_config() -> GlobalConfig:
  function update_global_config (line 129) | async def update_global_config(
  function get_config_toml (line 178) | async def get_config_toml(http_request: Request) -> ConfigToml:
  function update_config_toml (line 188) | async def update_config_toml(

FILE: src/kimi_cli/web/api/open_in.py
  class OpenInRequest (line 19) | class OpenInRequest(BaseModel):
  class OpenInResponse (line 26) | class OpenInResponse(BaseModel):
  function _resolve_path (line 33) | def _resolve_path(path: str) -> Path:
  function _run_command (line 52) | def _run_command(args: list[str]) -> None:
  function _spawn_process (line 61) | def _spawn_process(args: list[str]) -> None:
  function _open_app (line 65) | def _open_app(app_name: str, path: Path, fallback: str | None = None) ->...
  function _open_terminal (line 76) | def _open_terminal(path: Path) -> None:
  function _open_iterm (line 81) | def _open_iterm(path: Path) -> None:
  function _open_windows_app (line 99) | def _open_windows_app(command: str, path: Path) -> None:
  function _open_windows_explorer (line 103) | def _open_windows_explorer(path: Path, *, is_file: bool) -> None:
  function _open_windows_terminal (line 110) | def _open_windows_terminal(path: Path) -> None:
  function _open_in_macos (line 118) | def _open_in_macos(app: OpenInRequest, path: Path, *, is_file: bool) -> ...
  function _open_in_windows (line 146) | def _open_in_windows(app: OpenInRequest, path: Path, *, is_file: bool) -...
  function _open_in_sync (line 169) | def _open_in_sync(request: OpenInRequest, path: Path, *, is_file: bool) ...
  function open_in (line 177) | async def open_in(request: OpenInRequest) -> OpenInResponse:

FILE: src/kimi_cli/web/api/sessions.py
  function sanitize_filename (line 97) | def sanitize_filename(filename: str) -> str:
  function get_runner (line 104) | def get_runner(req: Request) -> KimiCLIRunner:
  function get_runner_ws (line 109) | def get_runner_ws(ws: WebSocket) -> KimiCLIRunner:
  function get_editable_session (line 114) | def get_editable_session(
  function _relative_parts (line 135) | def _relative_parts(path: Path) -> list[str]:
  function _is_sensitive_relative_path (line 139) | def _is_sensitive_relative_path(rel_path: Path) -> bool:
  function _contains_symlink (line 149) | def _contains_symlink(path: Path, base: Path) -> bool:
  function _is_path_in_sensitive_location (line 163) | def _is_path_in_sensitive_location(path: Path) -> bool:
  function _ensure_public_file_access_allowed (line 177) | def _ensure_public_file_access_allowed(
  function _read_wire_lines (line 198) | def _read_wire_lines(wire_file: Path) -> list[str]:
  function replay_history (line 239) | async def replay_history(ws: WebSocket, session_dir: Path) -> None:
  function list_sessions (line 254) | async def list_sessions(
  function get_session (line 290) | async def get_session(
  function create_session (line 304) | async def create_session(request: CreateSessionRequest | None = None) ->...
  class CreateSessionRequest (line 362) | class CreateSessionRequest(BaseModel):
Condensed preview — 818 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (5,164K chars).
[
  {
    "path": ".agents/skills/codex-worker/SKILL.md",
    "chars": 4918,
    "preview": "---\nname: codex-worker\ndescription: Spawn and manage multiple Codex CLI agents via tmux to work on tasks in parallel. Us"
  },
  {
    "path": ".agents/skills/feature-smoke-test/SKILL.md",
    "chars": 3369,
    "preview": "---\nname: feature-smoke-test\ndescription: 针对 Kimi Code CLI 的新增或变更功能,规划并执行可重复的端到端冒烟测试。从 git diff 推断功能边界,读取相关文档和代码,设计测试 pr"
  },
  {
    "path": ".agents/skills/feature-smoke-test/references/prompt-patterns.md",
    "chars": 1157,
    "preview": "# Prompt 模板\n\n以下模板作为脚手架使用。运行前替换占位符。\n\n## 单轮还是多轮\n\n满足以下任一条件时使用多轮:\n\n- 功能有状态\n- 功能依赖时序或并发\n- 功能需要审批、清理或恢复\n- session 产物本身是证据的一部分\n"
  },
  {
    "path": ".agents/skills/feature-smoke-test/scripts/inspect_session.py",
    "chars": 11262,
    "preview": "#!/usr/bin/env python3\n\"\"\"Locate and summarize a Kimi CLI session for smoke-test review.\"\"\"\n\nfrom __future__ import anno"
  },
  {
    "path": ".agents/skills/gen-changelog/SKILL.md",
    "chars": 970,
    "preview": "---\nname: gen-changelog\ndescription: Generate changelog entries for code changes.\n---\n\n根据当前分支相对于 main 分支的修改,生成更新日志条目并同步到"
  },
  {
    "path": ".agents/skills/gen-docs/SKILL.md",
    "chars": 416,
    "preview": "---\nname: gen-docs\ndescription: Update Kimi Code CLI user documentation.\n---\n\n现在我们正在为当前项目 Kimi Code CLI 编写和维护用户文档,文档内容在 "
  },
  {
    "path": ".agents/skills/gen-rust/SKILL.md",
    "chars": 3445,
    "preview": "---\nname: gen-rust\ndescription: Sync Rust implementation with Python changes (exclude UI/login) by reviewing recent chan"
  },
  {
    "path": ".agents/skills/pull-request/SKILL.md",
    "chars": 429,
    "preview": "---\nname: pull-request\ndescription: Create and submit a GitHub Pull Request.\ntype: flow\n---\n\n```mermaid\nflowchart TB\n   "
  },
  {
    "path": ".agents/skills/release/SKILL.md",
    "chars": 2166,
    "preview": "---\nname: release\ndescription: Execute the release workflow for Kimi Code CLI packages.\ntype: flow\n---\n\n```d2\nunderstand"
  },
  {
    "path": ".agents/skills/translate-docs/SKILL.md",
    "chars": 396,
    "preview": "---\nname: translate-docs\ndescription: Translate and sync bilingual documentation.\n---\n\n现在我们正在为当前项目 Kimi Code CLI 编写和维护用户"
  },
  {
    "path": ".agents/skills/worktree-status/SKILL.md",
    "chars": 4211,
    "preview": "---\nname: worktree-status\ndescription: Audit all git worktrees in the current project. Use when the user asks about work"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/1-bug-report.yml",
    "chars": 2670,
    "preview": "name: Bug Report\ndescription: Report an issue that should be fixed\nlabels:\n  - bug\n  - needs triage\nbody:\n  - type: mark"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/2-feature-request.yml",
    "chars": 883,
    "preview": "name: Feature Request\ndescription: Propose a new feature for Kimi Code CLI\nlabels:\n  - enhancement\nbody:\n  - type: markd"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "chars": 202,
    "preview": "blank_issues_enabled: true\ncontact_links:\n  - name: Questions and General Discussion\n    url: https://github.com/Moonsho"
  },
  {
    "path": ".github/actions/macos-code-sign/action.yml",
    "chars": 5811,
    "preview": "name: macos-code-sign\ndescription: Sign and notarize macOS PyInstaller binaries\n\ninputs:\n  binary-path:\n    description:"
  },
  {
    "path": ".github/dependabot.yml",
    "chars": 451,
    "preview": "# To get started with Dependabot version updates, you'll need to specify which\n# package ecosystems to update and where "
  },
  {
    "path": ".github/pr-title-checker-config.json",
    "chars": 362,
    "preview": "{\n    \"LABEL\": {\n        \"name\": \"Invalid PR Title\",\n        \"color\": \"B60205\"\n    },\n    \"CHECKS\": {\n        \"regexp\": "
  },
  {
    "path": ".github/pull_request_template.md",
    "chars": 962,
    "preview": "<!--\nThank you for your contribution to Kimi Code CLI!\nPlease make sure you already discussed the feature or bugfix you "
  },
  {
    "path": ".github/workflows/ci-docs.yml",
    "chars": 1054,
    "preview": "name: CI (docs)\n\non:\n  pull_request:\n    paths:\n      - \".github/workflows/ci-docs.yml\"\n      - \".github/workflows/docs-"
  },
  {
    "path": ".github/workflows/ci-kimi-cli.yml",
    "chars": 8357,
    "preview": "name: CI (kimi-cli)\n\non:\n  pull_request:\n    paths:\n      - \".github/workflows/**\"\n      - \"packages/**\"\n      - \"src/**"
  },
  {
    "path": ".github/workflows/ci-kimi-sdk.yml",
    "chars": 2368,
    "preview": "name: CI (kimi-sdk)\n\non:\n  pull_request:\n    paths:\n      - \".github/workflows/ci-kimi-sdk.yml\"\n      - \"sdks/kimi-sdk/*"
  },
  {
    "path": ".github/workflows/ci-kosong.yml",
    "chars": 2368,
    "preview": "name: CI (kosong)\n\non:\n  pull_request:\n    paths:\n      - \".github/workflows/ci-kosong.yml\"\n      - \"packages/kosong/**\""
  },
  {
    "path": ".github/workflows/ci-pykaos.yml",
    "chars": 3459,
    "preview": "name: CI (pykaos)\n\non:\n  pull_request:\n    paths:\n      - \".github/workflows/ci-pykaos.yml\"\n      - \"packages/kaos/**\"\n "
  },
  {
    "path": ".github/workflows/docs-pages.yml",
    "chars": 1863,
    "preview": "name: Docs (GitHub Pages)\n\non:\n  push:\n    branches:\n      - main\n\npermissions:\n  contents: read\n  pages: write\n  id-tok"
  },
  {
    "path": ".github/workflows/pr-title-checker.yml",
    "chars": 345,
    "preview": "name: PR Title Checker\n\non:\n  pull_request:\n    types: [opened, edited, labeled]\n\njobs:\n  check:\n    runs-on: ubuntu-lat"
  },
  {
    "path": ".github/workflows/release-kimi-cli.yml",
    "chars": 18319,
    "preview": "name: Release (kimi-cli)\n\non:\n  push:\n    tags:\n      - \"[0-9]*\"\n\npermissions:\n  contents: write\n\njobs:\n  validate:\n    "
  },
  {
    "path": ".github/workflows/release-kimi-sdk.yml",
    "chars": 1287,
    "preview": "name: Release (kimi-sdk)\n\non:\n  push:\n    tags:\n      - \"kimi-sdk-*\"\n\npermissions:\n  contents: read\n\njobs:\n  validate:\n "
  },
  {
    "path": ".github/workflows/release-kosong.yml",
    "chars": 3153,
    "preview": "name: Release (kosong)\n\non:\n  push:\n    tags:\n      - \"kosong-*\"\n\npermissions:\n  contents: read\n\njobs:\n  validate:\n    n"
  },
  {
    "path": ".github/workflows/release-pykaos.yml",
    "chars": 1277,
    "preview": "name: Release (pykaos)\n\non:\n  push:\n    tags:\n      - \"pykaos-*\"\n\npermissions:\n  contents: read\n\njobs:\n  validate:\n    n"
  },
  {
    "path": ".github/workflows/translator.yml",
    "chars": 659,
    "preview": "name: \"Translator\"\non:\n  issues:\n    types: [opened, edited]\n  issue_comment:\n    types: [created, edited]\n  discussion:"
  },
  {
    "path": ".github/workflows/typos.yml",
    "chars": 283,
    "preview": "name: Typo checker\non: [pull_request]\n\njobs:\n  run:\n    name: Spell Check with Typos\n    runs-on: ubuntu-latest\n    step"
  },
  {
    "path": ".gitignore",
    "chars": 518,
    "preview": "# Python-generated files\n__pycache__/\n*.py[oc]\nbuild/\ndist/\nwheels/\n*.egg-info\n\n# Virtual environments\n.venv\n\n# Project "
  },
  {
    "path": ".pre-commit-config.yaml",
    "chars": 393,
    "preview": "default_install_hook_types:\n  - pre-commit\n\nrepos:\n  - repo: local\n    hooks:\n      - id: make-format-kimi-cli\n        n"
  },
  {
    "path": ".python-version",
    "chars": 5,
    "preview": "3.14\n"
  },
  {
    "path": "AGENTS.md",
    "chars": 6729,
    "preview": "# Kimi Code CLI\n\n## Quick commands (use uv)\n\n- `make prepare` (sync deps for all workspace packages and install git hook"
  },
  {
    "path": "CHANGELOG.md",
    "chars": 50478,
    "preview": "# Changelog\n\n<!--\nRelease notes will be parsed and available as /release-notes\nThe parser extracts for each version:\n  -"
  },
  {
    "path": "CONTRIBUTING.md",
    "chars": 1785,
    "preview": "# Contributing to Kimi Code CLI\n\nThank you for being interested in contributing to Kimi Code CLI!\n\nWe welcome all kinds "
  },
  {
    "path": "LICENSE",
    "chars": 11357,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "Makefile",
    "chars": 8086,
    "preview": ".DEFAULT_GOAL := prepare\n\n.PHONY: help\nhelp: ## Show available make targets.\n\t@echo \"Available make targets:\"\n\t@awk 'BEG"
  },
  {
    "path": "NOTICE",
    "chars": 408,
    "preview": "Kimi Code CLI\nCopyright 2025 Moonshot AI\n\nThis product includes software developed at\nMoonshot AI (https://www.moonshot."
  },
  {
    "path": "README.md",
    "chars": 5702,
    "preview": "# Kimi Code CLI\n\n[![Commit Activity](https://img.shields.io/github/commit-activity/w/MoonshotAI/kimi-cli)](https://githu"
  },
  {
    "path": "SECURITY.md",
    "chars": 373,
    "preview": "# Security Policy\n\n## Supported Versions\n\nCurrently, Kimi CLI only provides security support for the latest version.\n\n##"
  },
  {
    "path": "docs/.gitignore",
    "chars": 49,
    "preview": "node_modules/\n.vitepress/cache/\n.vitepress/dist/\n"
  },
  {
    "path": "docs/.pre-commit-config.yaml",
    "chars": 69,
    "preview": "orphan: true\n\n# Docs changes do not need pre-commit hooks.\nrepos: []\n"
  },
  {
    "path": "docs/.vitepress/config.ts",
    "chars": 7888,
    "preview": "import { defineConfig } from 'vitepress'\nimport { withMermaid } from 'vitepress-plugin-mermaid'\nimport llmstxt from 'vit"
  },
  {
    "path": "docs/.vitepress/theme/index.ts",
    "chars": 93,
    "preview": "import DefaultTheme from 'vitepress/theme'\nimport './style.css'\n\nexport default DefaultTheme\n"
  },
  {
    "path": "docs/.vitepress/theme/style.css",
    "chars": 337,
    "preview": ":root {\n  --vp-c-brand-1: rgb(52, 118, 246);\n  --vp-c-brand-2: rgb(72, 138, 255);\n  --vp-c-brand-3: rgb(92, 158, 255);\n "
  },
  {
    "path": "docs/AGENTS.md",
    "chars": 8367,
    "preview": "# Documentation Agent Guide\n\nThis repository uses VitePress for the documentation site. The current docs are structural "
  },
  {
    "path": "docs/en/configuration/config-files.md",
    "chars": 6872,
    "preview": "# Config Files\n\nKimi Code CLI uses configuration files to manage API providers, models, services, and runtime parameters"
  },
  {
    "path": "docs/en/configuration/data-locations.md",
    "chars": 6092,
    "preview": "# Data Locations\n\nKimi Code CLI stores all data in the `~/.kimi/` directory under the user's home directory. This page d"
  },
  {
    "path": "docs/en/configuration/env-vars.md",
    "chars": 4388,
    "preview": "# Environment Variables\n\nKimi Code CLI supports overriding configuration or controlling runtime behavior through environ"
  },
  {
    "path": "docs/en/configuration/overrides.md",
    "chars": 2926,
    "preview": "# Config Overrides\n\nKimi Code CLI configuration can be set through multiple methods, with different sources overriding e"
  },
  {
    "path": "docs/en/configuration/providers.md",
    "chars": 4605,
    "preview": "# Providers and Models\n\nKimi Code CLI supports multiple LLM platforms, which can be configured via configuration files o"
  },
  {
    "path": "docs/en/customization/agents.md",
    "chars": 15868,
    "preview": "# Agents and Subagents\n\nAn agent defines the AI's behavior, including system prompts, available tools, and subagents. Yo"
  },
  {
    "path": "docs/en/customization/mcp.md",
    "chars": 4115,
    "preview": "# Model Context Protocol\n\n[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) is an open protocol that allo"
  },
  {
    "path": "docs/en/customization/print-mode.md",
    "chars": 4131,
    "preview": "# Print Mode\n\nPrint mode lets Kimi Code CLI run non-interactively, suitable for scripting and automation scenarios.\n\n## "
  },
  {
    "path": "docs/en/customization/skills.md",
    "chars": 9424,
    "preview": "# Agent Skills\n\n[Agent Skills](https://agentskills.io/) is an open format for adding specialized knowledge and workflows"
  },
  {
    "path": "docs/en/customization/wire-mode.md",
    "chars": 27700,
    "preview": "# Wire mode\n\nWire mode is Kimi Code CLI's low-level communication protocol for structured bidirectional communication wi"
  },
  {
    "path": "docs/en/faq.md",
    "chars": 5835,
    "preview": "# FAQ\n\n## Installation and authentication\n\n### Empty model list during `/login`\n\nIf you see \"No models available for the"
  },
  {
    "path": "docs/en/guides/getting-started.md",
    "chars": 3562,
    "preview": "# Getting Started\n\n## What is Kimi Code CLI\n\nKimi Code CLI is an AI agent that runs in the terminal, helping you complet"
  },
  {
    "path": "docs/en/guides/ides.md",
    "chars": 1723,
    "preview": "# Using in IDEs\n\nKimi Code CLI supports integration with IDEs through the [Agent Client Protocol (ACP)](https://agentcli"
  },
  {
    "path": "docs/en/guides/integrations.md",
    "chars": 928,
    "preview": "# Integrations with Tools\n\nBesides using in the terminal and IDEs, Kimi Code CLI can also be integrated with other tools"
  },
  {
    "path": "docs/en/guides/interaction.md",
    "chars": 9882,
    "preview": "# Interaction and Input\n\nKimi Code CLI provides rich interaction features to help you collaborate efficiently with AI.\n\n"
  },
  {
    "path": "docs/en/guides/sessions.md",
    "chars": 4601,
    "preview": "# Sessions and Context\n\nKimi Code CLI automatically saves your conversation history, allowing you to continue previous w"
  },
  {
    "path": "docs/en/guides/use-cases.md",
    "chars": 3054,
    "preview": "# Common Use Cases\n\nKimi Code CLI can help you complete various software development and general tasks. Here are some ty"
  },
  {
    "path": "docs/en/index.md",
    "chars": 305,
    "preview": "---\nlayout: home\nhero:\n  name: Kimi Code CLI\n  text: Intelligent Command Line Assistant\n  tagline: Technical Preview\n  a"
  },
  {
    "path": "docs/en/reference/keyboard.md",
    "chars": 5957,
    "preview": "# Keyboard Shortcuts\n\nKimi Code CLI shell mode supports the following keyboard shortcuts.\n\n## Shortcuts list\n\n| Shortcut"
  },
  {
    "path": "docs/en/reference/kimi-acp.md",
    "chars": 878,
    "preview": "# `kimi acp` Subcommand\n\nThe `kimi acp` command starts a multi-session ACP (Agent Client Protocol) server.\n\n```sh\nkimi a"
  },
  {
    "path": "docs/en/reference/kimi-command.md",
    "chars": 8925,
    "preview": "# `kimi` Command\n\n`kimi` is the main command for Kimi Code CLI, used to start interactive sessions or execute single que"
  },
  {
    "path": "docs/en/reference/kimi-info.md",
    "chars": 818,
    "preview": "# `kimi info` Subcommand\n\n`kimi info` displays version and protocol information for Kimi Code CLI.\n\n```sh\nkimi info [--j"
  },
  {
    "path": "docs/en/reference/kimi-mcp.md",
    "chars": 2380,
    "preview": "# `kimi mcp` Subcommand\n\n`kimi mcp` is used to manage MCP (Model Context Protocol) server configurations. For concepts a"
  },
  {
    "path": "docs/en/reference/kimi-term.md",
    "chars": 1300,
    "preview": "# `kimi term` Subcommand\n\nThe `kimi term` command launches the [Toad](https://github.com/batrachianai/toad) terminal UI,"
  },
  {
    "path": "docs/en/reference/kimi-vis.md",
    "chars": 2977,
    "preview": "# Agent Tracing Visualizer\n\n::: warning Note\nAgent Tracing Visualizer is currently in Technical Preview and may be unsta"
  },
  {
    "path": "docs/en/reference/kimi-web.md",
    "chars": 13887,
    "preview": "# Web UI\n\nWeb UI provides a browser-based interactive interface, allowing you to use all features of Kimi Code CLI in a "
  },
  {
    "path": "docs/en/reference/slash-commands.md",
    "chars": 10314,
    "preview": "# Slash Commands\n\nSlash commands are built-in commands for Kimi Code CLI, used to control sessions, configuration, and d"
  },
  {
    "path": "docs/en/release-notes/breaking-changes.md",
    "chars": 5345,
    "preview": "# Breaking changes and migration\n\nThis page documents breaking changes in Kimi Code CLI releases and provides migration "
  },
  {
    "path": "docs/en/release-notes/changelog.md",
    "chars": 49403,
    "preview": "# Changelog\n\nThis page documents the changes in each Kimi Code CLI release.\n\n## Unreleased\n\n- Shell: Show the current wo"
  },
  {
    "path": "docs/index.md",
    "chars": 476,
    "preview": "---\nlayout: home\nhero:\n  name: Kimi Code CLI\n  text: ' '\n  actions:\n    - theme: brand\n      text: 简体中文\n      link: /zh/"
  },
  {
    "path": "docs/package.json",
    "chars": 450,
    "preview": "{\n  \"name\": \"kimi-cli-docs\",\n  \"private\": true,\n  \"type\": \"module\",\n  \"scripts\": {\n    \"sync\": \"node scripts/sync-change"
  },
  {
    "path": "docs/scripts/sync-changelog.mjs",
    "chars": 1504,
    "preview": "#!/usr/bin/env node\n/**\n * Sync CHANGELOG.md to docs/en/release-notes/changelog.md\n *\n * This script copies the content "
  },
  {
    "path": "docs/zh/configuration/config-files.md",
    "chars": 4733,
    "preview": "# 配置文件\n\nKimi Code CLI 使用配置文件管理 API 供应商、模型、服务和运行参数,支持 TOML 和 JSON 两种格式。\n\n## 配置文件位置\n\n默认配置文件位于 `~/.kimi/config.toml`。首次运行时,"
  },
  {
    "path": "docs/zh/configuration/data-locations.md",
    "chars": 3417,
    "preview": "# 数据路径\n\nKimi Code CLI 将所有数据存储在用户主目录下的 `~/.kimi/` 目录中。本页介绍各类数据文件的位置和用途。\n\n::: tip 提示\n可以通过设置 `KIMI_SHARE_DIR` 环境变量来自定义共享目录路"
  },
  {
    "path": "docs/zh/configuration/env-vars.md",
    "chars": 2651,
    "preview": "# 环境变量\n\nKimi Code CLI 支持通过环境变量覆盖配置或控制运行行为。本页列出所有支持的环境变量。\n\n关于环境变量如何覆盖配置文件的详细说明,请参阅 [配置覆盖](./overrides.md)。\n\n## Kimi 环境变量\n"
  },
  {
    "path": "docs/zh/configuration/overrides.md",
    "chars": 1702,
    "preview": "# 配置覆盖\n\nKimi Code CLI 的配置可以通过多种方式设置,不同来源的配置按优先级覆盖。\n\n## 优先级\n\n配置的优先级从高到低为:\n\n1. **环境变量** - 最高优先级,用于临时覆盖或 CI/CD 环境\n2. **CLI "
  },
  {
    "path": "docs/zh/configuration/providers.md",
    "chars": 2961,
    "preview": "# 平台与模型\n\nKimi Code CLI 支持多种 LLM 平台,可以通过配置文件或 `/login` 命令进行配置。\n\n## 平台选择\n\n最简单的配置方式是在 Shell 模式下运行 `/login` 命令(别名 `/setup`),"
  },
  {
    "path": "docs/zh/customization/agents.md",
    "chars": 9531,
    "preview": "# Agent 与子 Agent\n\nAgent 定义了 AI 的行为方式,包括系统提示词、可用工具和子 Agent。你可以使用内置 Agent,也可以创建自定义 Agent。\n\n## 内置 Agent\n\nKimi Code CLI 提供两个"
  },
  {
    "path": "docs/zh/customization/mcp.md",
    "chars": 2345,
    "preview": "# Model Context Protocol\n\n[Model Context Protocol (MCP)](https://modelcontextprotocol.io/) 是一个开放协议,让 AI 模型可以安全地与外部工具和数据源"
  },
  {
    "path": "docs/zh/customization/print-mode.md",
    "chars": 2702,
    "preview": "# Print 模式\n\nPrint 模式让 Kimi Code CLI 以非交互方式运行,适合脚本调用和自动化场景。\n\n## 基本用法\n\n使用 `--print` 参数启用 Print 模式:\n\n```sh\n# 通过 -p 传入指令(或 -"
  },
  {
    "path": "docs/zh/customization/skills.md",
    "chars": 5058,
    "preview": "# Agent Skills\n\n[Agent Skills](https://agentskills.io/) 是一个开放格式,用于为 AI Agent 添加专业知识和工作流程。Kimi Code CLI 支持加载 Agent Skills"
  },
  {
    "path": "docs/zh/customization/wire-mode.md",
    "chars": 20204,
    "preview": "# Wire 模式\n\nWire 模式是 Kimi Code CLI 的底层通信协议,用于与外部程序进行结构化的双向通信。\n\n## Wire 是什么\n\nWire 是 Kimi Code CLI 内部使用的消息传递层。当你使用终端交互时,She"
  },
  {
    "path": "docs/zh/faq.md",
    "chars": 3171,
    "preview": "# 常见问题\n\n## 安装与鉴权\n\n### `/login` 时模型列表为空\n\n如果在运行 `/login`(或 `/setup`)命令时看到 \"No models available for the selected platform\" "
  },
  {
    "path": "docs/zh/guides/getting-started.md",
    "chars": 1971,
    "preview": "# 开始使用\n\n## Kimi Code CLI 是什么\n\nKimi Code CLI 是一个运行在终端中的 AI Agent,帮助你完成软件开发任务和终端操作。它可以阅读和编辑代码、执行 Shell 命令、搜索和抓取网页,并在执行过程中自"
  },
  {
    "path": "docs/zh/guides/ides.md",
    "chars": 1121,
    "preview": "# 在 IDE 中使用\n\nKimi Code CLI 支持通过 [Agent Client Protocol (ACP)](https://agentclientprotocol.com/) 集成到 IDE 中,让你在编辑器内直接使用 AI"
  },
  {
    "path": "docs/zh/guides/integrations.md",
    "chars": 625,
    "preview": "# 集成到工具\n\n除了在终端和 IDE 中使用,Kimi Code CLI 还可以集成到其他工具中。\n\n## Zsh 插件\n\n[zsh-kimi-cli](https://github.com/MoonshotAI/zsh-kimi-cli"
  },
  {
    "path": "docs/zh/guides/interaction.md",
    "chars": 4130,
    "preview": "# 交互与输入\n\nKimi Code CLI 提供了丰富的交互功能,帮助你高效地与 AI 协作。\n\n## Agent 与 Shell 模式\n\nKimi Code CLI 有两种输入模式:\n\n- **Agent 模式**:默认模式,输入的内容"
  },
  {
    "path": "docs/zh/guides/sessions.md",
    "chars": 1885,
    "preview": "# 会话与上下文\n\nKimi Code CLI 会自动保存你的对话历史,方便你随时继续之前的工作。\n\n## 会话续接\n\n每次启动 Kimi Code CLI 时,都会创建一个新的会话。在运行过程中,你也可以输入 `/new` 命令随时创建并"
  },
  {
    "path": "docs/zh/guides/use-cases.md",
    "chars": 1384,
    "preview": "# 常见使用案例\n\nKimi Code CLI 可以帮助你完成多种软件开发和通用任务,以下是一些典型场景。\n\n## 实现新功能\n\n当你需要为项目添加新功能时,直接用自然语言描述需求即可。Kimi Code CLI 会自动阅读相关代码、理解项"
  },
  {
    "path": "docs/zh/index.md",
    "chars": 256,
    "preview": "---\nlayout: home\nhero:\n  name: Kimi Code CLI\n  text: 你的终端智能助手\n  tagline: 技术预览版\n  actions:\n    - theme: brand\n      text:"
  },
  {
    "path": "docs/zh/reference/keyboard.md",
    "chars": 2773,
    "preview": "# 键盘快捷键\n\nKimi Code CLI Shell 模式支持以下键盘快捷键。\n\n## 快捷键列表\n\n| 快捷键 | 功能 |\n|--------|------|\n| `Ctrl-X` | 切换 Agent/Shell 模式 |\n| `"
  },
  {
    "path": "docs/zh/reference/kimi-acp.md",
    "chars": 434,
    "preview": "# `kimi acp` 子命令\n\n`kimi acp` 命令启动一个支持多会话的 ACP (Agent Client Protocol) 服务器。\n\n```sh\nkimi acp\n```\n\n## 说明\n\nACP 是一种标准化协议,允许 I"
  },
  {
    "path": "docs/zh/reference/kimi-command.md",
    "chars": 5344,
    "preview": "# `kimi` 命令\n\n`kimi` 是 Kimi Code CLI 的主命令,用于启动交互式会话或执行单次查询。\n\n```sh\nkimi [OPTIONS] COMMAND [ARGS]\n```\n\n## 基本信息\n\n| 选项 | 简写 "
  },
  {
    "path": "docs/zh/reference/kimi-info.md",
    "chars": 647,
    "preview": "# `kimi info` 子命令\n\n`kimi info` 显示 Kimi Code CLI 的版本和协议信息。\n\n```sh\nkimi info [--json]\n```\n\n## 选项\n\n| 选项 | 说明 |\n|------|----"
  },
  {
    "path": "docs/zh/reference/kimi-mcp.md",
    "chars": 1457,
    "preview": "# `kimi mcp` 子命令\n\n`kimi mcp` 用于管理 MCP (Model Context Protocol) 服务器配置。关于 MCP 的概念和使用方式,详见 [Model Context Protocol](../cust"
  },
  {
    "path": "docs/zh/reference/kimi-term.md",
    "chars": 817,
    "preview": "# `kimi term` 子命令\n\n`kimi term` 命令启动 [Toad](https://github.com/batrachianai/toad) 终端 UI,这是一个基于 [Textual](https://textual."
  },
  {
    "path": "docs/zh/reference/kimi-vis.md",
    "chars": 1446,
    "preview": "# Agent Tracing Visualizer\n\n::: warning 注意\nAgent Tracing Visualizer 目前为技术预览版(Technical Preview),功能和界面可能在后续版本中发生变化。\n:::\n\n"
  },
  {
    "path": "docs/zh/reference/kimi-web.md",
    "chars": 6796,
    "preview": "# Web UI\n\nWeb UI 提供了基于浏览器的交互界面,让你可以在网页中使用 Kimi Code CLI 的所有功能。相比终端界面,Web UI 提供了更丰富的视觉体验、更灵活的会话管理以及更便捷的文件操作。\n\n## 启动 Web U"
  },
  {
    "path": "docs/zh/reference/slash-commands.md",
    "chars": 4724,
    "preview": "# 斜杠命令\n\n斜杠命令是 Kimi Code CLI 的内置命令,用于控制会话、配置和调试。在输入框中输入 `/` 开头的命令即可触发。\n\n::: tip Shell 模式\n部分斜杠命令在 Shell 模式下也可以使用,包括 `/help"
  },
  {
    "path": "docs/zh/release-notes/breaking-changes.md",
    "chars": 3373,
    "preview": "# 破坏性变更与迁移说明\n\n本页面记录 Kimi Code CLI 各版本中的破坏性变更及对应的迁移指引。\n\n## 未发布\n\n## 0.81 - Prompt Flow 被 Flow Skills 取代\n\n### `--prompt-flo"
  },
  {
    "path": "docs/zh/release-notes/changelog.md",
    "chars": 26689,
    "preview": "# 变更记录\n\n本页面记录 Kimi Code CLI 各版本的变更内容。\n\n## 未发布\n\n- Shell:在提示工具栏中显示当前工作目录、Git 分支、脏状态以及与远端的 ahead/behind 同步状态\n- Shell:在工具栏中显"
  },
  {
    "path": "examples/.gitignore",
    "chars": 8,
    "preview": "uv.lock\n"
  },
  {
    "path": "examples/custom-echo-soul/README.md",
    "chars": 237,
    "preview": "# Example: Custom Echo Soul\n\nThis example demonstrates how to write a custom `Soul` (agent loop) implementation that can"
  },
  {
    "path": "examples/custom-echo-soul/main.py",
    "chars": 1196,
    "preview": "import asyncio\nfrom typing import Any\n\nfrom kimi_cli.llm import ALL_MODEL_CAPABILITIES, ModelCapability\nfrom kimi_cli.so"
  },
  {
    "path": "examples/custom-echo-soul/pyproject.toml",
    "chars": 232,
    "preview": "[project]\nname = \"custom-echo-soul\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nreq"
  },
  {
    "path": "examples/custom-kimi-soul/README.md",
    "chars": 238,
    "preview": "# Example: Custom Kimi Soul\n\nThis example demonstrates how to extend the `KimiSoul` (builtin agent loop) to customize it"
  },
  {
    "path": "examples/custom-kimi-soul/main.py",
    "chars": 3373,
    "preview": "import asyncio\nimport os\nfrom pathlib import Path\nfrom typing import override\n\nfrom kaos.path import KaosPath\nfrom koson"
  },
  {
    "path": "examples/custom-kimi-soul/pyproject.toml",
    "chars": 232,
    "preview": "[project]\nname = \"custom-kimi-soul\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nreq"
  },
  {
    "path": "examples/custom-tools/README.md",
    "chars": 204,
    "preview": "# Example: Custom Tools\n\nThis example demonstrates how to write custom tools for Kimi Code CLI and add them to your agen"
  },
  {
    "path": "examples/custom-tools/main.py",
    "chars": 560,
    "preview": "import asyncio\nfrom pathlib import Path\n\nfrom kaos.path import KaosPath\n\nfrom kimi_cli.app import KimiCLI, enable_loggin"
  },
  {
    "path": "examples/custom-tools/my_tools/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "examples/custom-tools/my_tools/ls.py",
    "chars": 771,
    "preview": "from kosong.tooling import CallableTool2, ToolError, ToolOk, ToolReturnValue\nfrom pydantic import BaseModel, Field\n\n\ncla"
  },
  {
    "path": "examples/custom-tools/myagent.yaml",
    "chars": 453,
    "preview": "version: 1\nagent:\n  extend: default\n  tools:\n    - \"kimi_cli.tools.multiagent:Task\"\n    - \"kimi_cli.tools.todo:SetTodoLi"
  },
  {
    "path": "examples/custom-tools/pyproject.toml",
    "chars": 228,
    "preview": "[project]\nname = \"custom-tools\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\"\nrequire"
  },
  {
    "path": "examples/kimi-cli-stream-json/README.md",
    "chars": 237,
    "preview": "# Example: Kimi Code CLI Stream JSON\n\nThis example demonstrates how to run Kimi Code CLI in a subprocess and interact wi"
  },
  {
    "path": "examples/kimi-cli-stream-json/main.py",
    "chars": 1032,
    "preview": "import asyncio\nimport json\nimport os\n\nKIMI_CLI_COMMAND = \"uv run --project ../../ kimi\"\n\n\nasync def main():\n    proc = a"
  },
  {
    "path": "examples/kimi-cli-stream-json/pyproject.toml",
    "chars": 166,
    "preview": "[project]\nname = \"kimi-cli-stream-json\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.md\""
  },
  {
    "path": "examples/kimi-cli-wire-messages/README.md",
    "chars": 224,
    "preview": "# Example: Kimi Code CLI Wire Messages\n\nThis example demonstrates how to create and run a Kimi Code CLI instance with ra"
  },
  {
    "path": "examples/kimi-cli-wire-messages/main.py",
    "chars": 623,
    "preview": "import asyncio\n\nfrom kaos.path import KaosPath\nfrom rich import print\n\nfrom kimi_cli.app import KimiCLI, enable_logging\n"
  },
  {
    "path": "examples/kimi-cli-wire-messages/pyproject.toml",
    "chars": 247,
    "preview": "[project]\nname = \"kimi-cli-wire-messages\"\nversion = \"0.1.0\"\ndescription = \"Add your description here\"\nreadme = \"README.m"
  },
  {
    "path": "examples/kimi-psql/README.md",
    "chars": 850,
    "preview": "# kimi-psql\n\nAI-assisted PostgreSQL interactive terminal.\n\n## Features\n\n- **AI Mode** (default): Natural language to SQL"
  },
  {
    "path": "examples/kimi-psql/agent.yaml",
    "chars": 1511,
    "preview": "version: 1\n\nagent:\n  extend: default\n  name: kimi-psql\n  system_prompt_args:\n    ROLE_ADDITIONAL: |\n      You are now a "
  },
  {
    "path": "examples/kimi-psql/main.py",
    "chars": 22156,
    "preview": "\"\"\"\nkimi-psql: AI-assisted PostgreSQL interactive terminal.\n\nUsage:\n    uv run main.py -h localhost -p 5432 -U postgres "
  },
  {
    "path": "examples/kimi-psql/pyproject.toml",
    "chars": 284,
    "preview": "[project]\nname = \"kimi-psql\"\nversion = \"0.1.0\"\ndescription = \"AI-assisted PostgreSQL interactive terminal\"\nreadme = \"REA"
  },
  {
    "path": "examples/sample-plugin/SKILL.md",
    "chars": 640,
    "preview": "---\nname: sample-plugin\ndescription: |\n  Sample plugin demonstrating the Skills + Tools model.\n  Includes a Python tool "
  },
  {
    "path": "examples/sample-plugin/plugin.json",
    "chars": 1178,
    "preview": "{\n  \"name\": \"sample-plugin\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Sample plugin demonstrating Skills + Tools with bot"
  },
  {
    "path": "examples/sample-plugin/scripts/calc.ts",
    "chars": 762,
    "preview": "#!/usr/bin/env npx tsx\n/** TypeScript tool: evaluate a math expression. */\n\nconst chunks: Buffer[] = [];\nprocess.stdin.o"
  },
  {
    "path": "examples/sample-plugin/scripts/greet.py",
    "chars": 433,
    "preview": "#!/usr/bin/env python3\n\"\"\"Python tool: generate a greeting message.\"\"\"\n\nimport json\nimport sys\n\nGREETINGS = {\n    \"en\": "
  },
  {
    "path": "flake.nix",
    "chars": 4313,
    "preview": "{\n  description = \"kimi-cli flake\";\n  inputs = {\n    nixpkgs.url = \"github:nixos/nixpkgs?ref=nixpkgs-unstable\";\n    syst"
  },
  {
    "path": "kimi.spec",
    "chars": 2036,
    "preview": "# -*- mode: python ; coding: utf-8 -*-\n\nimport os\nfrom kimi_cli.utils.pyinstaller import datas, hiddenimports\n\n# Read co"
  },
  {
    "path": "klips/.pre-commit-config.yaml",
    "chars": 69,
    "preview": "orphan: true\n\n# Docs changes do not need pre-commit hooks.\nrepos: []\n"
  },
  {
    "path": "klips/klip-0-klip.md",
    "chars": 3777,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-07\nStatus: Implemented\n---\n\n# KLIP-0: Kimi CLI Improvement Proposal\n\n## Kimi CLI 的"
  },
  {
    "path": "klips/klip-1-kimi-cli-monorepo.md",
    "chars": 5278,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2025-12-29\nStatus: Implemented\n---\n\n# KLIP-1: Move Kosong and PyKAOS to Kimi CLI Monorepo\n"
  },
  {
    "path": "klips/klip-10-agent-flow.md",
    "chars": 8309,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-20\nStatus: Implemented\n---\n\n# KLIP-10: Agent Flow (Agent Skill 扩展)\n\n## 背景\n\n当前 Kimi"
  },
  {
    "path": "klips/klip-11-kimi-code-rename.md",
    "chars": 2006,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-26\nStatus: Implemented\n---\n\n# KLIP-11: Rebrand Kimi CLI -> Kimi Code CLI (Docs + U"
  },
  {
    "path": "klips/klip-12-wire-initialize-external-tools.md",
    "chars": 7719,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-14\nStatus: Implemented\n---\n\n# KLIP-12: Wire 初始化协商与外部工具调用\n\n## Summary\n\n为 Wire 模式引入 "
  },
  {
    "path": "klips/klip-14-kimi-code-oauth-login.md",
    "chars": 6298,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-24\nStatus: Implemented\n---\n\n# KLIP-14: Kimi Code OAuth /login\n\n## 背景与现状\n\n* `/setup"
  },
  {
    "path": "klips/klip-15-kagent-sidecar-integration.md",
    "chars": 3600,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-26\nStatus: Draft\n---\n\n# KLIP-15: kagent Rust kernel 以 sidecar 方式接入 kimi-cli\n\n## 背景"
  },
  {
    "path": "klips/klip-2-acpkaos.md",
    "chars": 5960,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2025-12-29\nStatus: Implemented\n---\n\n# KLIP-2: ACPKaos, a LocalKaos variant that redirects "
  },
  {
    "path": "klips/klip-3-kimi-cli-user-docs.md",
    "chars": 17216,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2025-12-30\nStatus: Implemented\n---\n\n# KLIP-3: Kimi CLI User Documentation\n\n以下为后续文档大纲的层级约定:"
  },
  {
    "path": "klips/klip-6-setup-auto-refresh-models.md",
    "chars": 3223,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-07\nStatus: Implemented\n---\n\n# KLIP-6: /setup 平台模型自动刷新与托管命名空间\n\n## 背景与现状(最终实现)\n\n* `/"
  },
  {
    "path": "klips/klip-7-kimi-sdk.md",
    "chars": 5031,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-08\nStatus: Implemented\n---\n\n# KLIP-7: Kimi SDK (thin wrapper around Kosong)\n\n## Su"
  },
  {
    "path": "klips/klip-8-config-and-skills-layout.md",
    "chars": 1494,
    "preview": "---\nAuthor: \"@xxchan\"\nUpdated: 2026-01-14\nStatus: Implemented\n---\n\n# KLIP-8: Unified Skills Discovery\n\n## Motivation\n\n> "
  },
  {
    "path": "klips/klip-9-shell-ui-flicker-mitigation.md",
    "chars": 8016,
    "preview": "---\nAuthor: \"@stdrc\"\nUpdated: 2026-01-19\nStatus: Implemented\n---\n\n# KLIP-9: Shell UI 闪烁缓解 — Pager 展开方案\n\n## 问题背景\n\n### 终端渲"
  },
  {
    "path": "packages/kaos/.pre-commit-config.yaml",
    "chars": 369,
    "preview": "orphan: true\n\nrepos:\n  - repo: local\n    hooks:\n      - id: make-format-pykaos\n        name: make format-pykaos\n        "
  },
  {
    "path": "packages/kaos/CHANGELOG.md",
    "chars": 1294,
    "preview": "# Changelog\n\n## Unreleased\n\n## 0.7.0 (2026-02-06)\n\n- Add `env` parameter to `exec()` method for passing environment vari"
  },
  {
    "path": "packages/kaos/LICENSE",
    "chars": 11357,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "packages/kaos/NOTICE",
    "chars": 118,
    "preview": "PyKAOS\nCopyright 2025 Moonshot AI\n\nThis product includes software developed at\nMoonshot AI (https://www.moonshot.ai/)."
  },
  {
    "path": "packages/kaos/README.md",
    "chars": 253,
    "preview": "# PyKAOS\n\nPyKAOS is a lightweight Python library providing an abstraction layer for agents to interact with operating sy"
  },
  {
    "path": "packages/kaos/pyproject.toml",
    "chars": 966,
    "preview": "[project]\nname = \"pykaos\"\nversion = \"0.7.0\"\ndescription = \"\"\nreadme = \"README.md\"\nrequires-python = \">=3.12\"\ndependencie"
  },
  {
    "path": "packages/kaos/src/kaos/__init__.py",
    "chars": 9512,
    "preview": "from __future__ import annotations\n\nimport contextvars\nfrom collections.abc import AsyncGenerator, AsyncIterator, Iterab"
  },
  {
    "path": "packages/kaos/src/kaos/_current.py",
    "chars": 161,
    "preview": "from contextvars import ContextVar\n\nfrom kaos import Kaos\nfrom kaos.local import local_kaos\n\ncurrent_kaos = ContextVar[K"
  },
  {
    "path": "packages/kaos/src/kaos/local.py",
    "chars": 6519,
    "preview": "from __future__ import annotations\n\nimport asyncio\nimport os\nfrom asyncio.subprocess import Process as AsyncioProcess\nfr"
  },
  {
    "path": "packages/kaos/src/kaos/path.py",
    "chars": 7423,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import AsyncGenerator\nfrom pathlib import Path, PurePath\nfrom s"
  },
  {
    "path": "packages/kaos/src/kaos/py.typed",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "packages/kaos/src/kaos/ssh.py",
    "chars": 9823,
    "preview": "from __future__ import annotations\n\nimport posixpath\nimport shlex\nimport stat\nfrom collections.abc import AsyncGenerator"
  },
  {
    "path": "packages/kaos/tests/test_kaos_path.py",
    "chars": 2859,
    "preview": "from __future__ import annotations\n\nimport os\nfrom collections.abc import Generator\nfrom pathlib import Path\n\nimport pyt"
  },
  {
    "path": "packages/kaos/tests/test_local_kaos.py",
    "chars": 4978,
    "preview": "from __future__ import annotations\n\nimport asyncio\nimport os\nimport sys\nfrom collections.abc import Generator\nfrom pathl"
  },
  {
    "path": "packages/kaos/tests/test_local_kaos_cmd.py",
    "chars": 2780,
    "preview": "\"\"\"Tests for `kaos.exec` running commands via cmd.exe /c.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nimport "
  },
  {
    "path": "packages/kaos/tests/test_local_kaos_sh.py",
    "chars": 7077,
    "preview": "\"\"\"Tests for `kaos.exec` running commands via /bin/sh -c.\"\"\"\n\nfrom __future__ import annotations\n\nimport asyncio\nimport "
  },
  {
    "path": "packages/kaos/tests/test_ssh_kaos.py",
    "chars": 7780,
    "preview": "from __future__ import annotations\n\nimport asyncio\nimport os\nimport platform\nimport stat\nfrom collections.abc import Asy"
  },
  {
    "path": "packages/kimi-code/pyproject.toml",
    "chars": 393,
    "preview": "[project]\nname = \"kimi-code\"\nversion = \"1.24.0\"\ndescription = \"Kimi Code is a CLI agent that lives in your terminal.\"\nre"
  },
  {
    "path": "packages/kimi-code/src/kimi_code/__init__.py",
    "chars": 186,
    "preview": "from __future__ import annotations\n\nimport importlib\nimport sys\n\n# Alias the kimi_code package to kimi_cli for compatibi"
  },
  {
    "path": "packages/kosong/.pre-commit-config.yaml",
    "chars": 369,
    "preview": "orphan: true\n\nrepos:\n  - repo: local\n    hooks:\n      - id: make-format-kosong\n        name: make format-kosong\n        "
  },
  {
    "path": "packages/kosong/CHANGELOG.md",
    "chars": 5185,
    "preview": "# Changelog\n\n## Unreleased\n\n## 0.45.0 (2026-03-11)\n\n- OpenAI Responses: Fix implicit `reasoning.effort=null` being sent "
  },
  {
    "path": "packages/kosong/LICENSE",
    "chars": 11357,
    "preview": "\n                                 Apache License\n                           Version 2.0, January 2004\n                  "
  },
  {
    "path": "packages/kosong/NOTICE",
    "chars": 118,
    "preview": "Kosong\nCopyright 2025 Moonshot AI\n\nThis product includes software developed at\nMoonshot AI (https://www.moonshot.ai/)."
  },
  {
    "path": "packages/kosong/README.md",
    "chars": 3965,
    "preview": "# Kosong\n\nKosong is an LLM abstraction layer designed for modern AI agent applications. It unifies message structures, a"
  },
  {
    "path": "packages/kosong/pyproject.toml",
    "chars": 1373,
    "preview": "[project]\nname = \"kosong\"\nversion = \"0.45.0\"\ndescription = \"The LLM abstraction layer for modern AI agent applications.\""
  },
  {
    "path": "packages/kosong/src/kosong/__init__.py",
    "chars": 6846,
    "preview": "\"\"\"\nKosong is an LLM abstraction layer designed for modern AI agent applications.\nIt unifies message structures, asynchr"
  },
  {
    "path": "packages/kosong/src/kosong/__main__.py",
    "chars": 5436,
    "preview": "import asyncio\nimport os\nimport textwrap\nfrom argparse import ArgumentParser\nfrom typing import Literal\n\nfrom dotenv imp"
  },
  {
    "path": "packages/kosong/src/kosong/_generate.py",
    "chars": 3842,
    "preview": "from collections.abc import Sequence\nfrom dataclasses import dataclass\n\nfrom loguru import logger\n\nfrom kosong.chat_prov"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/__init__.py",
    "chars": 4097,
    "preview": "from collections.abc import AsyncIterator, Sequence\nfrom typing import Literal, Protocol, Self, runtime_checkable\n\nfrom "
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/chaos.py",
    "chars": 10165,
    "preview": "import json\nimport os\nimport random\nfrom collections.abc import AsyncIterator, Sequence\nfrom typing import TYPE_CHECKING"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/echo/__init__.py",
    "chars": 270,
    "preview": "from .echo import EchoChatProvider, EchoStreamedMessage\nfrom .scripted_echo import ScriptedEchoChatProvider, ScriptedEch"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/echo/dsl.py",
    "chars": 6797,
    "preview": "from __future__ import annotations\n\nimport json\nfrom typing import Any, cast\n\nfrom kosong.chat_provider import ChatProvi"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/echo/echo.py",
    "chars": 4004,
    "preview": "from __future__ import annotations\n\nimport copy\nfrom collections.abc import AsyncIterator, Sequence\nfrom typing import T"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/echo/scripted_echo.py",
    "chars": 2924,
    "preview": "from __future__ import annotations\n\nimport copy\nimport json\nfrom collections import deque\nfrom collections.abc import As"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/kimi.py",
    "chars": 15952,
    "preview": "import copy\nimport mimetypes\nimport os\nimport uuid\nfrom collections.abc import AsyncIterator, Sequence\nfrom typing impor"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/mock.py",
    "chars": 1983,
    "preview": "import copy\nfrom collections.abc import AsyncIterator, Sequence\nfrom typing import TYPE_CHECKING, Self\n\nfrom kosong.chat"
  },
  {
    "path": "packages/kosong/src/kosong/chat_provider/openai_common.py",
    "chars": 3752,
    "preview": "import asyncio\nimport inspect\nfrom collections.abc import Awaitable, Mapping\nfrom typing import Any, cast\n\nimport httpx\n"
  },
  {
    "path": "packages/kosong/src/kosong/contrib/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "packages/kosong/src/kosong/contrib/chat_provider/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "packages/kosong/src/kosong/contrib/chat_provider/anthropic.py",
    "chars": 21435,
    "preview": "try:\n    import anthropic as _  # noqa: F401\nexcept ModuleNotFoundError as exc:\n    raise ModuleNotFoundError(\n        \""
  },
  {
    "path": "packages/kosong/src/kosong/contrib/chat_provider/common.py",
    "chars": 117,
    "preview": "from __future__ import annotations\n\nfrom typing import Literal\n\ntype ToolMessageConversion = Literal[\"extract_text\"]\n"
  },
  {
    "path": "packages/kosong/src/kosong/contrib/chat_provider/google_genai.py",
    "chars": 28038,
    "preview": "try:\n    from google import genai as _  # noqa: F401\nexcept ModuleNotFoundError as exc:\n    raise ModuleNotFoundError(\n "
  },
  {
    "path": "packages/kosong/src/kosong/contrib/chat_provider/openai_legacy.py",
    "chars": 13323,
    "preview": "import copy\nimport uuid\nfrom collections.abc import AsyncIterator, Sequence\nfrom typing import TYPE_CHECKING, Any, Self,"
  },
  {
    "path": "packages/kosong/src/kosong/contrib/chat_provider/openai_responses.py",
    "chars": 22424,
    "preview": "import copy\nimport uuid\nfrom collections.abc import AsyncIterator, Sequence\nfrom typing import TYPE_CHECKING, Any, Self,"
  },
  {
    "path": "packages/kosong/src/kosong/contrib/context/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "packages/kosong/src/kosong/contrib/context/linear.py",
    "chars": 4066,
    "preview": "import asyncio\nimport json\nfrom pathlib import Path\nfrom typing import IO, Protocol, runtime_checkable\n\nfrom kosong.mess"
  },
  {
    "path": "packages/kosong/src/kosong/message.py",
    "chars": 9810,
    "preview": "from abc import ABC\nfrom typing import Any, ClassVar, Literal, cast, override\n\nfrom pydantic import BaseModel, GetCoreSc"
  },
  {
    "path": "packages/kosong/src/kosong/py.typed",
    "chars": 0,
    "preview": ""
  }
]

// ... and 618 more files (download for full content)

About this extraction

This page contains the full source code of the MoonshotAI/kimi-cli GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 818 files (4.7 MB), approximately 1.3M tokens, and a symbol index with 5340 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!