kimi-cli 0.40__tar.gz → 0.42__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kimi-cli might be problematic. Click here for more details.

Files changed (93) hide show
  1. kimi_cli-0.40/README.md → kimi_cli-0.42/PKG-INFO +57 -0
  2. kimi_cli-0.40/PKG-INFO → kimi_cli-0.42/README.md +33 -24
  3. {kimi_cli-0.40 → kimi_cli-0.42}/pyproject.toml +27 -21
  4. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/CHANGELOG.md +27 -0
  5. kimi_cli-0.42/src/kimi_cli/__init__.py +155 -0
  6. {kimi_cli-0.40/src/kimi_cli/agents/koder → kimi_cli-0.42/src/kimi_cli/agents/default}/agent.yaml +1 -1
  7. {kimi_cli-0.40/src/kimi_cli/agents/koder → kimi_cli-0.42/src/kimi_cli/agents/default}/system.md +1 -1
  8. kimi_cli-0.42/src/kimi_cli/agentspec.py +115 -0
  9. kimi_cli-0.42/src/kimi_cli/cli.py +249 -0
  10. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/config.py +28 -14
  11. kimi_cli-0.42/src/kimi_cli/constant.py +4 -0
  12. kimi_cli-0.42/src/kimi_cli/exception.py +16 -0
  13. kimi_cli-0.40/src/kimi_cli/utils/provider.py → kimi_cli-0.42/src/kimi_cli/llm.py +13 -5
  14. kimi_cli-0.42/src/kimi_cli/metadata.py +54 -0
  15. kimi_cli-0.42/src/kimi_cli/prompts/__init__.py +4 -0
  16. kimi_cli-0.42/src/kimi_cli/session.py +81 -0
  17. kimi_cli-0.42/src/kimi_cli/soul/__init__.py +155 -0
  18. kimi_cli-0.42/src/kimi_cli/soul/agent.py +152 -0
  19. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/approval.py +1 -1
  20. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/compaction.py +4 -4
  21. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/kimisoul.py +39 -46
  22. kimi_cli-0.42/src/kimi_cli/soul/runtime.py +94 -0
  23. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/dmail/__init__.py +1 -1
  24. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/glob.md +1 -1
  25. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/glob.py +2 -2
  26. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/grep.py +1 -1
  27. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/patch.py +2 -2
  28. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/read.py +1 -1
  29. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/replace.py +2 -2
  30. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/write.py +2 -2
  31. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/task/__init__.py +48 -40
  32. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/task/task.md +1 -1
  33. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/todo/__init__.py +1 -1
  34. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/utils.py +1 -1
  35. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/web/search.py +5 -2
  36. kimi_cli-0.42/src/kimi_cli/ui/__init__.py +0 -0
  37. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/acp/__init__.py +8 -9
  38. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/print/__init__.py +21 -37
  39. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/__init__.py +8 -19
  40. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/liveview.py +1 -1
  41. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/metacmd.py +5 -10
  42. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/prompt.py +10 -3
  43. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/setup.py +5 -5
  44. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/update.py +2 -2
  45. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/visualize.py +10 -7
  46. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/changelog.py +3 -1
  47. kimi_cli-0.42/src/kimi_cli/wire/__init__.py +69 -0
  48. kimi_cli-0.40/src/kimi_cli/soul/wire.py → kimi_cli-0.42/src/kimi_cli/wire/message.py +4 -39
  49. kimi_cli-0.40/src/kimi_cli/__init__.py +0 -387
  50. kimi_cli-0.40/src/kimi_cli/agent.py +0 -261
  51. kimi_cli-0.40/src/kimi_cli/agents/koder/README.md +0 -3
  52. kimi_cli-0.40/src/kimi_cli/llm.py +0 -8
  53. kimi_cli-0.40/src/kimi_cli/metadata.py +0 -117
  54. kimi_cli-0.40/src/kimi_cli/prompts/__init__.py +0 -4
  55. kimi_cli-0.40/src/kimi_cli/soul/__init__.py +0 -59
  56. kimi_cli-0.40/src/kimi_cli/ui/__init__.py +0 -69
  57. {kimi_cli-0.40/src/kimi_cli/agents/koder → kimi_cli-0.42/src/kimi_cli/agents/default}/sub.yaml +0 -0
  58. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/prompts/compact.md +0 -0
  59. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/prompts/init.md +0 -0
  60. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/py.typed +0 -0
  61. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/share.py +0 -0
  62. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/context.py +0 -0
  63. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/denwarenji.py +0 -0
  64. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/message.py +0 -0
  65. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/soul/toolset.py +0 -0
  66. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/__init__.py +0 -0
  67. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/bash/__init__.py +0 -0
  68. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/bash/bash.md +0 -0
  69. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/dmail/dmail.md +0 -0
  70. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/__init__.py +0 -0
  71. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/grep.md +0 -0
  72. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/patch.md +0 -0
  73. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/read.md +0 -0
  74. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/replace.md +0 -0
  75. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/file/write.md +0 -0
  76. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/mcp.py +0 -0
  77. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/test.py +0 -0
  78. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/think/__init__.py +0 -0
  79. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/think/think.md +0 -0
  80. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/todo/set_todo_list.md +0 -0
  81. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/web/__init__.py +0 -0
  82. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/web/fetch.md +0 -0
  83. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/web/fetch.py +0 -0
  84. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/tools/web/search.md +0 -0
  85. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/console.py +0 -0
  86. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/debug.py +0 -0
  87. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/ui/shell/keyboard.py +0 -0
  88. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/aiohttp.py +0 -0
  89. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/logging.py +0 -0
  90. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/message.py +0 -0
  91. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/path.py +0 -0
  92. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/pyinstaller.py +0 -0
  93. {kimi_cli-0.40 → kimi_cli-0.42}/src/kimi_cli/utils/string.py +0 -0
