diff --git a/.changeset/add-prompt-interactive.md b/.changeset/add-prompt-interactive.md new file mode 100644 index 0000000..08ba4c2 --- /dev/null +++ b/.changeset/add-prompt-interactive.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/kimi-code": minor +--- + +Add `--prompt-interactive` (`-P`) CLI option that sends an initial prompt to +the agent then keeps the interactive session open for follow-up conversation. diff --git a/apps/kimi-code/src/cli/commands.ts b/apps/kimi-code/src/cli/commands.ts index b0a65f2..5b201c4 100644 --- a/apps/kimi-code/src/cli/commands.ts +++ b/apps/kimi-code/src/cli/commands.ts @@ -52,6 +52,12 @@ export function createProgram( 'Run one prompt non-interactively and print the response.', ), ) + .addOption( + new Option( + '-P, --prompt-interactive ', + 'Send prompt to the agent then keep the interactive session open.', + ), + ) .addOption( new Option( '--output-format ', @@ -88,6 +94,7 @@ export function createProgram( model: raw['model'] as string | undefined, outputFormat: raw['outputFormat'] as CLIOptions['outputFormat'], prompt: raw['prompt'] as string | undefined, + promptInteractive: raw['promptInteractive'] as string | undefined, skillsDirs: raw['skillsDir'] as string[], }; diff --git a/apps/kimi-code/src/cli/options.ts b/apps/kimi-code/src/cli/options.ts index 67bc86d..292acf8 100644 --- a/apps/kimi-code/src/cli/options.ts +++ b/apps/kimi-code/src/cli/options.ts @@ -9,6 +9,7 @@ export interface CLIOptions { model: string | undefined; outputFormat: PromptOutputFormat | undefined; prompt: string | undefined; + promptInteractive: string | undefined; skillsDirs: string[]; } @@ -27,9 +28,18 @@ export class OptionConflictError extends Error { export function validateOptions(opts: CLIOptions): ValidatedOptions { const prompt = opts.prompt; const promptMode = prompt !== undefined; + const promptInteractive = opts.promptInteractive; + const promptInteractiveMode = promptInteractive !== undefined; + + if (promptMode && promptInteractiveMode) { + throw new OptionConflictError('Cannot combine --prompt with --prompt-interactive.'); + } if (promptMode && prompt.trim().length === 0) { throw new OptionConflictError('Prompt cannot be empty.'); } + if (promptInteractiveMode && promptInteractive.trim().length === 0) { + throw new OptionConflictError('Prompt cannot be empty.'); + } if (opts.model !== undefined && opts.model.trim().length === 0) { throw new OptionConflictError('Model cannot be empty.'); } @@ -45,6 +55,11 @@ export function validateOptions(opts: CLIOptions): ValidatedOptions { if (promptMode && opts.session === '') { throw new OptionConflictError('Cannot use --session without an id in prompt mode.'); } + if (promptInteractiveMode && opts.session === '') { + throw new OptionConflictError( + 'Cannot use --prompt-interactive with --session without an id.', + ); + } if (opts.continue && opts.session !== undefined) { throw new OptionConflictError('Cannot combine --continue, --session.'); } diff --git a/apps/kimi-code/src/cli/run-shell.ts b/apps/kimi-code/src/cli/run-shell.ts index 6e3e320..862a797 100644 --- a/apps/kimi-code/src/cli/run-shell.ts +++ b/apps/kimi-code/src/cli/run-shell.ts @@ -94,6 +94,7 @@ export async function runShell( resolvedTheme, migrationPlan, migrateOnly: runOptions.migrateOnly, + initialCommand: opts.promptInteractive, }); let firstLaunch = false; diff --git a/apps/kimi-code/src/main.ts b/apps/kimi-code/src/main.ts index d2804a9..c694782 100644 --- a/apps/kimi-code/src/main.ts +++ b/apps/kimi-code/src/main.ts @@ -68,6 +68,7 @@ const MIGRATE_CLI_OPTIONS: CLIOptions = { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }; diff --git a/apps/kimi-code/src/tui/kimi-tui.ts b/apps/kimi-code/src/tui/kimi-tui.ts index 885b6db..8b946e8 100644 --- a/apps/kimi-code/src/tui/kimi-tui.ts +++ b/apps/kimi-code/src/tui/kimi-tui.ts @@ -248,6 +248,8 @@ export interface KimiTUIStartupInput { readonly migrationPlan?: MigrationPlan | null; /** When true, run only the migration screen, then exit (the `kimi migrate` command). */ readonly migrateOnly?: boolean; + /** Initial command to send after startup (--prompt-interactive). */ + readonly initialCommand?: string; } export interface PendingExit { @@ -567,6 +569,8 @@ export class KimiTUI { private readonly migrationPlan: MigrationPlan | null; // When true, the migration screen is the whole session: run it, then exit. private readonly migrateOnly: boolean; + // Initial command supplied via --prompt-interactive, sent after startup. + private initialCommand: string | undefined; // High-frequency model/tool deltas update draft state immediately, then use // these flags to coalesce expensive component rebuilds into periodic flushes. private streamingUiFlushTimer: ReturnType | undefined; @@ -602,6 +606,7 @@ export class KimiTUI { this.options = tuiOptions; this.migrationPlan = startupInput.migrationPlan ?? null; this.migrateOnly = startupInput.migrateOnly ?? false; + this.initialCommand = startupInput.initialCommand; this.state = createTUIState(tuiOptions); this.gitLsFilesCache = createGitLsFilesCache(tuiOptions.initialAppState.workDir); @@ -745,6 +750,18 @@ export class KimiTUI { } } + // Sends the initial command from --prompt-interactive after startup completes. + private submitInitialCommand(): void { + if (!this.initialCommand || this.initialCommand.trim().length === 0) return; + const command = this.initialCommand; + this.initialCommand = undefined; + if (!this.session || this.state.appState.model.trim().length === 0) { + this.state.editor.setText(command); + return; + } + this.handleUserInput(command); + } + // Creates/resumes the session, renders the Welcome banner, configures // autocomplete and input history, and mounts the editor. Returns whether // transcript history should be replayed. @@ -796,6 +813,7 @@ export class KimiTUI { this.refreshSessionTitle(); } void this.refreshSkillCommands(this.session); + this.submitInitialCommand(); } // Warns tmux users when modified Enter shortcuts are likely to be swallowed. diff --git a/apps/kimi-code/test/cli/main.test.ts b/apps/kimi-code/test/cli/main.test.ts index 8ab1ea9..0e27a8e 100644 --- a/apps/kimi-code/test/cli/main.test.ts +++ b/apps/kimi-code/test/cli/main.test.ts @@ -72,6 +72,7 @@ function defaultOpts(): CLIOptions { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }; } diff --git a/apps/kimi-code/test/cli/options.test.ts b/apps/kimi-code/test/cli/options.test.ts index a98268c..ee144c8 100644 --- a/apps/kimi-code/test/cli/options.test.ts +++ b/apps/kimi-code/test/cli/options.test.ts @@ -211,6 +211,68 @@ describe('CLI options parsing', () => { }); }); + describe('--prompt-interactive / -P', () => { + it('parses -P as shell mode with promptInteractive set', () => { + const opts = parse(['-P', 'hello world']); + expect(opts.promptInteractive).toBe('hello world'); + expect(validateOptions(opts).uiMode).toBe('shell'); + }); + + it('parses --prompt-interactive=value', () => { + const opts = parse(['--prompt-interactive=hello world']); + expect(opts.promptInteractive).toBe('hello world'); + expect(validateOptions(opts).uiMode).toBe('shell'); + }); + + it('rejects empty promptInteractive values', () => { + const opts = parse(['-P', ' ']); + expect(() => validateOptions(opts)).toThrow(OptionConflictError); + expect(() => validateOptions(opts)).toThrow('Prompt cannot be empty.'); + }); + + it('rejects --prompt combined with --prompt-interactive', () => { + const opts = parse(['-p', 'run', '-P', 'hello']); + expect(() => validateOptions(opts)).toThrow(OptionConflictError); + expect(() => validateOptions(opts)).toThrow('Cannot combine --prompt with --prompt-interactive.'); + }); + + it('allows --prompt-interactive with --yolo', () => { + const opts = parse(['-P', 'hello', '--yolo']); + expect(opts.promptInteractive).toBe('hello'); + expect(opts.yolo).toBe(true); + expect(validateOptions(opts).uiMode).toBe('shell'); + }); + + it('allows --prompt-interactive with --plan', () => { + const opts = parse(['-P', 'hello', '--plan']); + expect(opts.promptInteractive).toBe('hello'); + expect(opts.plan).toBe(true); + expect(validateOptions(opts).uiMode).toBe('shell'); + }); + + it('rejects --prompt-interactive with bare --session (no id)', () => { + const opts = parse(['-P', 'hello', '--session']); + expect(() => validateOptions(opts)).toThrow(OptionConflictError); + expect(() => validateOptions(opts)).toThrow( + 'Cannot use --prompt-interactive with --session without an id.', + ); + }); + + it('allows --prompt-interactive with a concrete --session id', () => { + const opts = parse(['-P', 'hello', '--session', 'ses_123']); + expect(opts.promptInteractive).toBe('hello'); + expect(opts.session).toBe('ses_123'); + expect(validateOptions(opts).uiMode).toBe('shell'); + }); + + it('allows --prompt-interactive with --continue', () => { + const opts = parse(['-P', 'hello', '--continue']); + expect(opts.promptInteractive).toBe('hello'); + expect(opts.continue).toBe(true); + expect(validateOptions(opts).uiMode).toBe('shell'); + }); + }); + describe('--skills-dir', () => { it('collects repeated skill directories', () => { expect(parse(['--skills-dir', '/one', '--skills-dir=/two']).skillsDirs).toEqual([ diff --git a/apps/kimi-code/test/cli/run-prompt.test.ts b/apps/kimi-code/test/cli/run-prompt.test.ts index 0169acb..1b8856c 100644 --- a/apps/kimi-code/test/cli/run-prompt.test.ts +++ b/apps/kimi-code/test/cli/run-prompt.test.ts @@ -118,6 +118,7 @@ function opts(overrides: Partial[0]> = {}) { model: undefined, outputFormat: undefined, prompt: 'say hello', + promptInteractive: undefined, skillsDirs: [], ...overrides, }; diff --git a/apps/kimi-code/test/cli/run-shell.test.ts b/apps/kimi-code/test/cli/run-shell.test.ts index 64f9e02..a2c91b2 100644 --- a/apps/kimi-code/test/cli/run-shell.test.ts +++ b/apps/kimi-code/test/cli/run-shell.test.ts @@ -165,6 +165,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }; @@ -252,6 +253,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -282,6 +284,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -318,6 +321,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -350,6 +354,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -400,6 +405,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -436,6 +442,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -472,6 +479,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -524,6 +532,7 @@ describe('runShell', () => { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, '1.2.3-test', @@ -532,4 +541,58 @@ describe('runShell', () => { ).rejects.toThrow('Invalid configuration'); expect(mocks.tuiStart).not.toHaveBeenCalled(); }); + + it('forwards promptInteractive as initialCommand in KimiTUIStartupInput', async () => { + mocks.loadTuiConfig.mockResolvedValue({ + theme: 'dark', + editorCommand: null, + notifications: { enabled: true, condition: 'unfocused' }, + }); + mocks.tuiStart.mockResolvedValue(undefined); + + await runShell( + { + session: undefined, + continue: false, + yolo: false, + plan: false, + model: undefined, + outputFormat: undefined, + prompt: undefined, + promptInteractive: 'hello world', + skillsDirs: [], + }, + '1.2.3-test', + ); + + const [, , startupInput] = mocks.kimiTuiConstructor.mock.calls[0]!; + expect(startupInput.initialCommand).toBe('hello world'); + }); + + it('passes undefined initialCommand when promptInteractive is not set', async () => { + mocks.loadTuiConfig.mockResolvedValue({ + theme: 'dark', + editorCommand: null, + notifications: { enabled: true, condition: 'unfocused' }, + }); + mocks.tuiStart.mockResolvedValue(undefined); + + await runShell( + { + session: undefined, + continue: false, + yolo: false, + plan: false, + model: undefined, + outputFormat: undefined, + prompt: undefined, + promptInteractive: undefined, + skillsDirs: [], + }, + '1.2.3-test', + ); + + const [, , startupInput] = mocks.kimiTuiConstructor.mock.calls[0]!; + expect(startupInput.initialCommand).toBeUndefined(); + }); }); diff --git a/apps/kimi-code/test/tui/activity-pane.test.ts b/apps/kimi-code/test/tui/activity-pane.test.ts index d0eef5d..279879e 100644 --- a/apps/kimi-code/test/tui/activity-pane.test.ts +++ b/apps/kimi-code/test/tui/activity-pane.test.ts @@ -17,6 +17,7 @@ function makeStartupInput(): KimiTUIStartupInput { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, tuiConfig: { diff --git a/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts b/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts index ad5899a..5654dcd 100644 --- a/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts +++ b/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts @@ -46,6 +46,7 @@ function makeStartupInput(): KimiTUIStartupInput { model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], }, tuiConfig: { diff --git a/apps/kimi-code/test/tui/kimi-tui-startup.test.ts b/apps/kimi-code/test/tui/kimi-tui-startup.test.ts index 9d93ebf..feb4ff7 100644 --- a/apps/kimi-code/test/tui/kimi-tui-startup.test.ts +++ b/apps/kimi-code/test/tui/kimi-tui-startup.test.ts @@ -57,6 +57,7 @@ function makeStartupInput( model: undefined, outputFormat: undefined, prompt: undefined, + promptInteractive: undefined, skillsDirs: [], ...cliOptions, }, @@ -675,4 +676,53 @@ describe("KimiTUI startup", () => { await expect(driver.init()).rejects.toThrow("provider config is invalid"); }); + + it("stores initialCommand from startup input for --prompt-interactive", async () => { + const harness = makeHarness(); + const input = { + ...makeStartupInput(), + initialCommand: "hello from startup", + }; + const driver = new KimiTUI(harness as never, input) as unknown as { + initialCommand: string | undefined; + }; + + expect(driver.initialCommand).toBe("hello from startup"); + }); + + it("has undefined initialCommand when not provided", async () => { + const harness = makeHarness(); + const input = makeStartupInput(); + const driver = new KimiTUI(harness as never, input) as unknown as { + initialCommand: string | undefined; + }; + + expect(driver.initialCommand).toBeUndefined(); + }); + + it("pre-fills editor with initialCommand when session or model is not available", () => { + const harness = makeHarness(); + const input = { ...makeStartupInput(), initialCommand: "hello" }; + const editorSetText = vi.fn(); + const tui = new KimiTUI(harness as never, input) as unknown as { + session: unknown; + state: { + appState: { model: string }; + editor: { setText: (text: string) => void }; + }; + submitInitialCommand(): void; + initialCommand: string | undefined; + }; + + // Simulate OAuth login required: session undefined, model empty + tui.session = undefined; + tui.state.appState.model = ''; + tui.state.editor = { setText: editorSetText }; + + tui.submitInitialCommand(); + + // Command should be placed in editor, and cleared from the field + expect(editorSetText).toHaveBeenCalledWith("hello"); + expect(tui.initialCommand).toBeUndefined(); + }); }); diff --git a/docs/en/reference/kimi-command.md b/docs/en/reference/kimi-command.md index 088e6c3..9d8a8cf 100644 --- a/docs/en/reference/kimi-command.md +++ b/docs/en/reference/kimi-command.md @@ -19,6 +19,7 @@ The table below lists all options supported by the `kimi` main command. All flag | `--continue` | `-C` | Continue the most recent session in the current working directory, without manually specifying an ID. | | `--model ` | `-m` | Use a model alias for this invocation. When omitted, new sessions use `default_model` from the config file, and resumed sessions use the session's current model. | | `--prompt ` | `-p` | Run one prompt non-interactively and stream assistant output to stdout. This mode uses `auto` permission for tool calls and does not open the TUI. | +| `--prompt-interactive ` | `-P` | Send an initial prompt to the agent, then keep the interactive session open for follow-up conversation. Cannot be combined with `--prompt` or bare `--session` without an ID. | | `--output-format ` | | Set the non-interactive output format. Supported values are `text` and `stream-json`. Only valid with `--prompt`; defaults to `text`. | | `--yolo` | `-y` | Auto-approve ordinary tool calls, skipping approval requests; Plan mode `Bash` approval and Plan mode exit approval are not skipped. | | `--plan` | | Start a new session in Plan mode, where the AI favors read-only tools for exploration and planning and can write the current plan file; Plan mode `Bash` is handled separately according to the permission mode. | @@ -37,8 +38,9 @@ The following combinations are rejected at startup: - `--continue` and `--session` are mutually exclusive: both mean "resume a previous session" and overlap in meaning. - `--yolo` cannot be combined with `--continue` or `--session`: when resuming a session, the original session's approval settings are preserved. This rule only applies to interactive mode; in `--prompt` mode, `--yolo` is rejected earlier because it is mutually exclusive with `--prompt`. - `--plan` cannot be combined with `--continue` or `--session`: Plan mode only applies to new sessions. -- `--prompt` cannot be combined with `--yolo` or `--plan`: non-interactive mode always uses `auto` permission and does not enter Plan mode. +- `--prompt` cannot be combined with `--yolo`, `--plan`, or `--prompt-interactive`: non-interactive mode always uses `auto` permission and does not enter Plan mode. - `--prompt` can be combined with `--continue` or `--session ` with an ID; bare `--session` without an ID would open the interactive picker and therefore cannot be used in non-interactive mode. +- `--prompt-interactive` cannot be combined with `--prompt` or bare `--session` without an ID. Can be combined with `--yolo`, `--plan`, `--continue`, or `--session ` with an ID. - `--output-format` can only be used with `--prompt`; the interactive TUI does not support writing the full event stream as stdout JSONL. If you need to force YOLO or Plan mode while resuming a session, switch into them from inside the interactive session via slash commands instead. @@ -111,6 +113,22 @@ kimi -p "List changed files" --output-format stream-json In `stream-json` mode, each stdout line is one JSON object. Ordinary replies are emitted as assistant messages. If the model calls tools, the output first includes an assistant message with `tool_calls`, then the corresponding tool message, followed by later assistant messages. Thinking content is not written to JSONL; tool progress and the resume-session hint still go to stderr. +## Prompt then interact + +Use `-P` to send an initial prompt and stay in the interactive TUI for follow-up conversation: + +```sh +kimi -P "Analyze this project's structure" +``` + +The initial prompt runs immediately after startup, like `-p`, but the shell remains open afterward so you can continue the conversation. You can combine `-P` with `-y` (YOLO mode), `--plan`, `-m `, `-C`, or `-S `. The prompt text supports the same content as a normal message: plain text and file mentions. + +`-P` differs from `-p` in several ways: + +- **Stays interactive**: after the first response, the prompt stays open — unlike `-p` which exits. +- **Runs in the TUI**: all thinking, tool progress, and approvals render in the full TUI interface. +- **Retains session**: you can use slash commands, switch models, or start a new session just like starting `kimi` without arguments. + ## Subcommands ### `kimi export` diff --git a/docs/zh/reference/kimi-command.md b/docs/zh/reference/kimi-command.md index 78365bd..85a3935 100644 --- a/docs/zh/reference/kimi-command.md +++ b/docs/zh/reference/kimi-command.md @@ -19,6 +19,7 @@ kimi [options] | `--continue` | `-C` | 继续当前工作目录下最近一次的会话,无需手动指定 ID。 | | `--model ` | `-m` | 为本次启动指定模型别名。省略时,新会话使用配置文件中的 `default_model`,恢复会话使用会话当前模型。 | | `--prompt ` | `-p` | 非交互执行单次 prompt,并把 Assistant 输出流式写到 stdout。该模式会使用 `auto` 权限处理工具调用,不会打开 TUI。 | +| `--prompt-interactive ` | `-P` | 向 Agent 发送一条初始 prompt,然后保持交互式会话继续对话。不能与 `--prompt` 或不带 ID 的 `--session` 同时使用。 | | `--output-format ` | | 设置非交互输出格式,支持 `text` 与 `stream-json`。仅可与 `--prompt` 一起使用,默认 `text`。 | | `--yolo` | `-y` | 自动批准普通工具调用,跳过审批请求;Plan 模式中的 `Bash` 审批和退出审批不会被跳过。 | | `--plan` | | 以 Plan 模式启动新会话,AI 会优先使用只读工具进行探索和规划,可以写入当前计划文件;Plan 模式中的 `Bash` 按权限模式单独处理。 | @@ -37,8 +38,9 @@ kimi [options] - `--continue` 与 `--session` 互斥:两者都表示"恢复历史会话",含义重叠。 - `--yolo` 不能与 `--continue` 或 `--session` 同时使用:恢复会话时会沿用原会话的审批设置。此规则仅适用于交互式模式;在 `--prompt` 模式下,`--yolo` 已因与 `--prompt` 互斥而被更早拦截。 - `--plan` 不能与 `--continue` 或 `--session` 同时使用:Plan 模式只对新会话生效。 -- `--prompt` 不能与 `--yolo` 或 `--plan` 同时使用:非交互模式固定使用 `auto` 权限,并且不进入 Plan 模式。 +- `--prompt` 不能与 `--yolo`、`--plan` 或 `--prompt-interactive` 同时使用:非交互模式固定使用 `auto` 权限,并且不进入 Plan 模式。 - `--prompt` 可以与 `--continue` 或带 ID 的 `--session ` 一起使用;不带 ID 的 `--session` 会尝试打开选择器,因此不能用于非交互模式。 +- `--prompt-interactive` 不能与 `--prompt` 或不带 ID 的 `--session` 同时使用。可以与 `--yolo`、`--plan`、`--continue` 或带 ID 的 `--session ` 组合使用。 - `--output-format` 只能与 `--prompt` 一起使用;交互式 TUI 不支持把完整事件流写成 stdout JSONL。 如果需要在恢复会话时强制使用 YOLO 或 Plan 模式,请改在交互式会话内通过斜杠命令切换。 @@ -111,6 +113,22 @@ kimi -p "List changed files" --output-format stream-json `stream-json` 模式下,stdout 每行都是一个 JSON 对象。普通回复会输出 Assistant 消息;如果模型调用工具,会先输出带 `tool_calls` 的 Assistant 消息,再输出对应的 Tool 消息,最后继续输出后续 Assistant 消息。thinking 内容不会写入 JSONL;工具进度和恢复会话提示仍然写到 stderr。 +## 发送 prompt 后保持交互 + +需要先发送一条初始 prompt 然后留在交互式 TUI 中继续对话时,使用 `-P`: + +```sh +kimi -P "分析这个项目的结构" +``` + +初始 prompt 在启动后立即执行,类似 `-p`,但 shell 会保持打开状态,方便后续追问。`-P` 可以与 `-y`(YOLO 模式)、`--plan`、`-m `、`-C` 或 `-S ` 组合使用。prompt 文本支持与普通消息相同的内容:纯文本和文件提及。 + +`-P` 与 `-p` 的主要区别: + +- **保持交互**:首次响应后,prompt 保持打开,而 `-p` 会退出。 +- **在 TUI 中运行**:所有 thinking、工具进度和审批都在完整的 TUI 界面中呈现。 +- **保留会话**:可以像不带参数启动 `kimi` 一样使用斜杠命令、切换模型或开始新会话。 + ## 子命令 ### `kimi export`