kimi-cli 0.41__py3-none-any.whl → 0.43__py3-none-any.whl

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 (42) hide show
  1. kimi_cli/CHANGELOG.md +28 -0
  2. kimi_cli/__init__.py +169 -102
  3. kimi_cli/agents/{koder → default}/agent.yaml +1 -1
  4. kimi_cli/agentspec.py +19 -8
  5. kimi_cli/cli.py +51 -37
  6. kimi_cli/config.py +33 -14
  7. kimi_cli/exception.py +16 -0
  8. kimi_cli/llm.py +31 -3
  9. kimi_cli/metadata.py +5 -68
  10. kimi_cli/session.py +81 -0
  11. kimi_cli/soul/__init__.py +22 -4
  12. kimi_cli/soul/agent.py +18 -23
  13. kimi_cli/soul/context.py +0 -5
  14. kimi_cli/soul/kimisoul.py +40 -25
  15. kimi_cli/soul/message.py +1 -1
  16. kimi_cli/soul/{globals.py → runtime.py} +13 -11
  17. kimi_cli/tools/file/glob.py +1 -1
  18. kimi_cli/tools/file/patch.py +1 -1
  19. kimi_cli/tools/file/read.py +1 -1
  20. kimi_cli/tools/file/replace.py +1 -1
  21. kimi_cli/tools/file/write.py +1 -1
  22. kimi_cli/tools/task/__init__.py +29 -21
  23. kimi_cli/tools/web/search.py +3 -0
  24. kimi_cli/ui/acp/__init__.py +24 -28
  25. kimi_cli/ui/print/__init__.py +27 -30
  26. kimi_cli/ui/shell/__init__.py +58 -42
  27. kimi_cli/ui/shell/keyboard.py +82 -14
  28. kimi_cli/ui/shell/metacmd.py +3 -8
  29. kimi_cli/ui/shell/prompt.py +208 -6
  30. kimi_cli/ui/shell/replay.py +104 -0
  31. kimi_cli/ui/shell/visualize.py +54 -57
  32. kimi_cli/utils/message.py +14 -0
  33. kimi_cli/utils/signals.py +41 -0
  34. kimi_cli/utils/string.py +8 -0
  35. kimi_cli/wire/__init__.py +13 -0
  36. {kimi_cli-0.41.dist-info → kimi_cli-0.43.dist-info}/METADATA +21 -20
  37. {kimi_cli-0.41.dist-info → kimi_cli-0.43.dist-info}/RECORD +41 -38
  38. kimi_cli/agents/koder/README.md +0 -3
  39. /kimi_cli/agents/{koder → default}/sub.yaml +0 -0
  40. /kimi_cli/agents/{koder → default}/system.md +0 -0
  41. {kimi_cli-0.41.dist-info → kimi_cli-0.43.dist-info}/WHEEL +0 -0
  42. {kimi_cli-0.41.dist-info → kimi_cli-0.43.dist-info}/entry_points.txt +0 -0
kimi_cli/CHANGELOG.md CHANGED
@@ -9,6 +9,34 @@ 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.43] - 2025-10-30
13
+
14
+ ### Added
15
+
16
+ - Basic Windows support (experimental)
17
+ - Display warnings when base URL or API key is overridden in environment variables
18
+ - Support image input if the LLM model supports it
19
+ - Replay recent context history when continuing a session
20
+
21
+ ### Fixed
22
+
23
+ - Ensure new line after executing shell commands
24
+
25
+ ## [0.42] - 2025-10-28
26
+
27
+ ### Added
28
+
29
+ - Support Ctrl-J or Alt-Enter to insert a new line
30
+
31
+ ### Changed
32
+
33
+ - Change mode switch shortcut from Ctrl-K to Ctrl-X
34
+ - Improve overall robustness
35
+
36
+ ### Fixed
37
+
38
+ - Fix ACP server `no attribute` error
39
+
12
40
  ## [0.41] - 2025-10-26
13
41
 
14
42
  ### Fixed
kimi_cli/__init__.py CHANGED
@@ -1,125 +1,192 @@
1
1
  import contextlib
2
2
  import os
3
3
  import warnings
4
+ from collections.abc import Generator
4
5
  from pathlib import Path
5
- from typing import Any, Literal
6
+ from typing import Any
6
7
 
7
- import click
8
8
  from pydantic import SecretStr
9
9
 
10
10
  from kimi_cli.agentspec import DEFAULT_AGENT_FILE
11
- from kimi_cli.config import Config, LLMModel, LLMProvider
11
+ from kimi_cli.config import LLMModel, LLMProvider, load_config
12
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
13
+ from kimi_cli.session import Session
14
+ from kimi_cli.soul.agent import load_agent
15
15
  from kimi_cli.soul.context import Context
