kimi-cli 0.40__tar.gz → 0.41__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.
- {kimi_cli-0.40 → kimi_cli-0.41}/PKG-INFO +34 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/README.md +33 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/pyproject.toml +8 -2
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/CHANGELOG.md +12 -0
- kimi_cli-0.41/src/kimi_cli/__init__.py +125 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/agents/koder/system.md +1 -1
- kimi_cli-0.41/src/kimi_cli/agentspec.py +104 -0
- kimi_cli-0.40/src/kimi_cli/__init__.py → kimi_cli-0.41/src/kimi_cli/cli.py +12 -164
- kimi_cli-0.41/src/kimi_cli/constant.py +4 -0
- kimi_cli-0.40/src/kimi_cli/utils/provider.py → kimi_cli-0.41/src/kimi_cli/llm.py +12 -5
- kimi_cli-0.41/src/kimi_cli/prompts/__init__.py +4 -0
- kimi_cli-0.41/src/kimi_cli/soul/__init__.py +155 -0
- kimi_cli-0.41/src/kimi_cli/soul/agent.py +157 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/approval.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/compaction.py +4 -4
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/context.py +5 -0
- kimi_cli-0.41/src/kimi_cli/soul/globals.py +92 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/kimisoul.py +21 -26
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/dmail/__init__.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/glob.md +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/glob.py +2 -2
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/grep.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/patch.py +2 -2
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/read.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/replace.py +2 -2
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/write.py +2 -2
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/task/__init__.py +23 -22
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/task/task.md +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/todo/__init__.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/utils.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/web/search.py +2 -2
- kimi_cli-0.41/src/kimi_cli/ui/__init__.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/acp/__init__.py +8 -9
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/print/__init__.py +17 -35
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/__init__.py +5 -13
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/liveview.py +1 -1
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/metacmd.py +3 -3
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/setup.py +5 -5
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/update.py +2 -2
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/visualize.py +10 -7
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/changelog.py +3 -1
- kimi_cli-0.41/src/kimi_cli/wire/__init__.py +57 -0
- kimi_cli-0.40/src/kimi_cli/soul/wire.py → kimi_cli-0.41/src/kimi_cli/wire/message.py +4 -39
- kimi_cli-0.40/src/kimi_cli/agent.py +0 -261
- kimi_cli-0.40/src/kimi_cli/llm.py +0 -8
- kimi_cli-0.40/src/kimi_cli/prompts/__init__.py +0 -4
- kimi_cli-0.40/src/kimi_cli/soul/__init__.py +0 -59
- kimi_cli-0.40/src/kimi_cli/ui/__init__.py +0 -69
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/agents/koder/README.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/agents/koder/agent.yaml +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/agents/koder/sub.yaml +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/config.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/metadata.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/prompts/compact.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/prompts/init.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/py.typed +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/share.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/denwarenji.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/message.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/soul/toolset.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/__init__.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/bash/__init__.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/bash/bash.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/dmail/dmail.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/__init__.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/grep.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/patch.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/read.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/replace.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/file/write.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/mcp.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/test.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/think/__init__.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/think/think.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/todo/set_todo_list.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/web/__init__.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/web/fetch.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/web/fetch.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/tools/web/search.md +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/console.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/debug.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/keyboard.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/ui/shell/prompt.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/aiohttp.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/logging.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/message.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/path.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/pyinstaller.py +0 -0
- {kimi_cli-0.40 → kimi_cli-0.41}/src/kimi_cli/utils/string.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: kimi-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.41
|
|
4
4
|
Summary: Kimi CLI is your next CLI agent.
|
|
5
5
|
Requires-Dist: agent-client-protocol>=0.4.9
|
|
6
6
|
Requires-Dist: aiofiles>=25.1.0
|
|
@@ -24,6 +24,11 @@ Description-Content-Type: text/markdown
|
|
|
24
24
|
|
|
25
25
|
# Kimi CLI
|
|
26
26
|
|
|
27
|
+
[](https://github.com/MoonshotAI/kimi-cli/graphs/commit-activity)
|
|
28
|
+
[](https://github.com/MoonshotAI/kimi-cli/actions)
|
|
29
|
+
[](https://pypi.org/project/kimi-cli/)
|
|
30
|
+
[](https://pypistats.org/packages/kimi-cli)
|
|
31
|
+
|
|
27
32
|
[中文](https://www.kimi.com/coding/docs/kimi-cli.html)
|
|
28
33
|
|
|
29
34
|
Kimi CLI is a new CLI agent that can help you with your software development tasks and terminal operations.
|
|
@@ -152,3 +157,31 @@ Run `kimi` with `--mcp-config-file` option to connect to the specified MCP serve
|
|
|
152
157
|
```sh
|
|
153
158
|
kimi --mcp-config-file /path/to/mcp.json
|
|
154
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,5 +1,10 @@
|
|
|
1
1
|
# Kimi CLI
|
|
2
2
|
|
|
3
|
+
[](https://github.com/MoonshotAI/kimi-cli/graphs/commit-activity)
|
|
4
|
+
[](https://github.com/MoonshotAI/kimi-cli/actions)
|
|
5
|
+
[](https://pypi.org/project/kimi-cli/)
|
|
6
|
+
[](https://pypistats.org/packages/kimi-cli)
|
|
7
|
+
|
|
3
8
|
[中文](https://www.kimi.com/coding/docs/kimi-cli.html)
|
|
4
9
|
|
|
5
10
|
Kimi CLI is a new CLI agent that can help you with your software development tasks and terminal operations.
|
|
@@ -128,3 +133,31 @@ Run `kimi` with `--mcp-config-file` option to connect to the specified MCP serve
|
|
|
128
133
|
```sh
|
|
129
134
|
kimi --mcp-config-file /path/to/mcp.json
|
|
130
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,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "kimi-cli"
|
|
3
|
-
version = "0.
|
|
3
|
+
version = "0.41"
|
|
4
4
|
description = "Kimi CLI is your next CLI agent."
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.13"
|
|
@@ -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,18 @@ 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.41] - 2025-10-26
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
|
|
16
|
+
- Fix a bug for Glob tool when no matching files are found
|
|
17
|
+
- Ensure reading files with UTF-8 encoding
|
|
18
|
+
|
|
19
|
+
### Changed
|
|
20
|
+
|
|
21
|
+
- Disable reading command/query from stdin in shell mode
|
|
22
|
+
- Clarify the API platform selection in `/setup` meta command
|
|
23
|
+
|
|
12
24
|
## [0.40] - 2025-10-24
|
|
13
25
|
|
|
14
26
|
### Added
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import os
|
|
3
|
+
import warnings
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from typing import Any, Literal
|
|
6
|
+
|
|
7
|
+
import click
|
|
8
|
+
from pydantic import SecretStr
|
|
9
|
+
|
|
10
|
+
from kimi_cli.agentspec import DEFAULT_AGENT_FILE
|
|
11
|
+
from kimi_cli.config import Config, LLMModel, LLMProvider
|
|
12
|
+
from kimi_cli.llm import augment_provider_with_env_vars, create_llm
|
|
13
|
+
from kimi_cli.metadata import Session
|
|
14
|
+
from kimi_cli.soul.agent import load_agent_with_mcp
|
|
15
|
+
from kimi_cli.soul.context import Context
|
|
16
|
+
from kimi_cli.soul.globals import AgentGlobals
|
|
17
|
+
from kimi_cli.soul.kimisoul import KimiSoul
|
|
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
|
+
UIMode = Literal["shell", "print", "acp"]
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
async def kimi_run(
|
|
27
|
+
*,
|
|
28
|
+
config: Config,
|
|
29
|
+
model_name: str | None,
|
|
30
|
+
work_dir: Path,
|
|
31
|
+
session: Session,
|
|
32
|
+
command: str | None = None,
|
|
33
|
+
agent_file: Path = DEFAULT_AGENT_FILE,
|
|
34
|
+
ui: UIMode = "shell",
|
|
35
|
+
input_format: InputFormat | None = None,
|
|
36
|
+
output_format: OutputFormat | None = None,
|
|
37
|
+
mcp_configs: list[dict[str, Any]] | None = None,
|
|
38
|
+
yolo: bool = False,
|
|
39
|
+
) -> bool:
|
|
40
|
+
"""Run Kimi CLI."""
|
|
41
|
+
model: LLMModel | None = None
|
|
42
|
+
provider: LLMProvider | None = None
|
|
43
|
+
|
|
44
|
+
# try to use config file
|
|
45
|
+
if not model_name and config.default_model:
|
|
46
|
+
# no --model specified && default model is set in config
|
|
47
|
+
model = config.models[config.default_model]
|
|
48
|
+
provider = config.providers[model.provider]
|
|
49
|
+
if model_name and model_name in config.models:
|
|
50
|
+
# --model specified && model is set in config
|
|
51
|
+
model = config.models[model_name]
|
|
52
|
+
provider = config.providers[model.provider]
|
|
53
|
+
|
|
54
|
+
if not model:
|
|
55
|
+
model = LLMModel(provider="", model="", max_context_size=100_000)
|
|
56
|
+
provider = LLMProvider(type="kimi", base_url="", api_key=SecretStr(""))
|
|
57
|
+
|
|
58
|
+
# try overwrite with environment variables
|
|
59
|
+
assert provider is not None
|
|
60
|
+
assert model is not None
|
|
61
|
+
augment_provider_with_env_vars(provider, model)
|
|
62
|
+
|
|
63
|
+
if not provider.base_url or not model.model:
|
|
64
|
+
llm = None
|
|
65
|
+
else:
|
|
66
|
+
logger.info("Using LLM provider: {provider}", provider=provider)
|
|
67
|
+
logger.info("Using LLM model: {model}", model=model)
|
|
68
|
+
stream = ui != "print" # use non-streaming mode only for print UI
|
|
69
|
+
llm = create_llm(provider, model, stream=stream, session_id=session.id)
|
|
70
|
+
|
|
71
|
+
agent_globals = await AgentGlobals.create(config, llm, session, yolo)
|
|
72
|
+
try:
|
|
73
|
+
agent = await load_agent_with_mcp(agent_file, agent_globals, mcp_configs or [])
|
|
74
|
+
except ValueError as e:
|
|
75
|
+
raise click.BadParameter(f"Failed to load agent: {e}") from e
|
|
76
|
+
|
|
77
|
+
if command is not None:
|
|
78
|
+
command = command.strip()
|
|
79
|
+
if not command:
|
|
80
|
+
raise click.BadParameter("Command cannot be empty")
|
|
81
|
+
|
|
82
|
+
context = Context(session.history_file)
|
|
83
|
+
await context.restore()
|
|
84
|
+
|
|
85
|
+
soul = KimiSoul(
|
|
86
|
+
agent,
|
|
87
|
+
agent_globals,
|
|
88
|
+
context=context,
|
|
89
|
+
loop_control=config.loop_control,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
original_cwd = Path.cwd()
|
|
93
|
+
os.chdir(work_dir)
|
|
94
|
+
|
|
95
|
+
try:
|
|
96
|
+
if ui == "shell":
|
|
97
|
+
app = ShellApp(
|
|
98
|
+
soul,
|
|
99
|
+
welcome_info={
|
|
100
|
+
"Directory": str(work_dir),
|
|
101
|
+
"Session": session.id,
|
|
102
|
+
},
|
|
103
|
+
)
|
|
104
|
+
# to ignore possible warnings from dateparser
|
|
105
|
+
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
106
|
+
with contextlib.redirect_stderr(StreamToLogger()):
|
|
107
|
+
return await app.run(command)
|
|
108
|
+
elif ui == "print":
|
|
109
|
+
soul._approval.set_yolo(True) # print mode implies yolo mode
|
|
110
|
+
app = PrintApp(
|
|
111
|
+
soul,
|
|
112
|
+
input_format or "text",
|
|
113
|
+
output_format or "text",
|
|
114
|
+
context.file_backend,
|
|
115
|
+
)
|
|
116
|
+
return await app.run(command)
|
|
117
|
+
elif ui == "acp":
|
|
118
|
+
if command is not None:
|
|
119
|
+
logger.warning("ACP server ignores command argument")
|
|
120
|
+
app = ACPServer(soul)
|
|
121
|
+
return await app.run()
|
|
122
|
+
else:
|
|
123
|
+
raise click.BadParameter(f"Invalid UI mode: {ui}")
|
|
124
|
+
finally:
|
|
125
|
+
os.chdir(original_cwd)
|
|
@@ -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
|
|
50
|
+
The directory listing of current working directory is:
|
|
51
51
|
|
|
52
52
|
```
|
|
53
53
|
${KIMI_WORK_DIR_LS}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Any, NamedTuple
|
|
3
|
+
|
|
4
|
+
import yaml
|
|
5
|
+
from pydantic import BaseModel, Field
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_agents_dir() -> Path:
|
|
9
|
+
return Path(__file__).parent / "agents"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
DEFAULT_AGENT_FILE = get_agents_dir() / "koder" / "agent.yaml"
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class AgentSpec(BaseModel):
|
|
16
|
+
"""Agent specification."""
|
|
17
|
+
|
|
18
|
+
extend: str | None = Field(default=None, description="Agent file to extend")
|
|
19
|
+
name: str | None = Field(default=None, description="Agent name") # required
|
|
20
|
+
system_prompt_path: Path | None = Field(
|
|
21
|
+
default=None, description="System prompt path"
|
|
22
|
+
) # required
|
|
23
|
+
system_prompt_args: dict[str, str] = Field(
|
|
24
|
+
default_factory=dict, description="System prompt arguments"
|
|
25
|
+
)
|
|
26
|
+
tools: list[str] | None = Field(default=None, description="Tools") # required
|
|
27
|
+
exclude_tools: list[str] | None = Field(default=None, description="Tools to exclude")
|
|
28
|
+
subagents: dict[str, "SubagentSpec"] | None = Field(default=None, description="Subagents")
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class SubagentSpec(BaseModel):
|
|
32
|
+
"""Subagent specification."""
|
|
33
|
+
|
|
34
|
+
path: Path = Field(description="Subagent file path")
|
|
35
|
+
description: str = Field(description="Subagent description")
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class ResolvedAgentSpec(NamedTuple):
|
|
39
|
+
"""Resolved agent specification."""
|
|
40
|
+
|
|
41
|
+
name: str
|
|
42
|
+
system_prompt_path: Path
|
|
43
|
+
system_prompt_args: dict[str, str]
|
|
44
|
+
tools: list[str]
|
|
45
|
+
exclude_tools: list[str]
|
|
46
|
+
subagents: dict[str, "SubagentSpec"]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def load_agent_spec(agent_file: Path) -> ResolvedAgentSpec:
|
|
50
|
+
"""Load agent specification from file."""
|
|
51
|
+
agent_spec = _load_agent_spec(agent_file)
|
|
52
|
+
assert agent_spec.extend is None, "agent extension should be recursively resolved"
|
|
53
|
+
if agent_spec.name is None:
|
|
54
|
+
raise ValueError("Agent name is required")
|
|
55
|
+
if agent_spec.system_prompt_path is None:
|
|
56
|
+
raise ValueError("System prompt path is required")
|
|
57
|
+
if agent_spec.tools is None:
|
|
58
|
+
raise ValueError("Tools are required")
|
|
59
|
+
return ResolvedAgentSpec(
|
|
60
|
+
name=agent_spec.name,
|
|
61
|
+
system_prompt_path=agent_spec.system_prompt_path,
|
|
62
|
+
system_prompt_args=agent_spec.system_prompt_args,
|
|
63
|
+
tools=agent_spec.tools,
|
|
64
|
+
exclude_tools=agent_spec.exclude_tools or [],
|
|
65
|
+
subagents=agent_spec.subagents or {},
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _load_agent_spec(agent_file: Path) -> AgentSpec:
|
|
70
|
+
assert agent_file.is_file(), "expect agent file to exist"
|
|
71
|
+
with open(agent_file, encoding="utf-8") as f:
|
|
72
|
+
data: dict[str, Any] = yaml.safe_load(f)
|
|
73
|
+
|
|
74
|
+
version = data.get("version", 1)
|
|
75
|
+
if version != 1:
|
|
76
|
+
raise ValueError(f"Unsupported agent spec version: {version}")
|
|
77
|
+
|
|
78
|
+
agent_spec = AgentSpec(**data.get("agent", {}))
|
|
79
|
+
if agent_spec.system_prompt_path is not None:
|
|
80
|
+
agent_spec.system_prompt_path = agent_file.parent / agent_spec.system_prompt_path
|
|
81
|
+
if agent_spec.subagents is not None:
|
|
82
|
+
for v in agent_spec.subagents.values():
|
|
83
|
+
v.path = agent_file.parent / v.path
|
|
84
|
+
if agent_spec.extend:
|
|
85
|
+
if agent_spec.extend == "default":
|
|
86
|
+
base_agent_file = DEFAULT_AGENT_FILE
|
|
87
|
+
else:
|
|
88
|
+
base_agent_file = agent_file.parent / agent_spec.extend
|
|
89
|
+
base_agent_spec = _load_agent_spec(base_agent_file)
|
|
90
|
+
if agent_spec.name is not None:
|
|
91
|
+
base_agent_spec.name = agent_spec.name
|
|
92
|
+
if agent_spec.system_prompt_path is not None:
|
|
93
|
+
base_agent_spec.system_prompt_path = agent_spec.system_prompt_path
|
|
94
|
+
for k, v in agent_spec.system_prompt_args.items():
|
|
95
|
+
# system prompt args should be merged instead of overwritten
|
|
96
|
+
base_agent_spec.system_prompt_args[k] = v
|
|
97
|
+
if agent_spec.tools is not None:
|
|
98
|
+
base_agent_spec.tools = agent_spec.tools
|
|
99
|
+
if agent_spec.exclude_tools is not None:
|
|
100
|
+
base_agent_spec.exclude_tools = agent_spec.exclude_tools
|
|
101
|
+
if agent_spec.subagents is not None:
|
|
102
|
+
base_agent_spec.subagents = agent_spec.subagents
|
|
103
|
+
agent_spec = base_agent_spec
|
|
104
|
+
return agent_spec
|
|
@@ -1,53 +1,28 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import contextlib
|
|
3
|
-
import importlib.metadata
|
|
4
2
|
import json
|
|
5
|
-
import os
|
|
6
|
-
import subprocess
|
|
7
3
|
import sys
|
|
8
|
-
import textwrap
|
|
9
|
-
import warnings
|
|
10
|
-
from datetime import datetime
|
|
11
4
|
from pathlib import Path
|
|
12
|
-
from typing import Any, Literal
|
|
13
5
|
|
|
14
6
|
import click
|
|
15
|
-
from pydantic import SecretStr
|
|
16
7
|
|
|
17
|
-
from kimi_cli
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
load_agents_md,
|
|
23
|
-
)
|
|
24
|
-
from kimi_cli.config import (
|
|
25
|
-
Config,
|
|
26
|
-
ConfigError,
|
|
27
|
-
LLMModel,
|
|
28
|
-
LLMProvider,
|
|
29
|
-
load_config,
|
|
30
|
-
)
|
|
31
|
-
from kimi_cli.metadata import Session, continue_session, new_session
|
|
8
|
+
from kimi_cli import UIMode, kimi_run
|
|
9
|
+
from kimi_cli.agentspec import DEFAULT_AGENT_FILE
|
|
10
|
+
from kimi_cli.config import ConfigError, load_config
|
|
11
|
+
from kimi_cli.constant import VERSION
|
|
12
|
+
from kimi_cli.metadata import continue_session, new_session
|
|
32
13
|
from kimi_cli.share import get_share_dir
|
|
33
|
-
from kimi_cli.
|
|
34
|
-
from kimi_cli.
|
|
35
|
-
from kimi_cli.soul.denwarenji import DenwaRenji
|
|
36
|
-
from kimi_cli.soul.kimisoul import KimiSoul
|
|
37
|
-
from kimi_cli.ui.acp import ACPServer
|
|
38
|
-
from kimi_cli.ui.print import InputFormat, OutputFormat, PrintApp
|
|
39
|
-
from kimi_cli.ui.shell import Reload, ShellApp
|
|
40
|
-
from kimi_cli.utils.logging import StreamToLogger, logger
|
|
41
|
-
from kimi_cli.utils.provider import augment_provider_with_env_vars, create_llm
|
|
14
|
+
from kimi_cli.ui.print import InputFormat, OutputFormat
|
|
15
|
+
from kimi_cli.utils.logging import logger
|
|
42
16
|
|
|
43
|
-
__version__ = importlib.metadata.version("kimi-cli")
|
|
44
|
-
USER_AGENT = f"KimiCLI/{__version__}"
|
|
45
17
|
|
|
46
|
-
|
|
18
|
+
class Reload(Exception):
|
|
19
|
+
"""Reload configuration."""
|
|
20
|
+
|
|
21
|
+
pass
|
|
47
22
|
|
|
48
23
|
|
|
49
24
|
@click.command(context_settings=dict(help_option_names=["-h", "--help"]))
|
|
50
|
-
@click.version_option(
|
|
25
|
+
@click.version_option(VERSION)
|
|
51
26
|
@click.option(
|
|
52
27
|
"--verbose",
|
|
53
28
|
is_flag=True,
|
|
@@ -238,7 +213,6 @@ def kimi(
|
|
|
238
213
|
session=session,
|
|
239
214
|
command=command,
|
|
240
215
|
agent_file=agent_file,
|
|
241
|
-
verbose=verbose,
|
|
242
216
|
ui=ui,
|
|
243
217
|
input_format=input_format,
|
|
244
218
|
output_format=output_format,
|
|
@@ -253,132 +227,6 @@ def kimi(
|
|
|
253
227
|
continue
|
|
254
228
|
|
|
255
229
|
|
|
256
|
-
async def kimi_run(
|
|
257
|
-
*,
|
|
258
|
-
config: Config,
|
|
259
|
-
model_name: str | None,
|
|
260
|
-
work_dir: Path,
|
|
261
|
-
session: Session,
|
|
262
|
-
command: str | None = None,
|
|
263
|
-
agent_file: Path = DEFAULT_AGENT_FILE,
|
|
264
|
-
verbose: bool = True,
|
|
265
|
-
ui: UIMode = "shell",
|
|
266
|
-
input_format: InputFormat | None = None,
|
|
267
|
-
output_format: OutputFormat | None = None,
|
|
268
|
-
mcp_configs: list[dict[str, Any]] | None = None,
|
|
269
|
-
yolo: bool = False,
|
|
270
|
-
) -> bool:
|
|
271
|
-
"""Run Kimi CLI."""
|
|
272
|
-
echo = click.echo if verbose else lambda *args, **kwargs: None
|
|
273
|
-
|
|
274
|
-
model: LLMModel | None = None
|
|
275
|
-
provider: LLMProvider | None = None
|
|
276
|
-
|
|
277
|
-
# try to use config file
|
|
278
|
-
if not model_name and config.default_model:
|
|
279
|
-
# no --model specified && default model is set in config
|
|
280
|
-
model = config.models[config.default_model]
|
|
281
|
-
provider = config.providers[model.provider]
|
|
282
|
-
if model_name and model_name in config.models:
|
|
283
|
-
# --model specified && model is set in config
|
|
284
|
-
model = config.models[model_name]
|
|
285
|
-
provider = config.providers[model.provider]
|
|
286
|
-
|
|
287
|
-
if not model:
|
|
288
|
-
model = LLMModel(provider="", model="", max_context_size=100_000)
|
|
289
|
-
provider = LLMProvider(type="kimi", base_url="", api_key=SecretStr(""))
|
|
290
|
-
|
|
291
|
-
# try overwrite with environment variables
|
|
292
|
-
assert provider is not None
|
|
293
|
-
assert model is not None
|
|
294
|
-
augment_provider_with_env_vars(provider, model)
|
|
295
|
-
|
|
296
|
-
if not provider.base_url or not model.model:
|
|
297
|
-
llm = None
|
|
298
|
-
else:
|
|
299
|
-
echo(f"✓ Using LLM provider: {provider}")
|
|
300
|
-
echo(f"✓ Using LLM model: {model}")
|
|
301
|
-
stream = ui != "print" # use non-streaming mode only for print UI
|
|
302
|
-
llm = create_llm(provider, model, stream=stream, session_id=session.id)
|
|
303
|
-
|
|
304
|
-
# TODO: support Windows
|
|
305
|
-
ls = subprocess.run(["ls", "-la"], capture_output=True, text=True)
|
|
306
|
-
agents_md = load_agents_md(work_dir) or ""
|
|
307
|
-
if agents_md:
|
|
308
|
-
echo(f"✓ Loaded agents.md: {textwrap.shorten(agents_md, width=100)}")
|
|
309
|
-
|
|
310
|
-
agent_globals = AgentGlobals(
|
|
311
|
-
config=config,
|
|
312
|
-
llm=llm,
|
|
313
|
-
builtin_args=BuiltinSystemPromptArgs(
|
|
314
|
-
KIMI_NOW=datetime.now().astimezone().isoformat(),
|
|
315
|
-
KIMI_WORK_DIR=work_dir,
|
|
316
|
-
KIMI_WORK_DIR_LS=ls.stdout,
|
|
317
|
-
KIMI_AGENTS_MD=agents_md,
|
|
318
|
-
),
|
|
319
|
-
denwa_renji=DenwaRenji(),
|
|
320
|
-
session=session,
|
|
321
|
-
approval=Approval(yolo=yolo),
|
|
322
|
-
)
|
|
323
|
-
try:
|
|
324
|
-
agent = await load_agent_with_mcp(agent_file, agent_globals, mcp_configs or [])
|
|
325
|
-
except ValueError as e:
|
|
326
|
-
raise click.BadParameter(f"Failed to load agent: {e}") from e
|
|
327
|
-
echo(f"✓ Loaded agent: {agent.name}")
|
|
328
|
-
echo(f"✓ Loaded system prompt: {textwrap.shorten(agent.system_prompt, width=100)}")
|
|
329
|
-
echo(f"✓ Loaded tools: {[tool.name for tool in agent.toolset.tools]}")
|
|
330
|
-
|
|
331
|
-
if command is not None:
|
|
332
|
-
command = command.strip()
|
|
333
|
-
if not command:
|
|
334
|
-
raise click.BadParameter("Command cannot be empty")
|
|
335
|
-
|
|
336
|
-
context = Context(session.history_file)
|
|
337
|
-
restored = await context.restore()
|
|
338
|
-
if restored:
|
|
339
|
-
echo(f"✓ Restored history from {session.history_file}")
|
|
340
|
-
|
|
341
|
-
soul = KimiSoul(
|
|
342
|
-
agent,
|
|
343
|
-
agent_globals,
|
|
344
|
-
context=context,
|
|
345
|
-
loop_control=config.loop_control,
|
|
346
|
-
)
|
|
347
|
-
|
|
348
|
-
original_cwd = Path.cwd()
|
|
349
|
-
os.chdir(work_dir)
|
|
350
|
-
|
|
351
|
-
try:
|
|
352
|
-
if ui == "shell":
|
|
353
|
-
if command is None and not sys.stdin.isatty():
|
|
354
|
-
command = sys.stdin.read().strip()
|
|
355
|
-
echo(f"✓ Read command from stdin: {command}")
|
|
356
|
-
|
|
357
|
-
app = ShellApp(
|
|
358
|
-
soul,
|
|
359
|
-
welcome_info={
|
|
360
|
-
"Directory": str(work_dir),
|
|
361
|
-
"Session": session.id,
|
|
362
|
-
},
|
|
363
|
-
)
|
|
364
|
-
# to ignore possible warnings from dateparser
|
|
365
|
-
warnings.filterwarnings("ignore", category=DeprecationWarning)
|
|
366
|
-
with contextlib.redirect_stderr(StreamToLogger()):
|
|
367
|
-
return await app.run(command)
|
|
368
|
-
elif ui == "print":
|
|
369
|
-
app = PrintApp(soul, input_format or "text", output_format or "text")
|
|
370
|
-
return await app.run(command)
|
|
371
|
-
elif ui == "acp":
|
|
372
|
-
if command is not None:
|
|
373
|
-
logger.warning("ACP server ignores command argument")
|
|
374
|
-
app = ACPServer(soul)
|
|
375
|
-
return await app.run()
|
|
376
|
-
else:
|
|
377
|
-
raise click.BadParameter(f"Invalid UI mode: {ui}")
|
|
378
|
-
finally:
|
|
379
|
-
os.chdir(original_cwd)
|
|
380
|
-
|
|
381
|
-
|
|
382
230
|
def main():
|
|
383
231
|
kimi()
|
|
384
232
|
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
import os
|
|
2
|
+
from typing import NamedTuple
|
|
2
3
|
|
|
3
|
-
from kosong.chat_provider import
|
|
4
|
-
from kosong.chat_provider.chaos import ChaosConfig
|
|
4
|
+
from kosong.base.chat_provider import ChatProvider
|
|
5
|
+
from kosong.chat_provider.chaos import ChaosChatProvider, ChaosConfig
|
|
6
|
+
from kosong.chat_provider.kimi import Kimi
|
|
7
|
+
from kosong.chat_provider.openai_legacy import OpenAILegacy
|
|
5
8
|
from pydantic import SecretStr
|
|
6
9
|
|
|
7
|
-
import kimi_cli
|
|
8
10
|
from kimi_cli.config import LLMModel, LLMProvider
|
|
9
|
-
from kimi_cli.
|
|
11
|
+
from kimi_cli.constant import USER_AGENT
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class LLM(NamedTuple):
|
|
15
|
+
chat_provider: ChatProvider
|
|
16
|
+
max_context_size: int
|
|
10
17
|
|
|
11
18
|
|
|
12
19
|
def augment_provider_with_env_vars(provider: LLMProvider, model: LLMModel):
|
|
@@ -44,7 +51,7 @@ def create_llm(
|
|
|
44
51
|
api_key=provider.api_key.get_secret_value(),
|
|
45
52
|
stream=stream,
|
|
46
53
|
default_headers={
|
|
47
|
-
"User-Agent":
|
|
54
|
+
"User-Agent": USER_AGENT,
|
|
48
55
|
},
|
|
49
56
|
)
|
|
50
57
|
if session_id:
|