From 13de490c3b3c786ac3b1b404f1e948791c2e7393 Mon Sep 17 00:00:00 2001 From: Matt Rubens Date: Wed, 29 Jan 2025 01:28:32 -0500 Subject: [PATCH] Add a new_task tool --- src/core/Cline.ts | 78 ++++++- src/core/assistant-message/index.ts | 8 + .../__snapshots__/system.test.ts.snap | 200 ++++++++++++++++++ src/core/prompts/tools/index.ts | 2 + src/core/prompts/tools/new-task.ts | 23 ++ src/core/webview/ClineProvider.ts | 2 +- src/shared/ExtensionMessage.ts | 3 + src/shared/tool-groups.ts | 9 +- .../src/components/chat/AutoApproveMenu.tsx | 5 +- webview-ui/src/components/chat/ChatRow.tsx | 14 ++ webview-ui/src/components/chat/ChatView.tsx | 3 +- .../src/components/settings/SettingsView.tsx | 5 +- 12 files changed, 343 insertions(+), 9 deletions(-) create mode 100644 src/core/prompts/tools/new-task.ts diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 3037043b1f5..e2911976d36 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -631,7 +631,7 @@ export class Cline { let newUserContent: UserContent = [...modifiedOldUserContent] - const agoText = (() => { + const agoText = ((): string => { const timestamp = lastClineMessage?.ts ?? Date.now() const now = Date.now() const diff = now - timestamp @@ -996,7 +996,7 @@ export class Cline { break } case "tool_use": - const toolDescription = () => { + const toolDescription = (): string => { switch (block.name) { case "execute_command": return `[${block.name} for '${block.params.command}']` @@ -1030,6 +1030,12 @@ export class Cline { return `[${block.name}]` case "switch_mode": return `[${block.name} to '${block.params.mode_slug}'${block.params.reason ? ` because: ${block.params.reason}` : ""}]` + case "new_task": { + const mode = block.params.mode ?? defaultModeSlug + const message = block.params.message ?? "(no message)" + const modeName = getModeBySlug(mode, customModes)?.name ?? mode + return `[${block.name} in ${modeName} mode: '${message}']` + } } } @@ -2402,6 +2408,74 @@ export class Cline { } } + case "new_task": { + const mode: string | undefined = block.params.mode + const message: string | undefined = block.params.message + try { + if (block.partial) { + const partialMessage = JSON.stringify({ + tool: "newTask", + mode: removeClosingTag("mode", mode), + message: removeClosingTag("message", message), + }) + await this.ask("tool", partialMessage, block.partial).catch(() => {}) + break + } else { + if (!mode) { + this.consecutiveMistakeCount++ + pushToolResult(await this.sayAndCreateMissingParamError("new_task", "mode")) + break + } + if (!message) { + this.consecutiveMistakeCount++ + pushToolResult(await this.sayAndCreateMissingParamError("new_task", "message")) + break + } + this.consecutiveMistakeCount = 0 + + // Verify the mode exists + const targetMode = getModeBySlug( + mode, + (await this.providerRef.deref()?.getState())?.customModes, + ) + if (!targetMode) { + pushToolResult(formatResponse.toolError(`Invalid mode: ${mode}`)) + break + } + + // Show what we're about to do + const toolMessage = JSON.stringify({ + tool: "newTask", + mode: targetMode.name, + content: message, + }) + + const didApprove = await askApproval("tool", toolMessage) + if (!didApprove) { + break + } + + // Switch mode first, then create new task instance + const provider = this.providerRef.deref() + if (provider) { + await provider.handleModeSwitch(mode) + await provider.initClineWithTask(message) + pushToolResult( + `Successfully created new task in ${targetMode.name} mode with message: ${message}`, + ) + } else { + pushToolResult( + formatResponse.toolError("Failed to create new task: provider not available"), + ) + } + break + } + } catch (error) { + await handleError("creating new task", error) + break + } + } + case "attempt_completion": { /* this.consecutiveMistakeCount = 0 diff --git a/src/core/assistant-message/index.ts b/src/core/assistant-message/index.ts index c2a2d6290e2..f1c49f85ab7 100644 --- a/src/core/assistant-message/index.ts +++ b/src/core/assistant-message/index.ts @@ -24,6 +24,7 @@ export const toolUseNames = [ "ask_followup_question", "attempt_completion", "switch_mode", + "new_task", ] as const // Converts array of tool call names into a union type ("execute_command" | "read_file" | ...) @@ -53,6 +54,8 @@ export const toolParamNames = [ "mode_slug", "reason", "operations", + "mode", + "message", ] as const export type ToolParamName = (typeof toolParamNames)[number] @@ -130,3 +133,8 @@ export interface SwitchModeToolUse extends ToolUse { name: "switch_mode" params: Partial, "mode_slug" | "reason">> } + +export interface NewTaskToolUse extends ToolUse { + name: "new_task" + params: Partial, "mode" | "message">> +} diff --git a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap index 926a03209c6..24242cad706 100644 --- a/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap +++ b/src/core/prompts/__tests__/__snapshots__/system.test.ts.snap @@ -198,6 +198,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -501,6 +521,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -804,6 +844,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -1153,6 +1213,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -1508,6 +1588,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -2221,6 +2321,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -2587,6 +2707,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -2892,6 +3032,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -3238,6 +3398,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. @@ -3527,6 +3707,26 @@ Example: Requesting to switch to code mode Need to make code changes +## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + + + # Tool Use Guidelines 1. In tags, assess what information you already have and what information you need to proceed with the task. diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 8ad785c4d2b..c4bdfc98c3f 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -12,6 +12,7 @@ import { getAttemptCompletionDescription } from "./attempt-completion" import { getUseMcpToolDescription } from "./use-mcp-tool" import { getAccessMcpResourceDescription } from "./access-mcp-resource" import { getSwitchModeDescription } from "./switch-mode" +import { getNewTaskDescription } from "./new-task" import { DiffStrategy } from "../../diff/DiffStrategy" import { McpHub } from "../../../services/mcp/McpHub" import { Mode, ModeConfig, getModeConfig, isToolAllowedForMode, getGroupName } from "../../../shared/modes" @@ -32,6 +33,7 @@ const toolDescriptionMap: Record string | undefined> use_mcp_tool: (args) => getUseMcpToolDescription(args), access_mcp_resource: (args) => getAccessMcpResourceDescription(args), switch_mode: () => getSwitchModeDescription(), + new_task: (args) => getNewTaskDescription(args), insert_content: (args) => getInsertContentDescription(args), search_and_replace: (args) => getSearchAndReplaceDescription(args), apply_diff: (args) => diff --git a/src/core/prompts/tools/new-task.ts b/src/core/prompts/tools/new-task.ts new file mode 100644 index 00000000000..3de2e6a5373 --- /dev/null +++ b/src/core/prompts/tools/new-task.ts @@ -0,0 +1,23 @@ +import { ToolArgs } from "./types" + +export function getNewTaskDescription(args: ToolArgs): string { + return `## new_task +Description: Create a new task with a specified starting mode and initial message. This tool instructs the system to create a new Cline instance in the given mode with the provided message. + +Parameters: +- mode: (required) The slug of the mode to start the new task in (e.g., "code", "ask", "architect"). +- message: (required) The initial user message or instructions for this new task. + +Usage: + +your-mode-slug-here +Your initial instructions here + + +Example: + +code +Implement a new feature for the application. + +` +} diff --git a/src/core/webview/ClineProvider.ts b/src/core/webview/ClineProvider.ts index ba6a06ea33d..58ec513529e 100644 --- a/src/core/webview/ClineProvider.ts +++ b/src/core/webview/ClineProvider.ts @@ -2180,7 +2180,7 @@ export class ClineProvider implements vscode.WebviewViewProvider { this.getGlobalState("autoApprovalEnabled") as Promise, this.customModesManager.getCustomModes(), this.getGlobalState("experiments") as Promise | undefined>, - this.getSecret("unboundApiKey") as Promise, + this.getSecret("unboundApiKey") as Promise, this.getGlobalState("unboundModelId") as Promise, ]) diff --git a/src/shared/ExtensionMessage.ts b/src/shared/ExtensionMessage.ts index 4cd6370697c..69cc5187f87 100644 --- a/src/shared/ExtensionMessage.ts +++ b/src/shared/ExtensionMessage.ts @@ -159,6 +159,8 @@ export type ClineSay = | "command" | "mcp_server_request_started" | "mcp_server_response" + | "new_task_started" + | "new_task" export interface ClineSayTool { tool: @@ -171,6 +173,7 @@ export interface ClineSayTool { | "listCodeDefinitionNames" | "searchFiles" | "switchMode" + | "newTask" path?: string diff?: string content?: string diff --git a/src/shared/tool-groups.ts b/src/shared/tool-groups.ts index 19f43a59e18..055fb116607 100644 --- a/src/shared/tool-groups.ts +++ b/src/shared/tool-groups.ts @@ -16,6 +16,7 @@ export const TOOL_DISPLAY_NAMES = { ask_followup_question: "ask questions", attempt_completion: "complete tasks", switch_mode: "switch modes", + new_task: "create new task", } as const // Define available tool groups @@ -25,12 +26,18 @@ export const TOOL_GROUPS: Record = { browser: ["browser_action"], command: ["execute_command"], mcp: ["use_mcp_tool", "access_mcp_resource"], + modes: ["switch_mode", "new_task"], } export type ToolGroup = keyof typeof TOOL_GROUPS // Tools that are always available to all modes -export const ALWAYS_AVAILABLE_TOOLS = ["ask_followup_question", "attempt_completion", "switch_mode"] as const +export const ALWAYS_AVAILABLE_TOOLS = [ + "ask_followup_question", + "attempt_completion", + "switch_mode", + "new_task", +] as const // Tool name types for type safety export type ToolName = keyof typeof TOOL_DISPLAY_NAMES diff --git a/webview-ui/src/components/chat/AutoApproveMenu.tsx b/webview-ui/src/components/chat/AutoApproveMenu.tsx index b6b5c9a4f7c..161f3032b07 100644 --- a/webview-ui/src/components/chat/AutoApproveMenu.tsx +++ b/webview-ui/src/components/chat/AutoApproveMenu.tsx @@ -75,10 +75,11 @@ const AutoApproveMenu = ({ style }: AutoApproveMenuProps) => { }, { id: "switchModes", - label: "Switch between modes", + label: "Switch modes & create tasks", shortName: "Modes", enabled: alwaysAllowModeSwitch ?? false, - description: "Allows automatic switching between different AI modes without requiring approval.", + description: + "Allows automatic switching between different AI modes and creating new tasks without requiring approval.", }, { id: "retryRequests", diff --git a/webview-ui/src/components/chat/ChatRow.tsx b/webview-ui/src/components/chat/ChatRow.tsx index 92da0102693..1ec109f9301 100644 --- a/webview-ui/src/components/chat/ChatRow.tsx +++ b/webview-ui/src/components/chat/ChatRow.tsx @@ -473,6 +473,20 @@ export const ChatRowContent = ({ ) + case "newTask": + return ( + <> +
+ {toolIcon("new-file")} + + Roo wants to create a new task in {tool.mode} mode: + +
+
+ {tool.content} +
+ + ) default: return null } diff --git a/webview-ui/src/components/chat/ChatView.tsx b/webview-ui/src/components/chat/ChatView.tsx index b5d90c80f99..0a32eef77a2 100644 --- a/webview-ui/src/components/chat/ChatView.tsx +++ b/webview-ui/src/components/chat/ChatView.tsx @@ -569,7 +569,8 @@ const ChatView = ({ isHidden, showAnnouncement, hideAnnouncement, showHistoryVie (alwaysAllowMcp && message.ask === "use_mcp_server" && isMcpToolAlwaysAllowed(message)) || (alwaysAllowModeSwitch && message.ask === "tool" && - JSON.parse(message.text || "{}")?.tool === "switchMode") + (JSON.parse(message.text || "{}")?.tool === "switchMode" || + JSON.parse(message.text || "{}")?.tool === "newTask")) ) }, [ diff --git a/webview-ui/src/components/settings/SettingsView.tsx b/webview-ui/src/components/settings/SettingsView.tsx index 43b1e5a68d4..417a8c5b8dd 100644 --- a/webview-ui/src/components/settings/SettingsView.tsx +++ b/webview-ui/src/components/settings/SettingsView.tsx @@ -344,10 +344,11 @@ const SettingsView = ({ onDone }: SettingsViewProps) => { setAlwaysAllowModeSwitch(e.target.checked)}> - Always approve mode switching + Always approve mode switching & task creation

- Automatically switch between different AI modes without requiring approval + Automatically switch between different AI modes and create new tasks without requiring + approval