diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 6e26ad8ea9..f54a7149cf 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -20,7 +20,7 @@ import { parseSourceCodeForDefinitionsTopLevel } from "../services/tree-sitter" import { ApiConfiguration } from "../shared/api" import { findLastIndex } from "../shared/array" import { combineApiRequests } from "../shared/combineApiRequests" -import { combineCommandSequences } from "../shared/combineCommandSequences" +import { combineCommandSequences, COMMAND_REQ_APP_STRING } from "../shared/combineCommandSequences" import { BrowserAction, BrowserActionResult, @@ -1538,6 +1538,9 @@ export class Cline { } case "execute_command": { const command: string | undefined = block.params.command + const requiresApprovalRaw: string | undefined = block.params.requires_approval + const requiresApproval = requiresApprovalRaw?.toLowerCase() === "true" + try { if (block.partial) { await this.ask("command", removeClosingTag("command", command), block.partial).catch( @@ -1552,8 +1555,21 @@ export class Cline { ) break } + if (!requiresApprovalRaw) { + this.consecutiveMistakeCount++ + pushToolResult( + await this.sayAndCreateMissingParamError( + "execute_command", + "requires_approval", + ), + ) + break + } this.consecutiveMistakeCount = 0 - const didApprove = await askApproval("command", command) + const didApprove = await askApproval( + "command", + command + `${requiresApproval ? COMMAND_REQ_APP_STRING : ""}`, // ugly hack until we refactor combineCommandSequences + ) if (!didApprove) { break } diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index dafb9fcc5a..c18eba3f61 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -27,6 +27,7 @@ export type ToolUseName = (typeof toolUseNames)[number] export const toolParamNames = [ "command", + "requires_approval", "path", "diff", "regex", @@ -57,7 +58,7 @@ export interface ToolUse { export interface ExecuteCommandToolUse extends ToolUse { name: "execute_command" // Pick, "command"> makes "command" required, but Partial<> makes it optional - params: Partial, "command">> + params: Partial, "command" | "requires_approval">> } export interface ReadFileToolUse extends ToolUse { diff --git a/src/core/prompts/system.ts b/src/core/prompts/system.ts index 20ac75a9fe..5435572177 100644 --- a/src/core/prompts/system.ts +++ b/src/core/prompts/system.ts @@ -39,9 +39,11 @@ Always adhere to this format for the tool use to ensure proper parsing and execu Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Commands will be executed in the current working directory: ${cwd.toPosix()} Parameters: - command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions. +- requires_approval: (required) A boolean indicating whether this command requires explicit user approval before execution in case the user has auto-approve mode enabled. Set to 'true' for potentially impactful operations like installing/uninstalling packages, deleting/overwriting files, system configuration changes, network operations, or any commands that could have unintended side effects. Set to 'false' for safe operations like reading files/directories, running development servers, building projects, and other non-destructive operations. Usage: Your command here +true or false ## read_file @@ -219,6 +221,7 @@ Your final result description here npm run dev +false ## Example 2: Requesting to use an MCP tool diff --git a/src/shared/combineCommandSequences.ts b/src/shared/combineCommandSequences.ts index 31fe219f04..d864dec6ff 100644 --- a/src/shared/combineCommandSequences.ts +++ b/src/shared/combineCommandSequences.ts @@ -71,3 +71,4 @@ export function combineCommandSequences(messages: ClineMessage[]): ClineMessage[ }) } export const COMMAND_OUTPUT_STRING = "Output:" +export const COMMAND_REQ_APP_STRING = "REQ_APP" diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 91e51a0020..8c715725d7 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -8,7 +8,7 @@ import { ClineMessage, ClineSayTool, } from "../../../../src/shared/ExtensionMessage" -import { COMMAND_OUTPUT_STRING } from "../../../../src/shared/combineCommandSequences" +import { COMMAND_OUTPUT_STRING, COMMAND_REQ_APP_STRING } from "../../../../src/shared/combineCommandSequences" import { useExtensionState } from "../../context/ExtensionStateContext" import { findMatchingResourceOrTemplate } from "../../utils/mcp" import { vscode } from "../../utils/vscode" @@ -732,7 +732,11 @@ export const ChatRowContent = ({ } } - const { command, output } = splitMessage(message.text || "") + const { command: rawCommand, output } = splitMessage(message.text || "") + + const requestsApproval = rawCommand.endsWith(COMMAND_REQ_APP_STRING) + const command = requestsApproval ? rawCommand.slice(0, -COMMAND_REQ_APP_STRING.length) : rawCommand + return ( <>
@@ -772,6 +776,20 @@ export const ChatRowContent = ({
)} + {requestsApproval && ( +
+ + The model has determined this command requires explicit approval +
+ )} ) case "use_mcp_server":