Files
cline/apps/cli/src/runtime/interactive/chat-command-runner.ts
T
Saoud Rizwan 9958e3f354 feat(cli): allow plugin commands to submit prompts (#11479)
* feat(cli): allow plugin commands to submit prompts

* fix(cli): preserve plugin command output on abort

* revert(cli): drop ineffective clear view tweak
2026-06-11 13:49:50 -07:00

124 lines
3.4 KiB
TypeScript

import type { InteractiveTurnResult } from "../../tui/types";
import type { ChatCommandHost } from "../../utils/chat-commands";
import {
type ChatCommandState,
maybeHandleChatCommand,
} from "../../utils/chat-commands";
import {
enableTeamsForPrompt,
rewriteTeamPrompt,
TEAM_COMMAND_USAGE,
} from "../../utils/team-command";
import type { Config } from "../../utils/types";
import type { createInteractiveSessionRuntime } from "./session-runtime";
type AutoApproveRef = {
current: boolean;
};
export type InteractiveChatCommandRuntime = Pick<
ReturnType<typeof createInteractiveSessionRuntime>,
| "forkCurrentSession"
| "getActiveSessionId"
| "resetForNewSession"
| "restartEmpty"
>;
export type InteractiveChatCommandResult =
| { handled: true; turnResult: InteractiveTurnResult }
| { handled: false; input: string; commandOutput?: string };
function commandTurnResult(commandOutput?: string): InteractiveTurnResult {
return {
usage: { inputTokens: 0, outputTokens: 0 },
iterations: 0,
commandOutput,
};
}
export async function runInteractiveChatCommand(input: {
prompt: string;
enabled: boolean;
config: Config;
host: ChatCommandHost;
chatCommandState: ChatCommandState;
autoApproveAllRef: AutoApproveRef;
setInteractiveAutoApprove: (enabled: boolean) => void;
sessionRuntime: InteractiveChatCommandRuntime;
stop: () => void;
onCommandOutput?: (text: string) => void;
}): Promise<InteractiveChatCommandResult> {
let prompt = input.prompt;
const rewrittenTeamPrompt = rewriteTeamPrompt(prompt);
if (rewrittenTeamPrompt.kind !== "none") {
if (rewrittenTeamPrompt.kind === "usage") {
return {
handled: true,
turnResult: commandTurnResult(TEAM_COMMAND_USAGE),
};
}
if (!input.config.enableAgentTeams) {
await enableTeamsForPrompt(input.config);
await input.sessionRuntime.restartEmpty();
}
prompt = rewrittenTeamPrompt.prompt;
}
let commandOutput: string | undefined;
let submitPrompt: string | undefined;
const handled = await maybeHandleChatCommand(prompt, {
enabled: input.enabled,
host: input.host,
getState: () => ({
...input.chatCommandState,
autoApproveTools: input.autoApproveAllRef.current,
}),
setState: async (next) => {
input.chatCommandState.enableTools = next.enableTools;
input.chatCommandState.autoApproveTools = next.autoApproveTools;
input.chatCommandState.cwd = next.cwd;
input.chatCommandState.workspaceRoot = next.workspaceRoot;
input.setInteractiveAutoApprove(next.autoApproveTools);
},
reply: async (text) => {
commandOutput = text;
input.onCommandOutput?.(text);
},
submitPrompt: async (text) => {
const trimmed = text.trim();
if (trimmed) {
submitPrompt = trimmed;
}
},
reset: async () => {
await input.sessionRuntime.resetForNewSession();
},
stop: async () => {
input.stop();
},
describe: () =>
[
`sessionId=${input.sessionRuntime.getActiveSessionId()}`,
`tools=${input.chatCommandState.enableTools ? "on" : "off"}`,
`yolo=${input.autoApproveAllRef.current ? "on" : "off"}`,
`cwd=${input.chatCommandState.cwd}`,
`workspaceRoot=${input.chatCommandState.workspaceRoot}`,
].join("\n"),
fork: input.sessionRuntime.forkCurrentSession,
});
if (handled) {
if (submitPrompt) {
return {
handled: false,
input: submitPrompt,
...(commandOutput ? { commandOutput } : {}),
};
}
return {
handled: true,
turnResult: commandTurnResult(commandOutput),
};
}
return { handled: false, input: prompt };
}