@@ -1,5 +1,34 @@
1
+ Metadata-Version: 2.3
2
+ Name: kimi-cli
3
+ Version: 0.42
4
+ Summary: Kimi CLI is your next CLI agent.
5
+ Requires-Dist: agent-client-protocol==0.4.9
6
+ Requires-Dist: aiofiles==25.1.0
7
+ Requires-Dist: aiohttp==3.13.1
8
+ Requires-Dist: click==8.3.0
9
+ Requires-Dist: kosong==0.15.0
10
+ Requires-Dist: loguru==0.7.3
11
+ Requires-Dist: patch-ng==1.19.0
12
+ Requires-Dist: prompt-toolkit==3.0.52
13
+ Requires-Dist: pyyaml==6.0.3
14
+ Requires-Dist: rich==14.2.0
15
+ Requires-Dist: ripgrepy==2.2.0
16
+ Requires-Dist: streamingjson==0.0.5
17
+ Requires-Dist: trafilatura==2.0.0
18
+ Requires-Dist: tenacity==9.1.2
19
+ Requires-Dist: fastmcp==2.12.5
20
+ Requires-Dist: pydantic==2.12.3
21
+ Requires-Dist: httpx[socks]==0.28.1
22
+ Requires-Python: >=3.13
23
+ Description-Content-Type: text/markdown
24
+
1
25
  # Kimi CLI
2
26
 
