mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-09 17:29:12 +00:00
fix(cli,tui): allow --auto, --yolo, and --plan with resumed sessions (#683)
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
Some checks are pending
CI / build (push) Waiting to run
CI / test (push) Waiting to run
CI / lint (push) Waiting to run
CI / typecheck (push) Waiting to run
Nix Build / Check flake.nix workspace sync (push) Waiting to run
Nix Build / nix build .#kimi-code (push) Blocked by required conditions
Release / Release (push) Waiting to run
Release / Deploy docs (push) Blocked by required conditions
Release / Native release artifact (push) Blocked by required conditions
Release / Publish native release assets (push) Blocked by required conditions
* fix(cli,tui): allow --auto, --yolo, and --plan with resumed sessions * docs(cli): update flag conflict docs for resumed sessions * fix(tui): apply startup permission/plan overrides after picker selection
This commit is contained in:
parent
dff9fd4e32
commit
ad239cb1c0
9 changed files with 652 additions and 339 deletions
5
.changeset/allow-auto-yolo-plan-with-session-resume.md
Normal file
5
.changeset/allow-auto-yolo-plan-with-session-resume.md
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
---
|
||||
"@moonshot-ai/kimi-code": patch
|
||||
---
|
||||
|
||||
Allow `--auto`, `--yolo`, and `--plan` to be combined with `--session` or `--continue` by applying the requested mode to the resumed session.
|
||||
|
|
@ -55,14 +55,5 @@ export function validateOptions(opts: CLIOptions): ValidatedOptions {
|
|||
if (opts.yolo && opts.auto) {
|
||||
throw new OptionConflictError('Cannot combine --yolo with --auto.');
|
||||
}
|
||||
if (!promptMode && (opts.continue || opts.session !== undefined) && opts.yolo) {
|
||||
throw new OptionConflictError('Cannot combine --yolo with --continue or --session.');
|
||||
}
|
||||
if (!promptMode && (opts.continue || opts.session !== undefined) && opts.auto) {
|
||||
throw new OptionConflictError('Cannot combine --auto with --continue or --session.');
|
||||
}
|
||||
if (!promptMode && (opts.continue || opts.session !== undefined) && opts.plan) {
|
||||
throw new OptionConflictError('Cannot combine --plan with --continue or --session.');
|
||||
}
|
||||
return { options: opts, uiMode: promptMode ? 'print' : 'shell' };
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
getCapabilities,
|
||||
Spacer,
|
||||
} from '@earendil-works/pi-tui';
|
||||
import type { MigrationPlan } from '@moonshot-ai/migration-legacy';
|
||||
import type { DeviceAuthorization } from '@moonshot-ai/kimi-code-oauth';
|
||||
import type {
|
||||
ApprovalRequest,
|
||||
|
|
@ -20,14 +19,17 @@ import type {
|
|||
PromptPart,
|
||||
Session,
|
||||
} from '@moonshot-ai/kimi-code-sdk';
|
||||
import type { MigrationPlan } from '@moonshot-ai/migration-legacy';
|
||||
import { resolve } from 'pathe';
|
||||
|
||||
import type { CLIOptions } from '#/cli/options';
|
||||
import { MigrationScreenComponent, type MigrationScreenResult } from '#/migration/index';
|
||||
import { appendInputHistory, loadInputHistory } from '#/utils/history/input-history';
|
||||
import { openUrl } from '#/utils/open-url';
|
||||
import { getInputHistoryFile } from '#/utils/paths';
|
||||
import { detectFdPath, ensureFdPath } from '#/utils/process/fd-detect';
|
||||
|
||||
import { BannerProvider } from './banner/banner-provider';
|
||||
import {
|
||||
BUILTIN_SLASH_COMMANDS,
|
||||
buildSkillSlashCommands,
|
||||
|
|
@ -37,9 +39,10 @@ import {
|
|||
type KimiSlashCommand,
|
||||
type SkillListSession,
|
||||
} from './commands';
|
||||
import * as slashCommands from './commands/dispatch';
|
||||
import { BannerComponent } from './components/chrome/banner';
|
||||
import { DeviceCodeBoxComponent } from './components/chrome/device-code-box';
|
||||
import { GutterContainer } from './components/chrome/gutter-container';
|
||||
import { CHROME_GUTTER } from './constant/rendering';
|
||||
import { MoonLoader, type SpinnerStyle } from './components/chrome/moon-loader';
|
||||
import { WelcomeComponent } from './components/chrome/welcome';
|
||||
import {
|
||||
|
|
@ -54,15 +57,6 @@ import { CompactionComponent } from './components/dialogs/compaction';
|
|||
import { HelpPanelComponent } from './components/dialogs/help-panel';
|
||||
import { QuestionDialogComponent } from './components/dialogs/question-dialog';
|
||||
import { SessionPickerComponent } from './components/dialogs/session-picker';
|
||||
import { AuthFlowController } from './controllers/auth-flow';
|
||||
import { BtwPanelController } from './controllers/btw-panel';
|
||||
import { EditorKeyboardController } from './controllers/editor-keyboard';
|
||||
import { SessionEventHandler } from './controllers/session-event-handler';
|
||||
import * as slashCommands from './commands/dispatch';
|
||||
import { SessionReplayRenderer } from './controllers/session-replay';
|
||||
import { StreamingUIController } from './controllers/streaming-ui';
|
||||
import { TasksBrowserController } from './controllers/tasks-browser';
|
||||
import { installRainbowDance } from './easter-eggs/dance';
|
||||
import {
|
||||
FileMentionProvider,
|
||||
type SlashAutocompleteCommand,
|
||||
|
|
@ -92,19 +86,26 @@ import {
|
|||
NO_ACTIVE_SESSION_MESSAGE,
|
||||
PRODUCT_NAME,
|
||||
} from './constant/kimi-tui';
|
||||
import { CHROME_GUTTER } from './constant/rendering';
|
||||
import { MAX_TERMINAL_TITLE_LENGTH } from './constant/terminal';
|
||||
import { combineStartupNotice, isOAuthLoginRequiredError } from './utils/startup';
|
||||
import { AuthFlowController } from './controllers/auth-flow';
|
||||
import { BtwPanelController } from './controllers/btw-panel';
|
||||
import { EditorKeyboardController } from './controllers/editor-keyboard';
|
||||
import { SessionEventHandler } from './controllers/session-event-handler';
|
||||
import { SessionReplayRenderer } from './controllers/session-replay';
|
||||
import { StreamingUIController } from './controllers/streaming-ui';
|
||||
import { TasksBrowserController } from './controllers/tasks-browser';
|
||||
import { installRainbowDance } from './easter-eggs/dance';
|
||||
import { adaptPanelResponse } from './reverse-rpc/approval/adapter';
|
||||
import { ApprovalController } from './reverse-rpc/approval/controller';
|
||||
import { createApprovalRequestHandler } from './reverse-rpc/approval/handler';
|
||||
import { BannerProvider } from './banner/banner-provider';
|
||||
import { BannerComponent } from './components/chrome/banner';
|
||||
import { registerReverseRPCHandlers } from './reverse-rpc/index';
|
||||
import { QuestionController } from './reverse-rpc/question/controller';
|
||||
import { createQuestionAskHandler } from './reverse-rpc/question/handler';
|
||||
import type { ApprovalPanelData, QuestionPanelData } from './reverse-rpc/types';
|
||||
import { currentTheme, getColorPalette, getBuiltInPalette, isBuiltInTheme } from './theme';
|
||||
import type { ColorToken, ResolvedTheme, ThemeName } from './theme';
|
||||
import { createTUIState, type TUIState } from './tui-state';
|
||||
import {
|
||||
INITIAL_LIVE_PANE,
|
||||
type AppState,
|
||||
|
|
@ -116,15 +117,14 @@ import {
|
|||
type TUIStartupOptions,
|
||||
type TUIStartupState,
|
||||
} from './types';
|
||||
import { createTUIState, type TUIState } from './tui-state';
|
||||
import { isExpandable } from './utils/component-capabilities';
|
||||
import { isDeadTerminalError } from './utils/dead-terminal';
|
||||
import { formatErrorMessage } from './utils/event-payload';
|
||||
import { ImageAttachmentStore, type ImageAttachment } from './utils/image-attachment-store';
|
||||
import { extractMediaAttachments } from './utils/image-placeholder';
|
||||
import { hasPatchChanges } from './utils/object-patch';
|
||||
import { openUrl } from '#/utils/open-url';
|
||||
import { sessionRowsForPicker } from './utils/session-picker-rows';
|
||||
import { combineStartupNotice, isOAuthLoginRequiredError } from './utils/startup';
|
||||
import { installTerminalFocusTracking } from './utils/terminal-focus';
|
||||
import { notifyTerminalOnce } from './utils/terminal-notification';
|
||||
import { installTerminalThemeTracking } from './utils/terminal-theme';
|
||||
|
|
@ -246,10 +246,7 @@ export class KimiTUI {
|
|||
|
||||
public onExit?: (exitCode?: number) => Promise<void>;
|
||||
|
||||
track(
|
||||
event: string,
|
||||
properties?: Parameters<KimiHarness['track']>[1],
|
||||
): void {
|
||||
track(event: string, properties?: Parameters<KimiHarness['track']>[1]): void {
|
||||
this.harness.track(event, properties);
|
||||
}
|
||||
|
||||
|
|
@ -377,8 +374,7 @@ export class KimiTUI {
|
|||
try {
|
||||
const migrationResult = await this.runMigrationScreen(this.migrationPlan);
|
||||
if (this.migrateOnly) {
|
||||
const failed =
|
||||
migrationResult.decision === 'now' && migrationResult.migrated === false;
|
||||
const failed = migrationResult.decision === 'now' && migrationResult.migrated === false;
|
||||
this.disposeTerminalTracking();
|
||||
this.state.ui.stop();
|
||||
await this.onExit?.(failed ? 1 : 0);
|
||||
|
|
@ -424,11 +420,7 @@ export class KimiTUI {
|
|||
if (this.state.appState.banner === null || this.state.appState.banner === undefined) {
|
||||
return;
|
||||
}
|
||||
if (
|
||||
this.state.transcriptContainer.children.some(
|
||||
(child) => child instanceof BannerComponent,
|
||||
)
|
||||
) {
|
||||
if (this.state.transcriptContainer.children.some((child) => child instanceof BannerComponent)) {
|
||||
return;
|
||||
}
|
||||
const welcomeIndex = this.state.transcriptContainer.children.findIndex(
|
||||
|
|
@ -489,10 +481,7 @@ export class KimiTUI {
|
|||
this.showStatus(parts.join(' · ') + '.');
|
||||
}
|
||||
for (const f of result.failed) {
|
||||
this.showStatus(
|
||||
`Skipped refreshing ${f.provider}: ${f.reason}`,
|
||||
'warning',
|
||||
);
|
||||
this.showStatus(`Skipped refreshing ${f.provider}: ${f.reason}`, 'warning');
|
||||
}
|
||||
} catch {
|
||||
// Best-effort: startup must not crash on background refresh failures.
|
||||
|
|
@ -511,6 +500,7 @@ export class KimiTUI {
|
|||
}
|
||||
if (shouldReplayHistory) {
|
||||
await this.sessionReplay.hydrateFromReplay(this.requireSession());
|
||||
this.applyStartupPermissionAndPlanToAppState();
|
||||
}
|
||||
const resumeState = this.session?.getResumeState();
|
||||
if (resumeState?.warning !== undefined) {
|
||||
|
|
@ -568,7 +558,8 @@ export class KimiTUI {
|
|||
if (resolve(target.workDir) !== resolve(workDir)) {
|
||||
this.state.ui.stop();
|
||||
process.stderr.write(
|
||||
`${currentTheme.fg('warning',
|
||||
`${currentTheme.fg(
|
||||
'warning',
|
||||
`Session "${startup.sessionFlag}" was created under a different directory.\n` +
|
||||
` cd "${target.workDir}" && kimi -r ${startup.sessionFlag}`,
|
||||
)}\n\n`,
|
||||
|
|
@ -596,8 +587,18 @@ export class KimiTUI {
|
|||
} else {
|
||||
session = await this.harness.createSession(createSessionOptions);
|
||||
}
|
||||
if (session !== undefined && startup.model !== undefined && isResumeStartup) {
|
||||
await session.setModel(startup.model);
|
||||
if (session !== undefined && shouldReplayHistory) {
|
||||
if (startup.auto) {
|
||||
await session.setPermission('auto');
|
||||
} else if (startup.yolo) {
|
||||
await session.setPermission('yolo');
|
||||
}
|
||||
if (startup.plan) {
|
||||
await session.setPlanMode(true);
|
||||
}
|
||||
if (startup.model !== undefined) {
|
||||
await session.setModel(startup.model);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
if (!isOAuthLoginRequiredError(error)) throw error;
|
||||
|
|
@ -610,6 +611,7 @@ export class KimiTUI {
|
|||
}
|
||||
await this.setSession(session);
|
||||
await this.syncRuntimeState(session);
|
||||
this.applyStartupPermissionAndPlanToAppState();
|
||||
this.state.startupState = 'ready';
|
||||
return shouldReplayHistory;
|
||||
}
|
||||
|
|
@ -1079,10 +1081,7 @@ export class KimiTUI {
|
|||
}
|
||||
|
||||
async syncRuntimeState(session: Session = this.requireSession()): Promise<void> {
|
||||
const [status, goalResult] = await Promise.all([
|
||||
session.getStatus(),
|
||||
session.getGoal(),
|
||||
]);
|
||||
const [status, goalResult] = await Promise.all([session.getStatus(), session.getGoal()]);
|
||||
this.setAppState({
|
||||
sessionId: session.id,
|
||||
model: status.model ?? '',
|
||||
|
|
@ -1098,6 +1097,21 @@ export class KimiTUI {
|
|||
});
|
||||
}
|
||||
|
||||
// Re-apply startup flags that the user explicitly passed on the command line.
|
||||
// syncRuntimeState and session-replay hydration can both read stale persisted
|
||||
// values, so this guarantees the footer reflects the CLI intent.
|
||||
private applyStartupPermissionAndPlanToAppState(): void {
|
||||
const { startup } = this.options;
|
||||
if (startup.auto) {
|
||||
this.setAppState({ permissionMode: 'auto' });
|
||||
} else if (startup.yolo) {
|
||||
this.setAppState({ permissionMode: 'yolo' });
|
||||
}
|
||||
if (startup.plan) {
|
||||
this.setAppState({ planMode: true });
|
||||
}
|
||||
}
|
||||
|
||||
// Plan mode is set by createSession — do not re-enter it here.
|
||||
private async activateRuntime(): Promise<void> {
|
||||
const session = this.requireSession();
|
||||
|
|
@ -1336,10 +1350,7 @@ export class KimiTUI {
|
|||
return new GoalSetMessageComponent();
|
||||
}
|
||||
if (entry.goalData?.kind === 'lifecycle') {
|
||||
return buildGoalMarker(
|
||||
entry.goalData.change,
|
||||
this.state.toolOutputExpanded,
|
||||
);
|
||||
return buildGoalMarker(entry.goalData.change, this.state.toolOutputExpanded);
|
||||
}
|
||||
return null;
|
||||
case 'assistant': {
|
||||
|
|
@ -1396,7 +1407,10 @@ export class KimiTUI {
|
|||
}
|
||||
}
|
||||
|
||||
private appendApprovalTranscriptEntry(request: ApprovalRequest, response: ApprovalResponse): void {
|
||||
private appendApprovalTranscriptEntry(
|
||||
request: ApprovalRequest,
|
||||
response: ApprovalResponse,
|
||||
): void {
|
||||
if (request.toolName === 'ExitPlanMode' || request.display.kind === 'plan_review') return;
|
||||
const parts: string[] = [];
|
||||
switch (response.decision) {
|
||||
|
|
@ -1425,9 +1439,7 @@ export class KimiTUI {
|
|||
|
||||
private renderWelcome(): void {
|
||||
if (
|
||||
this.state.transcriptContainer.children.some(
|
||||
(child) => child instanceof WelcomeComponent,
|
||||
)
|
||||
this.state.transcriptContainer.children.some((child) => child instanceof WelcomeComponent)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
|
@ -1457,16 +1469,12 @@ export class KimiTUI {
|
|||
}
|
||||
|
||||
showStatus(message: string, color?: ColorToken): void {
|
||||
this.state.transcriptContainer.addChild(
|
||||
new StatusMessageComponent(message, color),
|
||||
);
|
||||
this.state.transcriptContainer.addChild(new StatusMessageComponent(message, color));
|
||||
this.state.ui.requestRender();
|
||||
}
|
||||
|
||||
showNotice(title: string, detail?: string): void {
|
||||
this.state.transcriptContainer.addChild(
|
||||
new NoticeMessageComponent(title, detail),
|
||||
);
|
||||
this.state.transcriptContainer.addChild(new NoticeMessageComponent(title, detail));
|
||||
this.state.ui.requestRender();
|
||||
}
|
||||
|
||||
|
|
@ -1641,9 +1649,7 @@ export class KimiTUI {
|
|||
}
|
||||
|
||||
async applyTheme(themeName: ThemeName, resolved?: ResolvedTheme): Promise<void> {
|
||||
const palette = await getColorPalette(
|
||||
themeName === 'auto' ? (resolved ?? 'dark') : themeName,
|
||||
);
|
||||
const palette = await getColorPalette(themeName === 'auto' ? (resolved ?? 'dark') : themeName);
|
||||
currentTheme.setPalette(palette);
|
||||
this.setAppState({ theme: themeName });
|
||||
this.updateEditorBorderHighlight();
|
||||
|
|
@ -1689,7 +1695,9 @@ export class KimiTUI {
|
|||
);
|
||||
}
|
||||
|
||||
private shouldPlaceActivitySpinnerInAgentSwarm(effectiveMode: EffectiveActivityPaneMode): boolean {
|
||||
private shouldPlaceActivitySpinnerInAgentSwarm(
|
||||
effectiveMode: EffectiveActivityPaneMode,
|
||||
): boolean {
|
||||
return (
|
||||
this.sessionEventHandler.hasActiveAgentSwarmToolCall() &&
|
||||
(effectiveMode === 'waiting' || effectiveMode === 'tool')
|
||||
|
|
@ -1781,11 +1789,7 @@ export class KimiTUI {
|
|||
// Persist the skip marker `detectPendingMigration` checks, so "Never ask
|
||||
// again" actually stops the prompt from reappearing every launch.
|
||||
try {
|
||||
writeFileSync(
|
||||
join(this.harness.homeDir, '.skip-migration-from-kimi-cli'),
|
||||
'',
|
||||
'utf-8',
|
||||
);
|
||||
writeFileSync(join(this.harness.homeDir, '.skip-migration-from-kimi-cli'), '', 'utf-8');
|
||||
} catch {
|
||||
// Non-blocking: a failed marker write must never crash startup.
|
||||
}
|
||||
|
|
@ -1852,10 +1856,21 @@ export class KimiTUI {
|
|||
loading: this.state.loadingSessions,
|
||||
currentSessionId: this.state.appState.sessionId,
|
||||
onSelect: (sessionId: string) => {
|
||||
void this.resumeSession(sessionId).then((switched) => {
|
||||
if (switched) {
|
||||
this.hideSessionPicker();
|
||||
void this.resumeSession(sessionId).then(async (switched) => {
|
||||
if (!switched) {
|
||||
return;
|
||||
}
|
||||
const session = this.requireSession();
|
||||
if (this.options.startup.auto) {
|
||||
await session.setPermission('auto');
|
||||
} else if (this.options.startup.yolo) {
|
||||
await session.setPermission('yolo');
|
||||
}
|
||||
if (this.options.startup.plan) {
|
||||
await session.setPlanMode(true);
|
||||
}
|
||||
this.applyStartupPermissionAndPlanToAppState();
|
||||
this.hideSessionPicker();
|
||||
});
|
||||
},
|
||||
onCancel,
|
||||
|
|
@ -1955,5 +1970,4 @@ export class KimiTUI {
|
|||
this.patchLivePane({ pendingQuestion: null });
|
||||
this.restoreEditor();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -47,7 +47,11 @@ describe('CLI options parsing', () => {
|
|||
describe('--version', () => {
|
||||
it('prints the version string and exits', () => {
|
||||
let output = '';
|
||||
const program = createProgram('1.2.3', () => {}, () => {});
|
||||
const program = createProgram(
|
||||
'1.2.3',
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
program.exitOverride();
|
||||
program.configureOutput({
|
||||
writeOut: (s) => {
|
||||
|
|
@ -61,7 +65,11 @@ describe('CLI options parsing', () => {
|
|||
|
||||
it('supports -V as a short alias', () => {
|
||||
let output = '';
|
||||
const program = createProgram('4.5.6', () => {}, () => {});
|
||||
const program = createProgram(
|
||||
'4.5.6',
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
program.exitOverride();
|
||||
program.configureOutput({
|
||||
writeOut: (s) => {
|
||||
|
|
@ -103,9 +111,7 @@ describe('CLI options parsing', () => {
|
|||
'--flag',
|
||||
]);
|
||||
|
||||
expect(pluginRunnerCalls).toEqual([
|
||||
{ entry: '/plugin/tool.mjs', args: ['query', '--flag'] },
|
||||
]);
|
||||
expect(pluginRunnerCalls).toEqual([{ entry: '/plugin/tool.mjs', args: ['query', '--flag'] }]);
|
||||
});
|
||||
});
|
||||
|
||||
|
|
@ -161,6 +167,50 @@ describe('CLI options parsing', () => {
|
|||
});
|
||||
});
|
||||
|
||||
describe('--auto / --yolo / --plan with --session / --continue', () => {
|
||||
it('allows --auto with --continue', () => {
|
||||
const opts = parse(['--auto', '--continue']);
|
||||
expect(opts.auto).toBe(true);
|
||||
expect(opts.continue).toBe(true);
|
||||
expect(validateOptions(opts).uiMode).toBe('shell');
|
||||
});
|
||||
|
||||
it('allows --auto with an explicit session id', () => {
|
||||
const opts = parse(['--auto', '--session', 'ses_123']);
|
||||
expect(opts.auto).toBe(true);
|
||||
expect(opts.session).toBe('ses_123');
|
||||
expect(validateOptions(opts).uiMode).toBe('shell');
|
||||
});
|
||||
|
||||
it('allows --yolo with --continue', () => {
|
||||
const opts = parse(['--yolo', '--continue']);
|
||||
expect(opts.yolo).toBe(true);
|
||||
expect(opts.continue).toBe(true);
|
||||
expect(validateOptions(opts).uiMode).toBe('shell');
|
||||
});
|
||||
|
||||
it('allows --yolo with an explicit session id', () => {
|
||||
const opts = parse(['--yolo', '--session', 'ses_123']);
|
||||
expect(opts.yolo).toBe(true);
|
||||
expect(opts.session).toBe('ses_123');
|
||||
expect(validateOptions(opts).uiMode).toBe('shell');
|
||||
});
|
||||
|
||||
it('allows --plan with --continue', () => {
|
||||
const opts = parse(['--plan', '--continue']);
|
||||
expect(opts.plan).toBe(true);
|
||||
expect(opts.continue).toBe(true);
|
||||
expect(validateOptions(opts).uiMode).toBe('shell');
|
||||
});
|
||||
|
||||
it('allows --plan with an explicit session id', () => {
|
||||
const opts = parse(['--plan', '--session', 'ses_123']);
|
||||
expect(opts.plan).toBe(true);
|
||||
expect(opts.session).toBe('ses_123');
|
||||
expect(validateOptions(opts).uiMode).toBe('shell');
|
||||
});
|
||||
});
|
||||
|
||||
describe('--model / -m', () => {
|
||||
it('parses -m as a model override', () => {
|
||||
expect(parse(['-m', 'kimi-code/k2']).model).toBe('kimi-code/k2');
|
||||
|
|
@ -211,7 +261,9 @@ describe('CLI options parsing', () => {
|
|||
it('rejects prompt mode with bare --session picker', () => {
|
||||
const opts = parse(['-p', 'resume here', '--session']);
|
||||
expect(() => validateOptions(opts)).toThrow(OptionConflictError);
|
||||
expect(() => validateOptions(opts)).toThrow('Cannot use --session without an id in prompt mode.');
|
||||
expect(() => validateOptions(opts)).toThrow(
|
||||
'Cannot use --session without an id in prompt mode.',
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects prompt mode with --yolo because prompt mode always uses auto permission', () => {
|
||||
|
|
@ -281,7 +333,11 @@ describe('CLI options parsing', () => {
|
|||
});
|
||||
|
||||
it('registers the visible sub-commands', () => {
|
||||
const program = createProgram('0.0.0', () => {}, () => {});
|
||||
const program = createProgram(
|
||||
'0.0.0',
|
||||
() => {},
|
||||
() => {},
|
||||
);
|
||||
const commandNames: string[] = program.commands
|
||||
.filter((command) => !command.name().startsWith('__'))
|
||||
.map((command) => command.name());
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -51,7 +51,7 @@ kimi --session
|
|||
```
|
||||
|
||||
::: warning
|
||||
`--continue` and `--session` are mutually exclusive. `--yolo` and `--plan` cannot be combined with them either.
|
||||
`--continue` and `--session` are mutually exclusive.
|
||||
:::
|
||||
|
||||
## Switching sessions inside the TUI
|
||||
|
|
|
|||
|
|
@ -37,12 +37,10 @@ The following combinations are rejected at startup:
|
|||
|
||||
- `--continue` and `--session` are mutually exclusive — both mean "resume a previous session"
|
||||
- `--yolo` and `--auto` are mutually exclusive — the two permission modes cannot be combined
|
||||
- `--yolo` and `--auto` cannot be used together with `--continue` or `--session` — resumed sessions inherit the approval settings of the original session
|
||||
- `--plan` cannot be used with `--continue` or `--session` — Plan mode only takes effect for new sessions
|
||||
- `--prompt` cannot be used with `--yolo`, `--auto`, or `--plan` — non-interactive mode uses `auto` permission by default
|
||||
- `--output-format` can only be used together with `--prompt`
|
||||
|
||||
To force YOLO or Plan mode when resuming a session, switch via slash commands inside the interactive session instead.
|
||||
When resuming a session, you can override its saved permission or plan mode by adding `--auto`, `--yolo`, or `--plan`. For example, `kimi --continue --auto` resumes the latest session and switches it to auto permission mode.
|
||||
|
||||
## Common Usage
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ kimi --session
|
|||
```
|
||||
|
||||
::: warning 注意
|
||||
`--continue` 与 `--session` 互斥;`--yolo` 和 `--plan` 也不能与它们同时使用。
|
||||
`--continue` 与 `--session` 互斥。
|
||||
:::
|
||||
|
||||
## 在 TUI 中切换会话
|
||||
|
|
|
|||
|
|
@ -37,12 +37,10 @@ kimi <subcommand> [options]
|
|||
|
||||
- `--continue` 与 `--session` 互斥——两者都表示"恢复历史会话"
|
||||
- `--yolo` 和 `--auto` 互斥——两种权限模式互斥
|
||||
- `--yolo` 与 `--auto` 不能与 `--continue` 或 `--session` 同时使用——恢复会话时沿用原会话的审批设置
|
||||
- `--plan` 不能与 `--continue` 或 `--session` 同时使用——Plan 模式只对新会话生效
|
||||
- `--prompt` 不能与 `--yolo`、`--auto` 或 `--plan` 同时使用——非交互模式固定使用 `auto` 权限
|
||||
- `--output-format` 只能与 `--prompt` 一起使用
|
||||
|
||||
如需在恢复会话时强制使用 YOLO 或 Plan 模式,请改在交互式会话内通过斜杠命令切换。
|
||||
恢复会话时,可以通过 `--auto`、`--yolo` 或 `--plan` 覆盖原会话保存的权限或计划模式。例如,`kimi --continue --auto` 会恢复最近会话并切换到 auto 权限模式。
|
||||
|
||||
## 典型用法
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue