Roo-Code/src/shared/tools.ts

355 lines
11 KiB
TypeScript

import { Anthropic } from "@anthropic-ai/sdk"
import type {
ClineAsk,
ToolProgressStatus,
ToolGroup,
ToolName,
FileEntry,
BrowserActionParams,
GenerateImageParams,
} from "@roo-code/types"
export type ToolResponse = string | Array<Anthropic.TextBlockParam | Anthropic.ImageBlockParam>
export type AskApproval = (
type: ClineAsk,
partialMessage?: string,
progressStatus?: ToolProgressStatus,
forceApproval?: boolean,
) => Promise<boolean>
export type HandleError = (action: string, error: Error) => Promise<void>
export type PushToolResult = (content: ToolResponse) => void
export type RemoveClosingTag = (tag: ToolParamName, content?: string) => string
export type AskFinishSubTaskApproval = () => Promise<boolean>
export type ToolDescription = () => string
export interface TextContent {
type: "text"
content: string
partial: boolean
}
export const toolParamNames = [
"command",
"path",
"content",
"line_count",
"regex",
"file_pattern",
"recursive",
"action",
"url",
"coordinate",
"text",
"server_name",
"tool_name",
"arguments",
"uri",
"question",
"result",
"diff",
"mode_slug",
"reason",
"line",
"mode",
"message",
"cwd",
"follow_up",
"task",
"size",
"query",
"args",
"start_line",
"end_line",
"todos",
"prompt",
"image",
"files", // Native protocol parameter for read_file
] as const
export type ToolParamName = (typeof toolParamNames)[number]
export type ToolProtocol = "xml" | "native"
/**
* Type map defining the native (typed) argument structure for each tool.
* Tools not listed here will fall back to `any` for backward compatibility.
*/
export type NativeToolArgs = {
access_mcp_resource: { server_name: string; uri: string }
read_file: { files: FileEntry[] }
attempt_completion: { result: string }
execute_command: { command: string; cwd?: string }
insert_content: { path: string; line: number; content: string }
apply_diff: { path: string; diff: string }
ask_followup_question: {
question: string
follow_up: Array<{ text: string; mode?: string }>
}
browser_action: BrowserActionParams
codebase_search: { query: string; path?: string }
fetch_instructions: { task: string }
generate_image: GenerateImageParams
list_code_definition_names: { path: string }
run_slash_command: { command: string; args?: string }
search_files: { path: string; regex: string; file_pattern?: string | null }
switch_mode: { mode_slug: string; reason: string }
update_todo_list: { todos: string }
use_mcp_tool: { server_name: string; tool_name: string; arguments?: Record<string, unknown> }
write_to_file: { path: string; content: string; line_count: number }
// Add more tools as they are migrated to native protocol
}
/**
* Generic ToolUse interface that provides proper typing for both protocols.
*
* @template TName - The specific tool name, which determines the nativeArgs type
*/
export interface ToolUse<TName extends ToolName = ToolName> {
type: "tool_use"
id?: string // Optional ID to track tool calls
name: TName
// params is a partial record, allowing only some or none of the possible parameters to be used
params: Partial<Record<ToolParamName, string>>
partial: boolean
// nativeArgs is properly typed based on TName if it's in NativeToolArgs, otherwise never
nativeArgs?: TName extends keyof NativeToolArgs ? NativeToolArgs[TName] : never
}
/**
* Represents a native MCP tool call from the model.
* In native mode, MCP tools are called directly with their prefixed name (e.g., "mcp_serverName_toolName")
* rather than through the use_mcp_tool wrapper. This type preserves the original tool name
* so it appears correctly in API conversation history.
*/
export interface McpToolUse {
type: "mcp_tool_use"
id?: string // Tool call ID from the API
/** The original tool name from the API (e.g., "mcp_serverName_toolName") */
name: string
/** Extracted server name from the tool name */
serverName: string
/** Extracted tool name from the tool name */
toolName: string
/** Arguments passed to the MCP tool */
arguments: Record<string, unknown>
partial: boolean
}
export interface ExecuteCommandToolUse extends ToolUse<"execute_command"> {
name: "execute_command"
// Pick<Record<ToolParamName, string>, "command"> makes "command" required, but Partial<> makes it optional
params: Partial<Pick<Record<ToolParamName, string>, "command" | "cwd">>
}
export interface ReadFileToolUse extends ToolUse<"read_file"> {
name: "read_file"
params: Partial<Pick<Record<ToolParamName, string>, "args" | "path" | "start_line" | "end_line" | "files">>
}
export interface FetchInstructionsToolUse extends ToolUse<"fetch_instructions"> {
name: "fetch_instructions"
params: Partial<Pick<Record<ToolParamName, string>, "task">>
}
export interface WriteToFileToolUse extends ToolUse<"write_to_file"> {
name: "write_to_file"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "content" | "line_count">>
}
export interface InsertCodeBlockToolUse extends ToolUse<"insert_content"> {
name: "insert_content"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "line" | "content">>
}
export interface CodebaseSearchToolUse extends ToolUse<"codebase_search"> {
name: "codebase_search"
params: Partial<Pick<Record<ToolParamName, string>, "query" | "path">>
}
export interface SearchFilesToolUse extends ToolUse<"search_files"> {
name: "search_files"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "regex" | "file_pattern">>
}
export interface ListFilesToolUse extends ToolUse<"list_files"> {
name: "list_files"
params: Partial<Pick<Record<ToolParamName, string>, "path" | "recursive">>
}
export interface ListCodeDefinitionNamesToolUse extends ToolUse<"list_code_definition_names"> {
name: "list_code_definition_names"
params: Partial<Pick<Record<ToolParamName, string>, "path">>
}
export interface BrowserActionToolUse extends ToolUse<"browser_action"> {
name: "browser_action"
params: Partial<Pick<Record<ToolParamName, string>, "action" | "url" | "coordinate" | "text" | "size">>
}
export interface UseMcpToolToolUse extends ToolUse<"use_mcp_tool"> {
name: "use_mcp_tool"
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "tool_name" | "arguments">>
}
export interface AccessMcpResourceToolUse extends ToolUse<"access_mcp_resource"> {
name: "access_mcp_resource"
params: Partial<Pick<Record<ToolParamName, string>, "server_name" | "uri">>
}
export interface AskFollowupQuestionToolUse extends ToolUse<"ask_followup_question"> {
name: "ask_followup_question"
params: Partial<Pick<Record<ToolParamName, string>, "question" | "follow_up">>
}
export interface AttemptCompletionToolUse extends ToolUse<"attempt_completion"> {
name: "attempt_completion"
params: Partial<Pick<Record<ToolParamName, string>, "result">>
}
export interface SwitchModeToolUse extends ToolUse<"switch_mode"> {
name: "switch_mode"
params: Partial<Pick<Record<ToolParamName, string>, "mode_slug" | "reason">>
}
export interface NewTaskToolUse extends ToolUse<"new_task"> {
name: "new_task"
params: Partial<Pick<Record<ToolParamName, string>, "mode" | "message" | "todos">>
}
export interface RunSlashCommandToolUse extends ToolUse<"run_slash_command"> {
name: "run_slash_command"
params: Partial<Pick<Record<ToolParamName, string>, "command" | "args">>
}
export interface GenerateImageToolUse extends ToolUse<"generate_image"> {
name: "generate_image"
params: Partial<Pick<Record<ToolParamName, string>, "prompt" | "path" | "image">>
}
// Define tool group configuration
export type ToolGroupConfig = {
tools: readonly string[]
alwaysAvailable?: boolean // Whether this group is always available and shouldn't show in prompts view
}
export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
execute_command: "run commands",
read_file: "read files",
fetch_instructions: "fetch instructions",
write_to_file: "write files",
apply_diff: "apply changes",
search_files: "search files",
list_files: "list files",
list_code_definition_names: "list definitions",
browser_action: "use a browser",
use_mcp_tool: "use mcp tools",
access_mcp_resource: "access mcp resources",
ask_followup_question: "ask questions",
attempt_completion: "complete tasks",
switch_mode: "switch modes",
new_task: "create new task",
insert_content: "insert content",
codebase_search: "codebase search",
update_todo_list: "update todo list",
run_slash_command: "run slash command",
generate_image: "generate images",
} as const
// Define available tool groups.
export const TOOL_GROUPS: Record<ToolGroup, ToolGroupConfig> = {
read: {
tools: [
"read_file",
"fetch_instructions",
"search_files",
"list_files",
"list_code_definition_names",
"codebase_search",
],
},
edit: {
tools: ["apply_diff", "write_to_file", "insert_content", "generate_image"],
},
browser: {
tools: ["browser_action"],
},
command: {
tools: ["execute_command"],
},
mcp: {
tools: ["use_mcp_tool", "access_mcp_resource"],
},
modes: {
tools: ["switch_mode", "new_task"],
alwaysAvailable: true,
},
}
// Tools that are always available to all modes.
export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [
"ask_followup_question",
"attempt_completion",
"switch_mode",
"new_task",
"update_todo_list",
"run_slash_command",
] as const
export type DiffResult =
| { success: true; content: string; failParts?: DiffResult[] }
| ({
success: false
error?: string
details?: {
similarity?: number
threshold?: number
matchedRange?: { start: number; end: number }
searchContent?: string
bestMatch?: string
}
failParts?: DiffResult[]
} & ({ error: string } | { failParts: DiffResult[] }))
export interface DiffItem {
content: string
startLine?: number
}
export interface DiffStrategy {
/**
* Get the name of this diff strategy for analytics and debugging
* @returns The name of the diff strategy
*/
getName(): string
/**
* Get the tool description for this diff strategy
* @param args The tool arguments including cwd and toolOptions
* @returns The complete tool description including format requirements and examples
*/
getToolDescription(args: { cwd: string; toolOptions?: { [key: string]: string } }): string
/**
* Apply a diff to the original content
* @param originalContent The original file content
* @param diffContent The diff content in the strategy's format (string for legacy, DiffItem[] for new)
* @param startLine Optional line number where the search block starts. If not provided, searches the entire file.
* @param endLine Optional line number where the search block ends. If not provided, searches the entire file.
* @returns A DiffResult object containing either the successful result or error details
*/
applyDiff(
originalContent: string,
diffContent: string | DiffItem[],
startLine?: number,
endLine?: number,
): Promise<DiffResult>
getProgressStatus?(toolUse: ToolUse, result?: any): ToolProgressStatus
}