27
+ [![Commit Activity](https://img.shields.io/github/commit-activity/w/MoonshotAI/kimi-cli)](https://github.com/MoonshotAI/kimi-cli/graphs/commit-activity)
28
+ [![Checks](https://img.shields.io/github/check-runs/MoonshotAI/kimi-cli/main)](https://github.com/MoonshotAI/kimi-cli/actions)
29
+ [![Version](https://img.shields.io/pypi/v/kimi-cli)](https://pypi.org/project/kimi-cli/)
30
+ [![Downloads](https://img.shields.io/pypi/dw/kimi-cli)](https://pypistats.org/packages/kimi-cli)
31
+
3
32
  [中文](https://www.kimi.com/coding/docs/kimi-cli.html)
4
33
 
5
34
  Kimi CLI is a new CLI agent that can help you with your software development tasks and terminal operations.
@@ -128,3 +157,31 @@ Run `kimi` with `--mcp-config-file` option to connect to the specified MCP serve
128
157
  ```sh
129
158
  kimi --mcp-config-file /path/to/mcp.json
130
159
  ```
160
+
161
+ ## Development
162
+
163
+ To develop Kimi CLI, run:
164
+
165
+ ```sh
166
+ git clone https://github.com/MoonshotAI/kimi-cli.git
167
+ cd kimi-cli
168
+
169
+ make prepare # prepare the development environment
170
+ ```
171
+
172
+ Then you can start working on Kimi CLI.
173
+
174
+ Refer to the following commands after you make changes:
175
+
176
+ ```sh
177
+ uv run kimi # run Kimi CLI
178
+
179
+ make format # format code
180
+ make check # run linting and type checking
181
+ make test # run tests
182
+ make help # show all make targets
183
+ ```
184
+
185
+ ## Contributing
186
+
187
+ We welcome contributions to Kimi CLI! Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
@@ -1,29 +1,10 @@
1
- Metadata-Version: 2.3
2
- Name: kimi-cli
3
- Version: 0.40
4
- Summary: Kimi CLI is your next CLI agent.
5
- Requires-Dist: agent-client-protocol>=0.4.9
6
- Requires-Dist: aiofiles>=25.1.0
7
- Requires-Dist: aiohttp>=3.13.1
8
- Requires-Dist: click>=8.3.0
9
- Requires-Dist: kosong>=0.15.0
10
- Requires-Dist: loguru>=0.7.3
11
- Requires-Dist: patch-ng>=1.19.0
12
- Requires-Dist: prompt-toolkit>=3.0.52
13
- Requires-Dist: pyyaml>=6.0.3
14
- Requires-Dist: rich>=14.2.0
15
- Requires-Dist: ripgrepy>=2.2.0
16
- Requires-Dist: streamingjson>=0.0.5
17
- Requires-Dist: trafilatura>=2.0.0
18
- Requires-Dist: tenacity>=9.1.2
19
- Requires-Dist: fastmcp>=2.12.5
20
- Requires-Dist: pydantic>=2.12.3
21
- Requires-Dist: httpx[socks]>=0.28.0
22
- Requires-Python: >=3.13
23
- Description-Content-Type: text/markdown
24
-
25
1
  # Kimi CLI
26
2
 
3
+ [![Commit Activity](https://img.shields.io/github/commit-activity/w/MoonshotAI/kimi-cli)](https://github.com/MoonshotAI/kimi-cli/graphs/commit-activity)
4
+ [![Checks](https://img.shields.io/github/check-runs/MoonshotAI/kimi-cli/main)](https://github.com/MoonshotAI/kimi-cli/actions)
5
+ [![Version](https://img.shields.io/pypi/v/kimi-cli)](https://pypi.org/project/kimi-cli/)
6
+ [![Downloads](https://img.shields.io/pypi/dw/kimi-cli)](https://pypistats.org/packages/kimi-cli)
7
+
27
8
  [中文](https://www.kimi.com/coding/docs/kimi-cli.html)
28
9
 
29
10
  Kimi CLI is a new CLI agent that can help you with your software development tasks and terminal operations.
@@ -152,3 +133,31 @@ Run `kimi` with `--mcp-config-file` option to connect to the specified MCP serve
152
133
  ```sh
153
134
  kimi --mcp-config-file /path/to/mcp.json
154
135
  ```
136
+
137
+ ## Development
138
+
139
+ To develop Kimi CLI, run:
140
+
141
+ ```sh
142
+ git clone https://github.com/MoonshotAI/kimi-cli.git
143
+ cd kimi-cli
144
+
145
+ make prepare # prepare the development environment
146
+ ```
147
+
148
+ Then you can start working on Kimi CLI.
149
+
150
+ Refer to the following commands after you make changes:
151
+
152
+ ```sh
153
+ uv run kimi # run Kimi CLI
154
+
155
+ make format # format code
156
+ make check # run linting and type checking
157
+ make test # run tests
158
+ make help # show all make targets
159
+ ```
160
+
161
+ ## Contributing
162
+
163
+ We welcome contributions to Kimi CLI! Please refer to [CONTRIBUTING.md](./CONTRIBUTING.md) for more information.
@@ -1,34 +1,34 @@
1
1
  [project]
2
2
  name = "kimi-cli"
3
- version = "0.40"
3
+ version = "0.42"
4
4
  description = "Kimi CLI is your next CLI agent."
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.13"
7
7
  dependencies = [
8
- "agent-client-protocol>=0.4.9",
9
- "aiofiles>=25.1.0",
10
- "aiohttp>=3.13.1",
11
- "click>=8.3.0",
12
- "kosong>=0.15.0",
13
- "loguru>=0.7.3",
14
- "patch-ng>=1.19.0",
15
- "prompt-toolkit>=3.0.52",
16
- "pyyaml>=6.0.3",
17
- "rich>=14.2.0",
18
- "ripgrepy>=2.2.0",
19
- "streamingjson>=0.0.5",
20
- "trafilatura>=2.0.0",
21
- "tenacity>=9.1.2",
22
- "fastmcp>=2.12.5",
23
- "pydantic>=2.12.3",
24
- "httpx[socks]>=0.28.0",
8
+ "agent-client-protocol==0.4.9",
9
+ "aiofiles==25.1.0",
10
+ "aiohttp==3.13.1",
11
+ "click==8.3.0",
12
+ "kosong==0.15.0",
13
+ "loguru==0.7.3",
14
+ "patch-ng==1.19.0",
15
+ "prompt-toolkit==3.0.52",
16
+ "pyyaml==6.0.3",
17
+ "rich==14.2.0",
18
+ "ripgrepy==2.2.0",
19
+ "streamingjson==0.0.5",
20
+ "trafilatura==2.0.0",
21
+ "tenacity==9.1.2",
22
+ "fastmcp==2.12.5",
23
+ "pydantic==2.12.3",
24
+ "httpx[socks]==0.28.1",
25
25
  ]
26
26
 
27
27
  [dependency-groups]
28
28
  dev = [
29
- "inline-snapshot[black]>=0.30.1",
29
+ "inline-snapshot[black]>=0.31.0",
30
30
  "pyinstaller>=6.16.0",
31
- "pyright>=1.1.406",
31
+ "pyright>=1.1.407",
32
32
  "pytest>=8.4.2",
33
33
  "pytest-asyncio>=1.2.0",
34
34
  "ruff>=0.14.1",
@@ -43,7 +43,7 @@ module-name = ["kimi_cli"]
43
43
  source-exclude = ["tests/**/*", "src/kimi_cli/deps/**/*"]
44
44
 
45
45
  [project.scripts]
46
- kimi = "kimi_cli:main"
46
+ kimi = "kimi_cli.cli:main"
47
47
 
48
48
  [tool.ruff]
49
49
  line-length = 100
@@ -57,3 +57,9 @@ select = [
57
57
  "SIM", # flake8-simplify
58
58
  "I", # isort
59
59
  ]
60
+
61
+ [tool.typos.files]
62
+ extend-exclude = ["kimi.spec", "pyinstaller.py"]
63
+
64
+ [tool.typos.default.extend-words]
65
+ datas = "datas"
@@ -9,6 +9,33 @@ Internal builds may append content to the Unreleased section.
9
9
  Only write entries that are worth mentioning to users.
10
10
  -->
11
11
 
12
+ ## [0.42] - 2025-10-28
13
+
14
+ ### Added
15
+
16
+ - Support Ctrl-J or Alt-Enter to insert a new line
17
+
18
+ ### Changed
19
+
20
+ - Change mode switch shortcut from Ctrl-K to Ctrl-X
21
+ - Improve overall robustness
22
+
23
+ ### Fixed
24
+
25
+ - Fix ACP server `no attribute` error
26
+
27
+ ## [0.41] - 2025-10-26
28
+
29
+ ### Fixed
30
+
31
+ - Fix a bug for Glob tool when no matching files are found
32
+ - Ensure reading files with UTF-8 encoding
33
+
34
+ ### Changed
35
+
36
+ - Disable reading command/query from stdin in shell mode
37
+ - Clarify the API platform selection in `/setup` meta command
38
+
12
39
  ## [0.40] - 2025-10-24
13
40
 
14
41
  ### Added
@@ -0,0 +1,155 @@
1
+ import contextlib
2
+ import os
3
+ import warnings
4
+ from collections.abc import Generator
5
+ from pathlib import Path
6
+ from typing import Any
7
+
8
+ from pydantic import SecretStr
9
+
10
+ from kimi_cli.agentspec import DEFAULT_AGENT_FILE
11
+ from kimi_cli.config import LLMModel, LLMProvider, load_config
12
+ from kimi_cli.llm import augment_provider_with_env_vars, create_llm
13
+ from kimi_cli.session import Session
14
+ from kimi_cli.soul.agent import load_agent
15
+ from kimi_cli.soul.context import Context
16
+ from kimi_cli.soul.kimisoul import KimiSoul
17
+ from kimi_cli.soul.runtime import Runtime
18
+ from kimi_cli.ui.acp import ACPServer
19
+ from kimi_cli.ui.print import InputFormat, OutputFormat, PrintApp
20
+ from kimi_cli.ui.shell import ShellApp
21
+ from kimi_cli.utils.logging import StreamToLogger, logger
22
+
23
+
24
+ class KimiCLI:
25
+ @staticmethod
26
+ async def create(
27
+ session: Session,
28
+ *,
29
+ yolo: bool = False,
30
+ stream: bool = True, # TODO: remove this when we have a correct print mode impl
31
+ mcp_configs: list[dict[str, Any]] | None = None,
32
+ config_file: Path | None = None,
33
+ model_name: str | None = None,
34
+ agent_file: Path | None = None,
35
+ ) -> "KimiCLI":
36
+ """
37
+ Create a KimiCLI instance.
38
+
39
+ Args:
40
+ session (Session): A session created by `Session.create` or `Session.continue_`.
41
+ yolo (bool, optional): Approve all actions without confirmation. Defaults to False.
42
+ stream (bool, optional): Use stream mode when calling LLM API. Defaults to True.
43
+ config_file (Path | None, optional): Path to the configuration file. Defaults to None.
44
+ model_name (str | None, optional): Name of the model to use. Defaults to None.
45
+ agent_file (Path | None, optional): Path to the agent file. Defaults to None.
46
+
47
+ Raises:
48
+ FileNotFoundError: When the agent file is not found.
49
+ ConfigError(KimiCLIException): When the configuration is invalid.
50
+ AgentSpecError(KimiCLIException): When the agent specification is invalid.
51
+ """
52
+ config = load_config(config_file)
53
+ logger.info("Loaded config: {config}", config=config)
54
+
55
+ model: LLMModel | None = None
56
+ provider: LLMProvider | None = None
57
+
58
+ # try to use config file
59
+ if not model_name and config.default_model:
60
+ # no --model specified && default model is set in config
61
+ model = config.models[config.default_model]
62
+ provider = config.providers[model.provider]
63
+ if model_name and model_name in config.models:
64
+ # --model specified && model is set in config
65
+ model = config.models[model_name]
66
+ provider = config.providers[model.provider]
67
+
68
+ if not model:
69
+ model = LLMModel(provider="", model="", max_context_size=100_000)
70
+ provider = LLMProvider(type="kimi", base_url="", api_key=SecretStr(""))
71
+
72
+ # try overwrite with environment variables
73
+ assert provider is not None
74
+ assert model is not None
75
+ augment_provider_with_env_vars(provider, model)
76
+
77
+ if not provider.base_url or not model.model:
78
+ llm = None
79
+ else:
80
+ logger.info("Using LLM provider: {provider}", provider=provider)
81
+ logger.info("Using LLM model: {model}", model=model)
82
+ llm = create_llm(provider, model, stream=stream, session_id=session.id)
83
+
84
+ runtime = await Runtime.create(config, llm, session, yolo)
85
+
86
+ if agent_file is None:
87
+ agent_file = DEFAULT_AGENT_FILE
88
+ agent = await load_agent(agent_file, runtime, mcp_configs=mcp_configs or [])
89
+
90
+ context = Context(session.history_file)
91
+ await context.restore()
92
+
93
+ soul = KimiSoul(
94
+ agent,
95
+ runtime,
96
+ context=context,
97
+ )
98
+ return KimiCLI(soul, session)
99
+
100
+ def __init__(self, soul: KimiSoul, session: Session) -> None:
101
+ self._soul = soul
102
+ self._session = session
103
+
104
+ @property
105
+ def soul(self) -> KimiSoul:
106
+ """Get the KimiSoul instance."""
107
+ return self._soul
108
+
109
+ @property
110
+ def session(self) -> Session:
111
+ """Get the Session instance."""
112
+ return self._session
113
+
114
+ @contextlib.contextmanager
115
+ def _app_env(self) -> Generator[None]:
116
+ original_cwd = Path.cwd()
117
+ os.chdir(self._session.work_dir)
118
+ try:
119
+ # to ignore possible warnings from dateparser
120
+ warnings.filterwarnings("ignore", category=DeprecationWarning)
121
+ with contextlib.redirect_stderr(StreamToLogger()):
122
+ yield
123
+ finally:
124
+ os.chdir(original_cwd)
125
+
126
+ async def run_shell_mode(self, command: str | None = None) -> bool:
127
+ with self._app_env():
128
+ app = ShellApp(
129
+ self._soul,
130
+ welcome_info={
131
+ "Directory": str(self._session.work_dir),
132
+ "Session": self._session.id,
133
+ },
134
+ )
135
+ return await app.run(command)
136
+
137
+ async def run_print_mode(
138
+ self,
139
+ input_format: InputFormat,
140
+ output_format: OutputFormat,
141
+ command: str | None = None,
142
+ ) -> bool:
143
+ with self._app_env():
144
+ app = PrintApp(
145
+ self._soul,
146
+ input_format,
147
+ output_format,
148
+ self._session.history_file,
149
+ )
150
+ return await app.run(command)
151
+
152
+ async def run_acp_server(self) -> bool:
153
+ with self._app_env():
154
+ app = ACPServer(self._soul)
155
+ return await app.run()
@@ -19,6 +19,6 @@ agent:
19
19
  - "kimi_cli.tools.web:SearchWeb"
20
20
  - "kimi_cli.tools.web:FetchURL"
21
21
  subagents:
22
- koder:
22
+ coder:
23
23
  path: ./sub.yaml
24
24
  description: "Good at general software engineering tasks."
@@ -47,7 +47,7 @@ The operating environment is not in a sandbox. Any action especially mutation yo
47
47
 
48
48
  The current working directory is `${KIMI_WORK_DIR}`. This should be considered as the project root if you are instructed to perform tasks on the project. Every file system operation will be relative to the working directory if you do not explicitly specify the absolute path. Tools may require absolute paths for some parameters, if so, you should strictly follow the requirements.
49
49
 
50
- The `ls -la` output of current working directory is:
50
+ The directory listing of current working directory is:
51
51
 
52
52
  ```
53
53
  ${KIMI_WORK_DIR_LS}
@@ -0,0 +1,115 @@
1
+ from pathlib import Path
2
+ from typing import Any, NamedTuple
3
+
4
+ import yaml
5
+ from pydantic import BaseModel, Field
6
+
7
+ from kimi_cli.exception import AgentSpecError
8
+
9
+
10
+ def get_agents_dir() -> Path:
11
+ return Path(__file__).parent / "agents"
12
+
13
+
14
+ DEFAULT_AGENT_FILE = get_agents_dir() / "default" / "agent.yaml"
15
+
16
+
17
+ class AgentSpec(BaseModel):
18
+ """Agent specification."""
19
+
20
+ extend: str | None = Field(default=None, description="Agent file to extend")
21
+ name: str | None = Field(default=None, description="Agent name") # required
22
+ system_prompt_path: Path | None = Field(
23
+ default=None, description="System prompt path"
24
+ ) # required
25
+ system_prompt_args: dict[str, str] = Field(
26
+ default_factory=dict, description="System prompt arguments"
27
+ )
28
+ tools: list[str] | None = Field(default=None, description="Tools") # required
29
+ exclude_tools: list[str] | None = Field(default=None, description="Tools to exclude")
30
+ subagents: dict[str, "SubagentSpec"] | None = Field(default=None, description="Subagents")
31
+
32
+
33
+ class SubagentSpec(BaseModel):
34
+ """Subagent specification."""
35
+
36
+ path: Path = Field(description="Subagent file path")
37
+ description: str = Field(description="Subagent description")
38
+
39
+
40
+ class ResolvedAgentSpec(NamedTuple):
41
+ """Resolved agent specification."""
42
+
43
+ name: str
44
+ system_prompt_path: Path
45
+ system_prompt_args: dict[str, str]
46
+ tools: list[str]
47
+ exclude_tools: list[str]
48
+ subagents: dict[str, "SubagentSpec"]
49
+
50
+
51
+ def load_agent_spec(agent_file: Path) -> ResolvedAgentSpec:
52
+ """
53
+ Load agent specification from file.
54
+
55
+ Raises:
56
+ FileNotFoundError: If the agent spec file is not found.
57
+ AgentSpecError: If the agent spec is not valid.
58
+ """
59
+ agent_spec = _load_agent_spec(agent_file)
60
+ assert agent_spec.extend is None, "agent extension should be recursively resolved"
61
+ if agent_spec.name is None:
62
+ raise AgentSpecError("Agent name is required")
63
+ if agent_spec.system_prompt_path is None:
64
+ raise AgentSpecError("System prompt path is required")
65
+ if agent_spec.tools is None:
66
+ raise AgentSpecError("Tools are required")
67
+ return ResolvedAgentSpec(
68
+ name=agent_spec.name,
69
+ system_prompt_path=agent_spec.system_prompt_path,
70
+ system_prompt_args=agent_spec.system_prompt_args,
71
+ tools=agent_spec.tools,
72
+ exclude_tools=agent_spec.exclude_tools or [],
73
+ subagents=agent_spec.subagents or {},
74
+ )
75
+
76
+
77
+ def _load_agent_spec(agent_file: Path) -> AgentSpec:
78
+ assert agent_file.is_file(), "expect agent file to exist"
79
+ try:
80
+ with open(agent_file, encoding="utf-8") as f:
81
+ data: dict[str, Any] = yaml.safe_load(f)
82
+ except yaml.YAMLError as e:
83
+ raise AgentSpecError(f"Invalid YAML in agent spec file: {e}") from e
84
+
85
+ version = data.get("version", 1)
86
+ if version != 1:
87
+ raise AgentSpecError(f"Unsupported agent spec version: {version}")
88
+
89
+ agent_spec = AgentSpec(**data.get("agent", {}))
90
+ if agent_spec.system_prompt_path is not None:
91
+ agent_spec.system_prompt_path = agent_file.parent / agent_spec.system_prompt_path
92
+ if agent_spec.subagents is not None:
93
+ for v in agent_spec.subagents.values():
94
+ v.path = agent_file.parent / v.path
95
+ if agent_spec.extend:
96
+ if agent_spec.extend == "default":
97
+ base_agent_file = DEFAULT_AGENT_FILE
98
+ else:
99
+ base_agent_file = agent_file.parent / agent_spec.extend
100
+ base_agent_spec = _load_agent_spec(base_agent_file)
101
+ if agent_spec.name is not None:
102
+ base_agent_spec.name = agent_spec.name
103
+ if agent_spec.system_prompt_path is not None:
104
+ base_agent_spec.system_prompt_path = agent_spec.system_prompt_path
105
+ for k, v in agent_spec.system_prompt_args.items():
106
+ # system prompt args should be merged instead of overwritten
107
+ base_agent_spec.system_prompt_args[k] = v
108
+ if agent_spec.tools is not None:
109
+ base_agent_spec.tools = agent_spec.tools
110
+ if agent_spec.exclude_tools is not None:
111
+ base_agent_spec.exclude_tools = agent_spec.exclude_tools
112
+ if agent_spec.subagents is not None:
113
+ base_agent_spec.subagents = agent_spec.subagents
114
+ agent_spec = base_agent_spec
115
+ return agent_spec