Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 9 additions & 3 deletions src/kimi_cli/app.py
Original file line number Diff line number Diff line change
Expand Up @@ -241,8 +241,14 @@ async def _ui_loop_fn(wire: Wire) -> None:
# wait for the soul task to finish, or raise
await soul_task

async def run_shell(self, command: str | None = None) -> bool:
"""Run the Kimi Code CLI instance with shell UI."""
async def run_shell(self, command: str | None = None, *, stay_open: bool = False) -> bool:
"""Run the Kimi Code CLI instance with shell UI.

Args:
command: The initial command/prompt to process. If None, starts interactively.
stay_open: If True and command is provided, stay open after processing the command.
If False and command is provided, exit after processing.
"""
from kimi_cli.ui.shell import Shell, WelcomeInfoItem

welcome_info = [
Expand Down Expand Up @@ -317,7 +323,7 @@ async def run_shell(self, command: str | None = None) -> bool:
)
async with self._env():
shell = Shell(self._soul, welcome_info=welcome_info)
return await shell.run(command)
return await shell.run(command, stay_open=stay_open)

async def run_print(
self,
Expand Down
33 changes: 31 additions & 2 deletions src/kimi_cli/cli/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,21 @@ def kimi(
"-p",
"--command",
"-c",
help="User prompt to the agent. Default: prompt interactively.",
help=(
"User prompt to the agent. The session exits after processing. "
"Default: prompt interactively."
),
),
] = None,
starting_prompt: Annotated[
str | None,
typer.Option(
"--starting-prompt",
"-s",
help=(
"Starting prompt to the agent. The session stays open after processing. "
"Default: prompt interactively."
),
),
] = None,
print_mode: Annotated[
Expand Down Expand Up @@ -359,6 +373,10 @@ def kimi(
"--config": config_string is not None,
"--config-file": config_file is not None,
},
{
"--prompt": prompt is not None,
"--starting-prompt": starting_prompt is not None,
},
]
for option_set in conflict_option_sets:
active_options = [flag for flag, active in option_set.items() if active]
Expand Down Expand Up @@ -388,6 +406,13 @@ def kimi(
if not prompt:
raise typer.BadParameter("Prompt cannot be empty", param_hint="--prompt")

if starting_prompt is not None:
starting_prompt = starting_prompt.strip()
if not starting_prompt:
raise typer.BadParameter(
"Starting prompt cannot be empty", param_hint="--starting-prompt"
)

if input_format is not None and ui != "print":
raise typer.BadParameter(
"Input format is only supported for print UI",
Expand Down Expand Up @@ -483,7 +508,11 @@ async def _run(session_id: str | None) -> tuple[Session, bool]:
)
match ui:
case "shell":
succeeded = await instance.run_shell(prompt)
# --prompt/-c exits after processing, --starting-prompt/-s stays open
succeeded = await instance.run_shell(
prompt or starting_prompt,
stay_open=starting_prompt is not None,
)
case "print":
succeeded = await instance.run_print(
input_format or "text",
Comment thread
stebbins marked this conversation as resolved.
Expand Down
23 changes: 19 additions & 4 deletions src/kimi_cli/ui/shell/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,26 @@ def available_slash_commands(self) -> dict[str, SlashCommand[Any]]:
"""Get all available slash commands, including shell-level and soul-level commands."""
return self._available_slash_commands

async def run(self, command: str | None = None) -> bool:
async def run(self, command: str | None = None, *, stay_open: bool = False) -> bool:
"""Run the shell UI.

Args:
command: The initial command/prompt to process. If None, starts interactively.
stay_open: If True and command is provided, stay open after processing the command.
If False and command is provided, exit after processing.

Returns:
bool: True if the run succeeded, False otherwise.
"""
if command is not None:
# run single command and exit
logger.info("Running agent with command: {command}", command=command)
return await self.run_soul_command(command)
if not stay_open:
# run single command and exit
logger.info("Running agent with command: {command}", command=command)
return await self.run_soul_command(command)
# stay_open is True: process the command first, then continue to interactive loop
logger.info("Running agent with starting command: {command}", command=command)
await self.run_soul_command(command)
# continue to interactive loop below

# Start auto-update background task if not disabled
if get_env_bool("KIMI_CLI_NO_AUTO_UPDATE"):
Comment thread
stebbins marked this conversation as resolved.
Expand Down