16
- from kimi_cli.soul.globals import AgentGlobals
17
16
  from kimi_cli.soul.kimisoul import KimiSoul
17
+ from kimi_cli.soul.runtime import Runtime
18
18
  from kimi_cli.ui.acp import ACPServer
19
19
  from kimi_cli.ui.print import InputFormat, OutputFormat, PrintApp
20
- from kimi_cli.ui.shell import ShellApp
20
+ from kimi_cli.ui.shell import ShellApp, WelcomeInfoItem
21
21
  from kimi_cli.utils.logging import StreamToLogger, logger
22
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
- },
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
+ env_overrides = 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
+ welcome_info = [
85
+ WelcomeInfoItem(name="Directory", value=str(session.work_dir)),
86
+ WelcomeInfoItem(name="Session", value=session.id),
87
+ ]
88
+ if base_url := env_overrides.get("KIMI_BASE_URL"):
89
+ welcome_info.append(
90
+ WelcomeInfoItem(
91
+ name="API URL",
92
+ value=f"{base_url} (from KIMI_BASE_URL)",
93
+ level=WelcomeInfoItem.Level.WARN,
94
+ )
95
+ )
96
+ if not llm:
97
+ welcome_info.append(
98
+ WelcomeInfoItem(
99
+ name="Model",
100
+ value="not set, send /setup to configure",
101
+ level=WelcomeInfoItem.Level.WARN,
102
+ )
103
+ )
104
+ elif "KIMI_MODEL_NAME" in env_overrides:
105
+ welcome_info.append(
106
+ WelcomeInfoItem(
107
+ name="Model",
108
+ value=f"{model.model} (from KIMI_MODEL_NAME)",
109
+ level=WelcomeInfoItem.Level.WARN,
110
+ )
111
+ )
112
+ else:
113
+ welcome_info.append(
114
+ WelcomeInfoItem(
115
+ name="Model",
116
+ value=model.model,
117
+ level=WelcomeInfoItem.Level.INFO,
118
+ )
103
119
  )
120
+
121
+ runtime = await Runtime.create(config, llm, session, yolo)
122
+
123
+ if agent_file is None:
124
+ agent_file = DEFAULT_AGENT_FILE
125
+ agent = await load_agent(agent_file, runtime, mcp_configs=mcp_configs or [])
126
+
127
+ context = Context(session.history_file)
128
+ await context.restore()
129
+
130
+ soul = KimiSoul(
131
+ agent,
132
+ runtime,
133
+ context=context,
134
+ )
135
+ return KimiCLI(soul, session, welcome_info)
136
+
137
+ def __init__(
138
+ self,
139
+ soul: KimiSoul,
140
+ session: Session,
141
+ welcome_info: list[WelcomeInfoItem],
142
+ ) -> None:
143
+ self._soul = soul
144
+ self._session = session
145
+ self._welcome_info = welcome_info
146
+
147
+ @property
148
+ def soul(self) -> KimiSoul:
149
+ """Get the KimiSoul instance."""
150
+ return self._soul
151
+
152
+ @property
153
+ def session(self) -> Session:
154
+ """Get the Session instance."""
155
+ return self._session
156
+
157
+ @contextlib.contextmanager
158
+ def _app_env(self) -> Generator[None]:
159
+ original_cwd = Path.cwd()
160
+ os.chdir(self._session.work_dir)
161
+ try:
104
162
  # to ignore possible warnings from dateparser
105
163
  warnings.filterwarnings("ignore", category=DeprecationWarning)
106
164
  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
165
+ yield
166
+ finally:
167
+ os.chdir(original_cwd)
168
+
169
+ async def run_shell_mode(self, command: str | None = None) -> bool:
170
+ with self._app_env():
171
+ app = ShellApp(self._soul, welcome_info=self._welcome_info)
172
+ return await app.run(command)
173
+
174
+ async def run_print_mode(
175
+ self,
176
+ input_format: InputFormat,
177
+ output_format: OutputFormat,
178
+ command: str | None = None,
179
+ ) -> bool:
180
+ with self._app_env():
110
181
  app = PrintApp(
111
- soul,
112
- input_format or "text",
113
- output_format or "text",
114
- context.file_backend,
182
+ self._soul,
183
+ input_format,
184
+ output_format,
185
+ self._session.history_file,
115
186
  )
116
187
  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)
188
+
189
+ async def run_acp_server(self) -> bool:
190
+ with self._app_env():
191
+ app = ACPServer(self._soul)
121
192
  return await app.run()
122
- else:
123
- raise click.BadParameter(f"Invalid UI mode: {ui}")
124
- finally:
125
- os.chdir(original_cwd)
@@ -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."
kimi_cli/agentspec.py CHANGED
@@ -4,12 +4,14 @@ from typing import Any, NamedTuple
4
4
  import yaml
