diff --git a/docs/slash-command-tool-plan.md b/docs/slash-command-tool-plan.md new file mode 100644 index 0000000000..b09eab6807 --- /dev/null +++ b/docs/slash-command-tool-plan.md @@ -0,0 +1,161 @@ +# Slash Command Tool Implementation Plan + +## Overview + +Add a new tool `execute_slash_command` that allows the LLM to trigger slash commands within the Roo-Code system. This will enable the LLM to programmatically execute commands that are typically invoked by users through the chat interface. + +## Background + +Currently, the system has various slash commands that users can invoke manually (like `/review`, `/mode`, etc.). The LLM cannot directly trigger these commands, limiting its ability to orchestrate complex workflows that might benefit from using these commands. + +## Implementation Details + +### 1. Tool Definition + +- **Tool Name**: `execute_slash_command` +- **Purpose**: Execute slash commands programmatically +- **Parameters**: + - `command`: The slash command to execute (e.g., "review", "mode", etc.) + - `args`: Optional arguments for the command (as a string) + +### 2. Architecture + +#### Component Structure: + +``` +src/ +├── shared/ +│ └── tools.ts # Add ExecuteSlashCommandToolUse interface +├── core/ +│ ├── tools/ +│ │ └── executeSlashCommandTool.ts # New tool implementation +│ └── assistant-message/ +│ └── presentAssistantMessage.ts # Add case for execute_slash_command +└── tests/ + └── executeSlashCommandTool.spec.ts # Tests for the new tool +``` + +### 3. Key Components + +#### A. Type Definition (src/shared/tools.ts) + +```typescript +export interface ExecuteSlashCommandToolUse extends ToolUse { + name: "execute_slash_command" + params: Partial, "command" | "args">> +} +``` + +#### B. Tool Implementation (src/core/tools/executeSlashCommandTool.ts) + +The tool will: + +1. Parse the command and arguments +2. Validate the command exists and is allowed +3. Execute the command through the appropriate handler +4. Return the result or error + +#### C. Integration Points + +1. Add to tool registry in `TOOL_DISPLAY_NAMES` +2. Add to tool groups if needed +3. Add case in `presentAssistantMessage.ts` +4. Update system prompt to include the new tool + +### 4. Command Execution Strategy + +Since slash commands are typically handled through the chat interface and may involve complex interactions with the Task instance, we have several options: + +**Option 1: Direct Command Execution** + +- Parse and execute commands directly within the tool +- Requires mapping each command to its implementation + +**Option 2: Command Router Pattern** + +- Create a command router that maps command names to handlers +- More extensible for future commands + +**Option 3: Leverage Existing Infrastructure** + +- Use the existing command handling infrastructure if available +- Most consistent with current architecture + +**Recommended: Option 2** - Command Router Pattern for extensibility + +### 5. Security Considerations + +1. **Command Whitelist**: Only allow specific commands to be executed +2. **Permission Checks**: Ensure the LLM respects mode restrictions +3. **Argument Validation**: Validate and sanitize command arguments +4. **Audit Logging**: Log all slash command executions for debugging + +### 6. Supported Commands (Initial) + +For the initial implementation, support these commands: + +- `/review` - Trigger code review +- `/mode [mode_name]` - Switch modes +- `/checkpoint` - Create a checkpoint +- `/diff` - Show diff view +- `/test` - Run tests + +### 7. Error Handling + +1. Invalid command: Return clear error message +2. Missing arguments: Provide helpful feedback +3. Permission denied: Explain why command cannot be executed +4. Command failure: Return detailed error information + +### 8. Testing Strategy + +1. Unit tests for the tool implementation +2. Integration tests for command execution +3. Test permission checks and restrictions +4. Test error scenarios + +### 9. Documentation + +1. Update tool documentation +2. Add examples to system prompt +3. Document supported commands and their arguments + +## Implementation Steps + +1. **Phase 1: Core Implementation** + + - Create type definitions + - Implement basic tool structure + - Add to tool registry + +2. **Phase 2: Command Handling** + + - Implement command router + - Add initial command handlers + - Implement validation and security + +3. **Phase 3: Integration** + + - Integrate with presentAssistantMessage + - Update system prompt + - Add to tool groups + +4. **Phase 4: Testing & Documentation** + - Write comprehensive tests + - Document the feature + - Add usage examples + +## Success Criteria + +1. LLM can successfully execute slash commands +2. Commands respect mode restrictions +3. Clear error messages for invalid commands +4. All tests pass +5. No regression in existing functionality + +## Future Enhancements + +1. Support for more complex command arguments +2. Command chaining capabilities +3. Custom command definitions +4. Command history and undo functionality diff --git a/packages/types/src/tool.ts b/packages/types/src/tool.ts index 7a3fd21199..8e41b7b2c7 100644 --- a/packages/types/src/tool.ts +++ b/packages/types/src/tool.ts @@ -34,6 +34,7 @@ export const toolNames = [ "fetch_instructions", "codebase_search", "update_todo_list", + "execute_slash_command", ] as const export const toolNamesSchema = z.enum(toolNames) diff --git a/src/core/assistant-message/presentAssistantMessage.ts b/src/core/assistant-message/presentAssistantMessage.ts index a8b90728b1..7e81bfa14c 100644 --- a/src/core/assistant-message/presentAssistantMessage.ts +++ b/src/core/assistant-message/presentAssistantMessage.ts @@ -26,6 +26,7 @@ import { askFollowupQuestionTool } from "../tools/askFollowupQuestionTool" import { switchModeTool } from "../tools/switchModeTool" import { attemptCompletionTool } from "../tools/attemptCompletionTool" import { newTaskTool } from "../tools/newTaskTool" +import { executeSlashCommandTool } from "../tools/executeSlashCommandTool" import { updateTodoListTool } from "../tools/updateTodoListTool" @@ -221,6 +222,11 @@ export async function presentAssistantMessage(cline: Task) { const modeName = getModeBySlug(mode, customModes)?.name ?? mode return `[${block.name} in ${modeName} mode: '${message}']` } + case "execute_slash_command": { + const command = block.params.slash_command ?? "(no command)" + const args = block.params.args + return `[${block.name}: /${command}${args ? ` ${args}` : ""}]` + } } } @@ -546,6 +552,16 @@ export async function presentAssistantMessage(cline: Task) { askFinishSubTaskApproval, ) break + case "execute_slash_command": + await executeSlashCommandTool( + cline, + block, + askApproval, + handleError, + pushToolResult, + removeClosingTag, + ) + break } break diff --git a/src/core/prompts/tools/execute-slash-command.ts b/src/core/prompts/tools/execute-slash-command.ts new file mode 100644 index 0000000000..dca0876bce --- /dev/null +++ b/src/core/prompts/tools/execute-slash-command.ts @@ -0,0 +1,52 @@ +import { ToolArgs } from "./types" + +export function getExecuteSlashCommandDescription(args: ToolArgs): string { + return `## execute_slash_command +Description: Execute slash commands programmatically. This tool allows you to trigger commands that are typically invoked by users through the chat interface using the "/" prefix. +Parameters: +- slash_command: (required) The name of the slash command to execute (without the "/" prefix). Available commands: + - review: Trigger code review for current changes (requires args like "slack comment: " or "github issue #123") + - mode: Switch to a different mode (requires mode name as args, e.g., "code", "architect", "debug") + - checkpoint: Create a checkpoint of current changes + - diff: Show diff view for current changes + - test: Run tests for the project (optionally specify test command as args) +- args: (optional) Arguments to pass to the slash command. Required for some commands like "review" and "mode". + +Usage: + +command_name +optional arguments + + +Examples: + +1. Trigger a code review: + +review +slack comment: Please review the authentication implementation + + +2. Switch to architect mode: + +mode +architect + + +3. Create a checkpoint: + +checkpoint + + +4. Show diff view: + +diff + + +5. Run tests with custom command: + +test +npm run test:unit + + +Note: Some commands may have limited functionality when executed programmatically compared to user invocation. The tool will provide feedback if a command cannot be fully executed.` +} diff --git a/src/core/prompts/tools/index.ts b/src/core/prompts/tools/index.ts index 3eb112d270..7bfd05caf4 100644 --- a/src/core/prompts/tools/index.ts +++ b/src/core/prompts/tools/index.ts @@ -25,6 +25,7 @@ import { getSwitchModeDescription } from "./switch-mode" import { getNewTaskDescription } from "./new-task" import { getCodebaseSearchDescription } from "./codebase-search" import { getUpdateTodoListDescription } from "./update-todo-list" +import { getExecuteSlashCommandDescription } from "./execute-slash-command" import { CodeIndexManager } from "../../../services/code-index/manager" // Map of tool names to their description functions @@ -56,6 +57,7 @@ const toolDescriptionMap: Record string | undefined> apply_diff: (args) => args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "", update_todo_list: (args) => getUpdateTodoListDescription(args), + execute_slash_command: (args) => getExecuteSlashCommandDescription(args), } export function getToolDescriptionsForMode( @@ -164,4 +166,5 @@ export { getInsertContentDescription, getSearchAndReplaceDescription, getCodebaseSearchDescription, + getExecuteSlashCommandDescription, } diff --git a/src/core/tools/__tests__/executeSlashCommandTool.spec.ts b/src/core/tools/__tests__/executeSlashCommandTool.spec.ts new file mode 100644 index 0000000000..95ed2a1a73 --- /dev/null +++ b/src/core/tools/__tests__/executeSlashCommandTool.spec.ts @@ -0,0 +1,298 @@ +import { describe, it, expect, vi, beforeEach } from "vitest" +import { executeSlashCommandTool, getAvailableSlashCommands, getSlashCommandsInfo } from "../executeSlashCommandTool" +import { Task } from "../../task/Task" +import { formatResponse } from "../../prompts/responses" + +// Mock dependencies +vi.mock("../../prompts/responses", () => ({ + formatResponse: { + toolResult: vi.fn((result) => result), + toolError: vi.fn((error) => error), + }, +})) + +describe("executeSlashCommandTool", () => { + let mockCline: any + let mockBlock: any + let mockAskApproval: any + let mockHandleError: any + let mockPushToolResult: any + let mockRemoveClosingTag: any + + beforeEach(() => { + vi.clearAllMocks() + + // Create mock Cline instance + mockCline = { + taskId: "test-task-id", + instanceId: "test-instance-id", + consecutiveMistakeCount: 0, + recordToolError: vi.fn(), + sayAndCreateMissingParamError: vi.fn().mockResolvedValue("Missing parameter error"), + say: vi.fn().mockResolvedValue(undefined), + ask: vi.fn().mockResolvedValue({ response: "yesButtonClicked" }), + checkpointSave: vi.fn().mockResolvedValue(undefined), + providerRef: { + deref: vi.fn().mockReturnValue({ + handleModeSwitch: vi.fn().mockResolvedValue(undefined), + }), + }, + } as any + + // Create mock tool use block + mockBlock = { + params: { + slash_command: "checkpoint", + args: undefined, + }, + partial: false, + } + + // Create mock functions + mockAskApproval = vi.fn().mockResolvedValue(true) + mockHandleError = vi.fn() + mockPushToolResult = vi.fn() + mockRemoveClosingTag = vi.fn((tag, value) => value) + }) + + describe("Basic functionality", () => { + it("should execute checkpoint command successfully", async () => { + mockBlock.params.slash_command = "checkpoint" + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.checkpointSave).toHaveBeenCalledWith(true) + expect(mockPushToolResult).toHaveBeenCalledWith("Checkpoint created successfully") + expect(mockCline.consecutiveMistakeCount).toBe(0) + }) + + it("should handle mode switch command", async () => { + mockBlock.params.slash_command = "mode" + mockBlock.params.args = "architect" + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + const provider = mockCline.providerRef.deref() + expect(provider.handleModeSwitch).toHaveBeenCalledWith("architect") + expect(mockPushToolResult).toHaveBeenCalledWith("Successfully switched to architect mode") + }) + + it("should handle test command", async () => { + mockBlock.params.slash_command = "test" + mockBlock.params.args = "npm run test:unit" + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockPushToolResult).toHaveBeenCalledWith( + expect.stringContaining("Would execute test command: npm run test:unit"), + ) + }) + }) + + describe("Error handling", () => { + it("should handle missing slash_command parameter", async () => { + mockBlock.params.slash_command = undefined + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.consecutiveMistakeCount).toBe(1) + expect(mockCline.recordToolError).toHaveBeenCalledWith("execute_slash_command") + expect(mockCline.sayAndCreateMissingParamError).toHaveBeenCalledWith( + "execute_slash_command", + "slash_command", + ) + expect(mockPushToolResult).toHaveBeenCalledWith("Missing parameter error") + }) + + it("should handle unknown command", async () => { + mockBlock.params.slash_command = "unknown_command" + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.consecutiveMistakeCount).toBe(1) + expect(mockCline.recordToolError).toHaveBeenCalledWith("execute_slash_command") + expect(mockCline.say).toHaveBeenCalledWith("error", expect.stringContaining("Unknown slash command")) + expect(mockPushToolResult).toHaveBeenCalledWith(expect.stringContaining("Unknown slash command")) + }) + + it("should handle missing required arguments", async () => { + mockBlock.params.slash_command = "mode" + mockBlock.params.args = undefined + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.consecutiveMistakeCount).toBe(1) + expect(mockCline.recordToolError).toHaveBeenCalledWith("execute_slash_command") + expect(mockCline.say).toHaveBeenCalledWith("error", "The /mode command requires arguments") + expect(mockPushToolResult).toHaveBeenCalledWith("The /mode command requires arguments") + }) + + it("should handle checkpoint save failure", async () => { + mockBlock.params.slash_command = "checkpoint" + mockCline.checkpointSave.mockRejectedValue(new Error("Checkpoint failed")) + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockPushToolResult).toHaveBeenCalledWith("Failed to create checkpoint: Checkpoint failed") + }) + + it("should handle mode switch failure", async () => { + mockBlock.params.slash_command = "mode" + mockBlock.params.args = "invalid_mode" + const provider = mockCline.providerRef.deref() + provider.handleModeSwitch.mockRejectedValue(new Error("Invalid mode")) + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockPushToolResult).toHaveBeenCalledWith("Failed to switch to invalid_mode mode: Invalid mode") + }) + }) + + describe("User approval", () => { + it("should ask for user approval before executing command", async () => { + mockBlock.params.slash_command = "checkpoint" + mockCline.ask.mockResolvedValue({ response: "yesButtonClicked" }) + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.ask).toHaveBeenCalledWith( + "tool", + expect.stringContaining("Execute slash command: /checkpoint"), + ) + expect(mockCline.checkpointSave).toHaveBeenCalled() + }) + + it("should not execute command if user denies approval", async () => { + mockBlock.params.slash_command = "checkpoint" + mockCline.ask.mockResolvedValue({ response: "noButtonClicked" }) + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.checkpointSave).not.toHaveBeenCalled() + expect(mockPushToolResult).toHaveBeenCalledWith("Slash command execution was rejected by the user.") + }) + }) + + describe("Partial blocks", () => { + it("should handle partial blocks", async () => { + mockBlock.partial = true + mockBlock.params.slash_command = "checkpoint" + + await executeSlashCommandTool( + mockCline as Task, + mockBlock, + mockAskApproval, + mockHandleError, + mockPushToolResult, + mockRemoveClosingTag, + ) + + expect(mockCline.ask).toHaveBeenCalledWith( + "tool", + expect.stringContaining("execute_slash_command: /checkpoint"), + true, + ) + expect(mockCline.checkpointSave).not.toHaveBeenCalled() + expect(mockPushToolResult).not.toHaveBeenCalled() + }) + }) +}) + +describe("getAvailableSlashCommands", () => { + it("should return list of available commands", () => { + const commands = getAvailableSlashCommands() + expect(commands).toContain("review") + expect(commands).toContain("mode") + expect(commands).toContain("checkpoint") + expect(commands).toContain("diff") + expect(commands).toContain("test") + }) +}) + +describe("getSlashCommandsInfo", () => { + it("should return detailed information about commands", () => { + const info = getSlashCommandsInfo() + + expect(info).toHaveLength(5) + + const reviewCommand = info.find((cmd) => cmd.name === "review") + expect(reviewCommand).toBeDefined() + expect(reviewCommand?.requiresArgs).toBe(true) + expect(reviewCommand?.description).toContain("Trigger code review") + + const checkpointCommand = info.find((cmd) => cmd.name === "checkpoint") + expect(checkpointCommand).toBeDefined() + expect(checkpointCommand?.requiresArgs).toBe(false) + expect(checkpointCommand?.description).toContain("Create a checkpoint") + }) +}) diff --git a/src/core/tools/executeSlashCommandTool.ts b/src/core/tools/executeSlashCommandTool.ts new file mode 100644 index 0000000000..c400f24f0f --- /dev/null +++ b/src/core/tools/executeSlashCommandTool.ts @@ -0,0 +1,198 @@ +import { Task } from "../task/Task" +import { formatResponse } from "../prompts/responses" +import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools" + +// Define supported slash commands and their handlers +interface SlashCommandHandler { + name: string + description: string + requiresArgs: boolean + execute: (cline: Task, args?: string) => Promise +} + +// Command registry - maps command names to their handlers +const SLASH_COMMAND_REGISTRY: Record = { + review: { + name: "review", + description: "Trigger code review for current changes", + requiresArgs: true, + execute: async (cline: Task, args?: string) => { + // The review command is typically handled by creating a new task + // We'll simulate this by returning a message about what would happen + if (!args) { + return "Error: The /review command requires arguments (e.g., 'slack comment: ' or 'github issue #123')" + } + + // In a real implementation, this would trigger the review workflow + // For now, we'll return a message about what would happen + return `Would trigger review with arguments: ${args}\n\nNote: To actually trigger a review, use the new_task tool with mode 'code' and message '/review ${args}'` + }, + }, + + mode: { + name: "mode", + description: "Switch to a different mode", + requiresArgs: true, + execute: async (cline: Task, args?: string) => { + if (!args) { + return "Error: The /mode command requires a mode name (e.g., /mode code, /mode architect)" + } + + const provider = cline.providerRef.deref() + if (!provider) { + return "Error: Provider reference lost, cannot switch mode" + } + + // Parse the mode name from args + const modeName = args.trim().toLowerCase() + + // Switch mode through the provider + try { + await provider.handleModeSwitch(modeName) + return `Successfully switched to ${modeName} mode` + } catch (error) { + return `Failed to switch to ${modeName} mode: ${error.message}` + } + }, + }, + + checkpoint: { + name: "checkpoint", + description: "Create a checkpoint of current changes", + requiresArgs: false, + execute: async (cline: Task, args?: string) => { + try { + await cline.checkpointSave(true) + return "Checkpoint created successfully" + } catch (error) { + return `Failed to create checkpoint: ${error.message}` + } + }, + }, + + diff: { + name: "diff", + description: "Show diff view for current changes", + requiresArgs: false, + execute: async (cline: Task, args?: string) => { + // The diff command would typically show a diff view + // Since this requires specific checkpoint data, we'll return guidance + return "To view diffs, the checkpoint service needs to be properly initialized with specific commit hashes. Use the checkpoint command first to create checkpoints, then diffs can be viewed between them." + }, + }, + + test: { + name: "test", + description: "Run tests for the project", + requiresArgs: false, + execute: async (cline: Task, args?: string) => { + // This would typically execute test commands + // For now, we'll return a message about what would happen + const testCommand = args || "npm test" + return `Would execute test command: ${testCommand}\n\nNote: To actually run tests, use the execute_command tool with the appropriate test command.` + }, + }, +} + +/** + * Execute a slash command programmatically + * + * @param cline - The Task instance + * @param block - The tool use block containing command parameters + * @param askApproval - Function to ask user approval + * @param handleError - Function to handle errors + * @param pushToolResult - Function to push tool results + * @param removeClosingTag - Function to remove closing tags from partial content + */ +export async function executeSlashCommandTool( + cline: Task, + block: ToolUse, + askApproval: AskApproval, + handleError: HandleError, + pushToolResult: PushToolResult, + removeClosingTag: RemoveClosingTag, +) { + const commandName: string | undefined = block.params.slash_command + const commandArgs: string | undefined = block.params.args + + try { + if (block.partial) { + // For partial blocks, just show the command being typed + const partialMessage = `execute_slash_command: /${removeClosingTag("slash_command", commandName)} ${removeClosingTag("args", commandArgs) || ""}` + await cline.ask("tool", partialMessage, block.partial).catch(() => {}) + return + } + + // Validate required parameters + if (!commandName) { + cline.consecutiveMistakeCount++ + cline.recordToolError("execute_slash_command") + pushToolResult(await cline.sayAndCreateMissingParamError("execute_slash_command", "slash_command")) + return + } + + // Check if command exists in registry + const handler = SLASH_COMMAND_REGISTRY[commandName.toLowerCase()] + if (!handler) { + cline.consecutiveMistakeCount++ + cline.recordToolError("execute_slash_command") + const availableCommands = Object.keys(SLASH_COMMAND_REGISTRY).join(", ") + const errorMessage = `Unknown slash command: /${commandName}. Available commands: ${availableCommands}` + await cline.say("error", errorMessage) + pushToolResult(formatResponse.toolError(errorMessage)) + return + } + + // Check if command requires arguments + if (handler.requiresArgs && !commandArgs) { + cline.consecutiveMistakeCount++ + cline.recordToolError("execute_slash_command") + const errorMessage = `The /${commandName} command requires arguments` + await cline.say("error", errorMessage) + pushToolResult(formatResponse.toolError(errorMessage)) + return + } + + cline.consecutiveMistakeCount = 0 + + // Ask for approval to execute the command + const approvalMessage = `Execute slash command: /${commandName}${commandArgs ? ` ${commandArgs}` : ""}\n\nDescription: ${handler.description}` + + const didApprove = await cline + .ask("tool", approvalMessage) + .then((response) => response.response === "yesButtonClicked") + + if (!didApprove) { + pushToolResult("Slash command execution was rejected by the user.") + return + } + + // Execute the command + const result = await handler.execute(cline, commandArgs) + + // Return the result + pushToolResult(formatResponse.toolResult(result)) + } catch (error) { + await handleError("executing slash command", error) + } +} + +/** + * Get the list of available slash commands + * This can be used by the system prompt to inform the LLM about available commands + */ +export function getAvailableSlashCommands(): string[] { + return Object.keys(SLASH_COMMAND_REGISTRY) +} + +/** + * Get detailed information about all slash commands + * This can be used for documentation or help purposes + */ +export function getSlashCommandsInfo(): Array<{ name: string; description: string; requiresArgs: boolean }> { + return Object.values(SLASH_COMMAND_REGISTRY).map((handler) => ({ + name: handler.name, + description: handler.description, + requiresArgs: handler.requiresArgs, + })) +} diff --git a/src/shared/tools.ts b/src/shared/tools.ts index 047c2fe351..236a31f5ac 100644 --- a/src/shared/tools.ts +++ b/src/shared/tools.ts @@ -65,6 +65,7 @@ export const toolParamNames = [ "query", "args", "todos", + "slash_command", ] as const export type ToolParamName = (typeof toolParamNames)[number] @@ -164,6 +165,11 @@ export interface SearchAndReplaceToolUse extends ToolUse { Partial, "use_regex" | "ignore_case" | "start_line" | "end_line">> } +export interface ExecuteSlashCommandToolUse extends ToolUse { + name: "execute_slash_command" + params: Partial, "slash_command" | "args">> +} + // Define tool group configuration export type ToolGroupConfig = { tools: readonly string[] @@ -190,6 +196,7 @@ export const TOOL_DISPLAY_NAMES: Record = { search_and_replace: "search and replace", codebase_search: "codebase search", update_todo_list: "update todo list", + execute_slash_command: "execute slash commands", } as const // Define available tool groups. @@ -211,7 +218,7 @@ export const TOOL_GROUPS: Record = { tools: ["browser_action"], }, command: { - tools: ["execute_command"], + tools: ["execute_command", "execute_slash_command"], }, mcp: { tools: ["use_mcp_tool", "access_mcp_resource"],