Roo-Code/src/core/tools/RunSlashCommandTool.ts
Daniel 5e6e601b0a
Add native tool call support (#9159)
Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com>
2025-11-13 12:48:35 -05:00

122 lines
3.5 KiB
TypeScript

import { Task } from "../task/Task"
import { formatResponse } from "../prompts/responses"
import { getCommand, getCommandNames } from "../../services/command/commands"
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
import { BaseTool, ToolCallbacks } from "./BaseTool"
import type { ToolUse } from "../../shared/tools"
interface RunSlashCommandParams {
command: string
args?: string
}
export class RunSlashCommandTool extends BaseTool<"run_slash_command"> {
readonly name = "run_slash_command" as const
parseLegacy(params: Partial<Record<string, string>>): RunSlashCommandParams {
return {
command: params.command || "",
args: params.args,
}
}
async execute(params: RunSlashCommandParams, task: Task, callbacks: ToolCallbacks): Promise<void> {
const { command: commandName, args } = params
const { askApproval, handleError, pushToolResult } = callbacks
// Check if run slash command experiment is enabled
const provider = task.providerRef.deref()
const state = await provider?.getState()
const isRunSlashCommandEnabled = experiments.isEnabled(
state?.experiments ?? {},
EXPERIMENT_IDS.RUN_SLASH_COMMAND,
)
if (!isRunSlashCommandEnabled) {
pushToolResult(
formatResponse.toolError(
"Run slash command is an experimental feature that must be enabled in settings. Please enable 'Run Slash Command' in the Experimental Settings section.",
),
)
return
}
try {
if (!commandName) {
task.consecutiveMistakeCount++
task.recordToolError("run_slash_command")
pushToolResult(await task.sayAndCreateMissingParamError("run_slash_command", "command"))
return
}
task.consecutiveMistakeCount = 0
// Get the command from the commands service
const command = await getCommand(task.cwd, commandName)
if (!command) {
// Get available commands for error message
const availableCommands = await getCommandNames(task.cwd)
task.recordToolError("run_slash_command")
pushToolResult(
formatResponse.toolError(
`Command '${commandName}' not found. Available commands: ${availableCommands.join(", ") || "(none)"}`,
),
)
return
}
const toolMessage = JSON.stringify({
tool: "runSlashCommand",
command: commandName,
args: args,
source: command.source,
description: command.description,
})
const didApprove = await askApproval("tool", toolMessage)
if (!didApprove) {
return
}
// Build the result message
let result = `Command: /${commandName}`
if (command.description) {
result += `\nDescription: ${command.description}`
}
if (command.argumentHint) {
result += `\nArgument hint: ${command.argumentHint}`
}
if (args) {
result += `\nProvided arguments: ${args}`
}
result += `\nSource: ${command.source}`
result += `\n\n--- Command Content ---\n\n${command.content}`
// Return the command content as the tool result
pushToolResult(result)
} catch (error) {
await handleError("running slash command", error as Error)
}
}
override async handlePartial(task: Task, block: ToolUse<"run_slash_command">): Promise<void> {
const commandName: string | undefined = block.params.command
const args: string | undefined = block.params.args
const partialMessage = JSON.stringify({
tool: "runSlashCommand",
command: this.removeClosingTag("command", commandName, block.partial),
args: this.removeClosingTag("args", args, block.partial),
})
await task.ask("tool", partialMessage, block.partial).catch(() => {})
}
}
export const runSlashCommandTool = new RunSlashCommandTool()