5
5
  from pydantic import BaseModel, Field
6
6
 
7
+ from kimi_cli.exception import AgentSpecError
8
+
7
9
 
8
10
  def get_agents_dir() -> Path:
9
11
  return Path(__file__).parent / "agents"
10
12
 
11
13
 
12
- DEFAULT_AGENT_FILE = get_agents_dir() / "koder" / "agent.yaml"
14
+ DEFAULT_AGENT_FILE = get_agents_dir() / "default" / "agent.yaml"
13
15
 
14
16
 
15
17
  class AgentSpec(BaseModel):
@@ -47,15 +49,21 @@ class ResolvedAgentSpec(NamedTuple):
47
49
 
48
50
 
49
51
  def load_agent_spec(agent_file: Path) -> ResolvedAgentSpec:
50
- """Load agent specification from file."""
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
+ """
51
59
  agent_spec = _load_agent_spec(agent_file)
52
60
  assert agent_spec.extend is None, "agent extension should be recursively resolved"
53
61
  if agent_spec.name is None:
54
- raise ValueError("Agent name is required")
62
+ raise AgentSpecError("Agent name is required")
55
63
  if agent_spec.system_prompt_path is None:
56
- raise ValueError("System prompt path is required")
64
+ raise AgentSpecError("System prompt path is required")
57
65
  if agent_spec.tools is None:
58
- raise ValueError("Tools are required")
66
+ raise AgentSpecError("Tools are required")
59
67
  return ResolvedAgentSpec(
60
68
  name=agent_spec.name,
61
69
  system_prompt_path=agent_spec.system_prompt_path,
@@ -68,12 +76,15 @@ def load_agent_spec(agent_file: Path) -> ResolvedAgentSpec:
68
76
 
69
77
  def _load_agent_spec(agent_file: Path) -> AgentSpec:
70
78
  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)
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
73
84
 
74
85
  version = data.get("version", 1)
75
86
  if version != 1:
76
- raise ValueError(f"Unsupported agent spec version: {version}")
87
+ raise AgentSpecError(f"Unsupported agent spec version: {version}")
77
88
 
78
89
  agent_spec = AgentSpec(**data.get("agent", {}))
79
90
  if agent_spec.system_prompt_path is not None:
kimi_cli/cli.py CHANGED
@@ -1,15 +1,15 @@
1
1
  import asyncio
2
2
  import json
3
3
  import sys
4
+ from collections.abc import Callable
4
5
  from pathlib import Path
6
+ from typing import Any, Literal, get_args
5
7
 
6
8
  import click
7
9
 
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
10
+ from kimi_cli import KimiCLI
11
11
  from kimi_cli.constant import VERSION
12
- from kimi_cli.metadata import continue_session, new_session
12
+ from kimi_cli.session import Session
13
13
  from kimi_cli.share import get_share_dir
14
14
  from kimi_cli.ui.print import InputFormat, OutputFormat
15
15
  from kimi_cli.utils.logging import logger
@@ -21,6 +21,9 @@ class Reload(Exception):
21
21
  pass
22
22
 
23
23
 
24
+ UIMode = Literal["shell", "print", "acp"]
25
+
26
+
24
27
  @click.command(context_settings=dict(help_option_names=["-h", "--help"]))
25
28
  @click.version_option(VERSION)
26
29
  @click.option(
@@ -38,8 +41,8 @@ class Reload(Exception):
38
41
  @click.option(
39
42
  "--agent-file",
40
43
  type=click.Path(exists=True, file_okay=True, dir_okay=False, path_type=Path),
41
- default=DEFAULT_AGENT_FILE,
42
- help="Custom agent specification file. Default: builtin Kimi Koder.",
44
+ default=None,
45
+ help="Custom agent specification file. Default: builtin default agent.",
43
46
  )
44
47
  @click.option(
45
48
  "--model",
@@ -77,7 +80,7 @@ class Reload(Exception):
77
80
  @click.option(
78
81
  "--ui",
79
82
  "ui",
80
- type=click.Choice(["shell", "print", "acp"]),
83
+ type=click.Choice(get_args(UIMode)),
81
84
  default="shell",
82
85
  help="UI mode to use. Default: shell.",
83
86
  )
@@ -85,7 +88,7 @@ class Reload(Exception):
85
88
  "--print",
86
89
  "ui",
87
90
  flag_value="print",
88
- help="Run in print mode. Shortcut for `--ui print`.",
91
+ help="Run in print mode. Shortcut for `--ui print`. Note: print mode implicitly adds `--yolo`.",
89
92
  )
90
93
  @click.option(
91
94
  "--acp",
@@ -95,7 +98,7 @@ class Reload(Exception):
95
98
  )
96
99
  @click.option(
97
100
  "--input-format",
98
- type=click.Choice(["text", "stream-json"]),
101
+ type=click.Choice(get_args(InputFormat)),
99
102
  default=None,
100
103
  help=(
101
104
  "Input format to use. Must be used with `--print` "
@@ -105,7 +108,7 @@ class Reload(Exception):
105
108
  )
106
109
  @click.option(
107
110
  "--output-format",
108
- type=click.Choice(["text", "stream-json"]),
111
+ type=click.Choice(get_args(OutputFormat)),
109
112
  default=None,
110
113
  help="Output format to use. Must be used with `--print`. Default: text.",
111
114
  )
@@ -140,7 +143,7 @@ class Reload(Exception):
140
143
  def kimi(
141
144
  verbose: bool,
142
145
  debug: bool,
143
- agent_file: Path,
146
+ agent_file: Path | None,
144
147
  model_name: str | None,
145
148
  work_dir: Path,
146
149
  continue_: bool,
@@ -153,7 +156,11 @@ def kimi(
153
156
  yolo: bool,
154
157
  ):
155
158
  """Kimi, your next CLI agent."""
156
- echo = click.echo if verbose else lambda *args, **kwargs: None
159
+
160
+ def _noop_echo(*args: Any, **kwargs: Any):
161
+ pass
162
+
163
+ echo: Callable[..., None] = click.echo if verbose else _noop_echo
157
164
 
158
165
  logger.add(
159
166
  get_share_dir() / "logs" / "kimi.log",
@@ -163,19 +170,23 @@ def kimi(
163
170
  )
164
171
 
165
172
  work_dir = work_dir.absolute()
166
-
167
173
  if continue_:
168
- session = continue_session(work_dir)
174
+ session = Session.continue_(work_dir)
169
175
  if session is None:
170
176
  raise click.BadOptionUsage(
171
177
  "--continue", "No previous session found for the working directory"
172
178
  )
173
179
  echo(f"✓ Continuing previous session: {session.id}")
174
180
  else:
175
- session = new_session(work_dir)
181
+ session = Session.create(work_dir)
176
182
  echo(f"✓ Created new session: {session.id}")
177
183
  echo(f"✓ Session history file: {session.history_file}")
178
184
 
185
+ if command is not None:
186
+ command = command.strip()
187
+ if not command:
188
+ raise click.BadOptionUsage("--command", "Command cannot be empty")
189
+
179
190
  if input_format is not None and ui != "print":
180
191
  raise click.BadOptionUsage(
181
192
  "--input-format",
@@ -188,7 +199,7 @@ def kimi(
188
199
  )
189
200
 
190
201
  try:
191
- mcp_configs = [json.loads(conf.read_text()) for conf in mcp_config_file]
202
+ mcp_configs = [json.loads(conf.read_text(encoding="utf-8")) for conf in mcp_config_file]
192
203
  except json.JSONDecodeError as e:
193
204
  raise click.BadOptionUsage("--mcp-config-file", f"Invalid JSON: {e}") from e
194
205
 
@@ -197,29 +208,32 @@ def kimi(
197
208
  except json.JSONDecodeError as e:
198
209
  raise click.BadOptionUsage("--mcp-config", f"Invalid JSON: {e}") from e
199
210
 
211
+ async def _run() -> bool:
212
+ instance = await KimiCLI.create(
213
+ session,
214
+ yolo=yolo or (ui == "print"), # print mode implies yolo
215
+ stream=ui != "print", # use non-streaming mode only for print UI
216
+ mcp_configs=mcp_configs,
217
+ model_name=model_name,
218
+ agent_file=agent_file,
219
+ )
220
+ match ui:
221
+ case "shell":
222
+ return await instance.run_shell_mode(command)
223
+ case "print":
224
+ return await instance.run_print_mode(
225
+ input_format or "text",
226
+ output_format or "text",
227
+ command,
228
+ )
229
+ case "acp":
230
+ if command is not None:
231
+ logger.warning("ACP server ignores command argument")
232
+ return await instance.run_acp_server()
233
+
200
234
  while True:
201
235
  try:
202
- try:
203
- config = load_config()
204
- except ConfigError as e:
205
- raise click.ClickException(f"Failed to load config: {e}") from e
206
- echo(f"✓ Loaded config: {config}")
207
-
208
- succeeded = asyncio.run(
209
- kimi_run(
210
- config=config,
211
- model_name=model_name,
212
- work_dir=work_dir,
213
- session=session,
214
- command=command,
215
- agent_file=agent_file,
216
- ui=ui,
217
- input_format=input_format,
218
- output_format=output_format,
219
- mcp_configs=mcp_configs,
220
- yolo=yolo,
221
- )
222
- )
236
+ succeeded = asyncio.run(_run())
223
237
  if not succeeded:
224
238
  sys.exit(1)
225
239
  break