mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-10-07 02:58:15 +00:00
feat: add run_slash_command tool for executing slash commands (#7473)
Co-authored-by: Roo Code <roomote@roocode.com> Co-authored-by: Matt Rubens <mrubens@users.noreply.github.com> Co-authored-by: Daniel Riccio <ricciodaniel98@gmail.com>
This commit is contained in:
parent
130ce2936e
commit
d7896924a9
51 changed files with 972 additions and 0 deletions
|
|
@ -11,6 +11,7 @@ export const experimentIds = [
|
|||
"multiFileApplyDiff",
|
||||
"preventFocusDisruption",
|
||||
"imageGeneration",
|
||||
"runSlashCommand",
|
||||
] as const
|
||||
|
||||
export const experimentIdsSchema = z.enum(experimentIds)
|
||||
|
|
@ -26,6 +27,7 @@ export const experimentsSchema = z.object({
|
|||
multiFileApplyDiff: z.boolean().optional(),
|
||||
preventFocusDisruption: z.boolean().optional(),
|
||||
imageGeneration: z.boolean().optional(),
|
||||
runSlashCommand: z.boolean().optional(),
|
||||
})
|
||||
|
||||
export type Experiments = z.infer<typeof experimentsSchema>
|
||||
|
|
|
|||
|
|
@ -34,6 +34,7 @@ export const toolNames = [
|
|||
"fetch_instructions",
|
||||
"codebase_search",
|
||||
"update_todo_list",
|
||||
"run_slash_command",
|
||||
"generate_image",
|
||||
] as const
|
||||
|
||||
|
|
|
|||
|
|
@ -28,6 +28,7 @@ import { attemptCompletionTool } from "../tools/attemptCompletionTool"
|
|||
import { newTaskTool } from "../tools/newTaskTool"
|
||||
|
||||
import { updateTodoListTool } from "../tools/updateTodoListTool"
|
||||
import { runSlashCommandTool } from "../tools/runSlashCommandTool"
|
||||
import { generateImageTool } from "../tools/generateImageTool"
|
||||
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
|
|
@ -222,6 +223,8 @@ export async function presentAssistantMessage(cline: Task) {
|
|||
const modeName = getModeBySlug(mode, customModes)?.name ?? mode
|
||||
return `[${block.name} in ${modeName} mode: '${message}']`
|
||||
}
|
||||
case "run_slash_command":
|
||||
return `[${block.name} for '${block.params.command}'${block.params.args ? ` with args: ${block.params.args}` : ""}]`
|
||||
case "generate_image":
|
||||
return `[${block.name} for '${block.params.path}']`
|
||||
}
|
||||
|
|
@ -549,6 +552,9 @@ export async function presentAssistantMessage(cline: Task) {
|
|||
askFinishSubTaskApproval,
|
||||
)
|
||||
break
|
||||
case "run_slash_command":
|
||||
await runSlashCommandTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
|
||||
break
|
||||
case "generate_image":
|
||||
await generateImageTool(cline, block, askApproval, handleError, pushToolResult, removeClosingTag)
|
||||
break
|
||||
|
|
|
|||
|
|
@ -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 { getRunSlashCommandDescription } from "./run-slash-command"
|
||||
import { getGenerateImageDescription } from "./generate-image"
|
||||
import { CodeIndexManager } from "../../../services/code-index/manager"
|
||||
|
||||
|
|
@ -57,6 +58,7 @@ const toolDescriptionMap: Record<string, (args: ToolArgs) => string | undefined>
|
|||
apply_diff: (args) =>
|
||||
args.diffStrategy ? args.diffStrategy.getToolDescription({ cwd: args.cwd, toolOptions: args.toolOptions }) : "",
|
||||
update_todo_list: (args) => getUpdateTodoListDescription(args),
|
||||
run_slash_command: () => getRunSlashCommandDescription(),
|
||||
generate_image: (args) => getGenerateImageDescription(args),
|
||||
}
|
||||
|
||||
|
|
@ -136,6 +138,11 @@ export function getToolDescriptionsForMode(
|
|||
tools.delete("generate_image")
|
||||
}
|
||||
|
||||
// Conditionally exclude run_slash_command if experiment is not enabled
|
||||
if (!experiments?.runSlashCommand) {
|
||||
tools.delete("run_slash_command")
|
||||
}
|
||||
|
||||
// Map tool descriptions for allowed tools
|
||||
const descriptions = Array.from(tools).map((toolName) => {
|
||||
const descriptionFn = toolDescriptionMap[toolName]
|
||||
|
|
@ -171,5 +178,6 @@ export {
|
|||
getInsertContentDescription,
|
||||
getSearchAndReplaceDescription,
|
||||
getCodebaseSearchDescription,
|
||||
getRunSlashCommandDescription,
|
||||
getGenerateImageDescription,
|
||||
}
|
||||
|
|
|
|||
32
src/core/prompts/tools/run-slash-command.ts
Normal file
32
src/core/prompts/tools/run-slash-command.ts
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/**
|
||||
* Generates the run_slash_command tool description.
|
||||
*/
|
||||
export function getRunSlashCommandDescription(): string {
|
||||
return `## run_slash_command
|
||||
Description: Execute a slash command to get specific instructions or content. Slash commands are predefined templates that provide detailed guidance for common tasks.
|
||||
|
||||
Parameters:
|
||||
- command: (required) The name of the slash command to execute (e.g., "init", "test", "deploy")
|
||||
- args: (optional) Additional arguments or context to pass to the command
|
||||
|
||||
Usage:
|
||||
<run_slash_command>
|
||||
<command>command_name</command>
|
||||
<args>optional arguments</args>
|
||||
</run_slash_command>
|
||||
|
||||
Examples:
|
||||
|
||||
1. Running the init command to analyze a codebase:
|
||||
<run_slash_command>
|
||||
<command>init</command>
|
||||
</run_slash_command>
|
||||
|
||||
2. Running a command with additional context:
|
||||
<run_slash_command>
|
||||
<command>test</command>
|
||||
<args>focus on integration tests</args>
|
||||
</run_slash_command>
|
||||
|
||||
The command content will be returned for you to execute or follow as instructions.`
|
||||
}
|
||||
380
src/core/tools/__tests__/runSlashCommandTool.spec.ts
Normal file
380
src/core/tools/__tests__/runSlashCommandTool.spec.ts
Normal file
|
|
@ -0,0 +1,380 @@
|
|||
import { describe, it, expect, vi, beforeEach } from "vitest"
|
||||
import { runSlashCommandTool } from "../runSlashCommandTool"
|
||||
import { Task } from "../../task/Task"
|
||||
import { formatResponse } from "../../prompts/responses"
|
||||
import { getCommand, getCommandNames } from "../../../services/command/commands"
|
||||
|
||||
// Mock dependencies
|
||||
vi.mock("../../../services/command/commands", () => ({
|
||||
getCommand: vi.fn(),
|
||||
getCommandNames: vi.fn(),
|
||||
}))
|
||||
|
||||
describe("runSlashCommandTool", () => {
|
||||
let mockTask: any
|
||||
let mockAskApproval: any
|
||||
let mockHandleError: any
|
||||
let mockPushToolResult: any
|
||||
let mockRemoveClosingTag: any
|
||||
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
|
||||
mockTask = {
|
||||
consecutiveMistakeCount: 0,
|
||||
recordToolError: vi.fn(),
|
||||
sayAndCreateMissingParamError: vi.fn().mockResolvedValue("Missing parameter error"),
|
||||
ask: vi.fn(),
|
||||
cwd: "/test/project",
|
||||
providerRef: {
|
||||
deref: vi.fn().mockReturnValue({
|
||||
getState: vi.fn().mockResolvedValue({
|
||||
experiments: {
|
||||
runSlashCommand: true,
|
||||
},
|
||||
}),
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
||||
mockAskApproval = vi.fn().mockResolvedValue(true)
|
||||
mockHandleError = vi.fn()
|
||||
mockPushToolResult = vi.fn()
|
||||
mockRemoveClosingTag = vi.fn((tag, text) => text || "")
|
||||
})
|
||||
|
||||
it("should handle missing command parameter", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockTask.consecutiveMistakeCount).toBe(1)
|
||||
expect(mockTask.recordToolError).toHaveBeenCalledWith("run_slash_command")
|
||||
expect(mockTask.sayAndCreateMissingParamError).toHaveBeenCalledWith("run_slash_command", "command")
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith("Missing parameter error")
|
||||
})
|
||||
|
||||
it("should handle command not found", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "nonexistent",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(undefined)
|
||||
vi.mocked(getCommandNames).mockResolvedValue(["init", "test", "deploy"])
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockTask.recordToolError).toHaveBeenCalledWith("run_slash_command")
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(
|
||||
formatResponse.toolError("Command 'nonexistent' not found. Available commands: init, test, deploy"),
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle user rejection", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "init",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const mockCommand = {
|
||||
name: "init",
|
||||
content: "Initialize project",
|
||||
source: "built-in" as const,
|
||||
filePath: "<built-in:init>",
|
||||
description: "Initialize the project",
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(mockCommand)
|
||||
mockAskApproval.mockResolvedValue(false)
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockAskApproval).toHaveBeenCalled()
|
||||
expect(mockPushToolResult).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should successfully execute built-in command", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "init",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const mockCommand = {
|
||||
name: "init",
|
||||
content: "Initialize project content here",
|
||||
source: "built-in" as const,
|
||||
filePath: "<built-in:init>",
|
||||
description: "Analyze codebase and create AGENTS.md",
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(mockCommand)
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockAskApproval).toHaveBeenCalledWith(
|
||||
"tool",
|
||||
JSON.stringify({
|
||||
tool: "runSlashCommand",
|
||||
command: "init",
|
||||
args: undefined,
|
||||
source: "built-in",
|
||||
description: "Analyze codebase and create AGENTS.md",
|
||||
}),
|
||||
)
|
||||
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(
|
||||
`Command: /init
|
||||
Description: Analyze codebase and create AGENTS.md
|
||||
Source: built-in
|
||||
|
||||
--- Command Content ---
|
||||
|
||||
Initialize project content here`,
|
||||
)
|
||||
})
|
||||
|
||||
it("should successfully execute command with arguments", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "test",
|
||||
args: "focus on unit tests",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const mockCommand = {
|
||||
name: "test",
|
||||
content: "Run tests with specific focus",
|
||||
source: "project" as const,
|
||||
filePath: ".roo/commands/test.md",
|
||||
description: "Run project tests",
|
||||
argumentHint: "test type or focus area",
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(mockCommand)
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(
|
||||
`Command: /test
|
||||
Description: Run project tests
|
||||
Argument hint: test type or focus area
|
||||
Provided arguments: focus on unit tests
|
||||
Source: project
|
||||
|
||||
--- Command Content ---
|
||||
|
||||
Run tests with specific focus`,
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle global command", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "deploy",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const mockCommand = {
|
||||
name: "deploy",
|
||||
content: "Deploy application to production",
|
||||
source: "global" as const,
|
||||
filePath: "~/.roo/commands/deploy.md",
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(mockCommand)
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(
|
||||
`Command: /deploy
|
||||
Source: global
|
||||
|
||||
--- Command Content ---
|
||||
|
||||
Deploy application to production`,
|
||||
)
|
||||
})
|
||||
|
||||
it("should handle partial block", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "init",
|
||||
},
|
||||
partial: true,
|
||||
}
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockTask.ask).toHaveBeenCalledWith(
|
||||
"tool",
|
||||
JSON.stringify({
|
||||
tool: "runSlashCommand",
|
||||
command: "init",
|
||||
args: "",
|
||||
}),
|
||||
true,
|
||||
)
|
||||
|
||||
expect(mockPushToolResult).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
it("should handle errors during execution", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "init",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const error = new Error("Test error")
|
||||
vi.mocked(getCommand).mockRejectedValue(error)
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockHandleError).toHaveBeenCalledWith("running slash command", error)
|
||||
})
|
||||
|
||||
it("should handle empty available commands list", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "nonexistent",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(undefined)
|
||||
vi.mocked(getCommandNames).mockResolvedValue([])
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockPushToolResult).toHaveBeenCalledWith(
|
||||
formatResponse.toolError("Command 'nonexistent' not found. Available commands: (none)"),
|
||||
)
|
||||
})
|
||||
|
||||
it("should reset consecutive mistake count on valid command", async () => {
|
||||
const block = {
|
||||
type: "tool_use" as const,
|
||||
name: "run_slash_command" as const,
|
||||
params: {
|
||||
command: "init",
|
||||
},
|
||||
partial: false,
|
||||
}
|
||||
|
||||
mockTask.consecutiveMistakeCount = 5
|
||||
|
||||
const mockCommand = {
|
||||
name: "init",
|
||||
content: "Initialize project",
|
||||
source: "built-in" as const,
|
||||
filePath: "<built-in:init>",
|
||||
}
|
||||
|
||||
vi.mocked(getCommand).mockResolvedValue(mockCommand)
|
||||
|
||||
await runSlashCommandTool(
|
||||
mockTask as Task,
|
||||
block,
|
||||
mockAskApproval,
|
||||
mockHandleError,
|
||||
mockPushToolResult,
|
||||
mockRemoveClosingTag,
|
||||
)
|
||||
|
||||
expect(mockTask.consecutiveMistakeCount).toBe(0)
|
||||
})
|
||||
})
|
||||
108
src/core/tools/runSlashCommandTool.ts
Normal file
108
src/core/tools/runSlashCommandTool.ts
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
import { Task } from "../task/Task"
|
||||
import { ToolUse, AskApproval, HandleError, PushToolResult, RemoveClosingTag } from "../../shared/tools"
|
||||
import { formatResponse } from "../prompts/responses"
|
||||
import { getCommand, getCommandNames } from "../../services/command/commands"
|
||||
import { EXPERIMENT_IDS, experiments } from "../../shared/experiments"
|
||||
|
||||
export async function runSlashCommandTool(
|
||||
task: Task,
|
||||
block: ToolUse,
|
||||
askApproval: AskApproval,
|
||||
handleError: HandleError,
|
||||
pushToolResult: PushToolResult,
|
||||
removeClosingTag: RemoveClosingTag,
|
||||
) {
|
||||
// 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
|
||||
}
|
||||
|
||||
const commandName: string | undefined = block.params.command
|
||||
const args: string | undefined = block.params.args
|
||||
|
||||
try {
|
||||
if (block.partial) {
|
||||
const partialMessage = JSON.stringify({
|
||||
tool: "runSlashCommand",
|
||||
command: removeClosingTag("command", commandName),
|
||||
args: removeClosingTag("args", args),
|
||||
})
|
||||
|
||||
await task.ask("tool", partialMessage, block.partial).catch(() => {})
|
||||
return
|
||||
} else {
|
||||
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)
|
||||
|
||||
return
|
||||
}
|
||||
} catch (error) {
|
||||
await handleError("running slash command", error)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -362,6 +362,7 @@ export interface ClineSayTool {
|
|||
| "insertContent"
|
||||
| "generateImage"
|
||||
| "imageGenerated"
|
||||
| "runSlashCommand"
|
||||
path?: string
|
||||
diff?: string
|
||||
content?: string
|
||||
|
|
@ -399,6 +400,11 @@ export interface ClineSayTool {
|
|||
}>
|
||||
question?: string
|
||||
imageData?: string // Base64 encoded image data for generated images
|
||||
// Properties for runSlashCommand tool
|
||||
command?: string
|
||||
args?: string
|
||||
source?: string
|
||||
description?: string
|
||||
}
|
||||
|
||||
// Must keep in sync with system prompt.
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@ describe("experiments", () => {
|
|||
multiFileApplyDiff: false,
|
||||
preventFocusDisruption: false,
|
||||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
|
||||
})
|
||||
|
|
@ -40,6 +41,7 @@ describe("experiments", () => {
|
|||
multiFileApplyDiff: false,
|
||||
preventFocusDisruption: false,
|
||||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(true)
|
||||
})
|
||||
|
|
@ -50,6 +52,7 @@ describe("experiments", () => {
|
|||
multiFileApplyDiff: false,
|
||||
preventFocusDisruption: false,
|
||||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
}
|
||||
expect(Experiments.isEnabled(experiments, EXPERIMENT_IDS.POWER_STEERING)).toBe(false)
|
||||
})
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export const EXPERIMENT_IDS = {
|
|||
POWER_STEERING: "powerSteering",
|
||||
PREVENT_FOCUS_DISRUPTION: "preventFocusDisruption",
|
||||
IMAGE_GENERATION: "imageGeneration",
|
||||
RUN_SLASH_COMMAND: "runSlashCommand",
|
||||
} as const satisfies Record<string, ExperimentId>
|
||||
|
||||
type _AssertExperimentIds = AssertEqual<Equals<ExperimentId, Values<typeof EXPERIMENT_IDS>>>
|
||||
|
|
@ -20,6 +21,7 @@ export const experimentConfigsMap: Record<ExperimentKey, ExperimentConfig> = {
|
|||
POWER_STEERING: { enabled: false },
|
||||
PREVENT_FOCUS_DISRUPTION: { enabled: false },
|
||||
IMAGE_GENERATION: { enabled: false },
|
||||
RUN_SLASH_COMMAND: { enabled: false },
|
||||
}
|
||||
|
||||
export const experimentDefault = Object.fromEntries(
|
||||
|
|
|
|||
|
|
@ -160,6 +160,11 @@ export interface NewTaskToolUse extends ToolUse {
|
|||
params: Partial<Pick<Record<ToolParamName, string>, "mode" | "message" | "todos">>
|
||||
}
|
||||
|
||||
export interface RunSlashCommandToolUse extends ToolUse {
|
||||
name: "run_slash_command"
|
||||
params: Partial<Pick<Record<ToolParamName, string>, "command" | "args">>
|
||||
}
|
||||
|
||||
export interface SearchAndReplaceToolUse extends ToolUse {
|
||||
name: "search_and_replace"
|
||||
params: Required<Pick<Record<ToolParamName, string>, "path" | "search" | "replace">> &
|
||||
|
|
@ -197,6 +202,7 @@ export const TOOL_DISPLAY_NAMES: Record<ToolName, string> = {
|
|||
search_and_replace: "search and replace",
|
||||
codebase_search: "codebase search",
|
||||
update_todo_list: "update todo list",
|
||||
run_slash_command: "run slash command",
|
||||
generate_image: "generate images",
|
||||
} as const
|
||||
|
||||
|
|
@ -237,6 +243,7 @@ export const ALWAYS_AVAILABLE_TOOLS: ToolName[] = [
|
|||
"switch_mode",
|
||||
"new_task",
|
||||
"update_todo_list",
|
||||
"run_slash_command",
|
||||
] as const
|
||||
|
||||
export type DiffResult =
|
||||
|
|
|
|||
|
|
@ -791,6 +791,75 @@ export const ChatRowContent = ({
|
|||
</div>
|
||||
</>
|
||||
)
|
||||
case "runSlashCommand": {
|
||||
const slashCommandInfo = tool
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
{toolIcon("play")}
|
||||
<span style={{ fontWeight: "bold" }}>
|
||||
{message.type === "ask"
|
||||
? t("chat:slashCommand.wantsToRun")
|
||||
: t("chat:slashCommand.didRun")}
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
marginTop: "4px",
|
||||
backgroundColor: "var(--vscode-editor-background)",
|
||||
border: "1px solid var(--vscode-editorGroup-border)",
|
||||
borderRadius: "4px",
|
||||
overflow: "hidden",
|
||||
cursor: "pointer",
|
||||
}}
|
||||
onClick={handleToggleExpand}>
|
||||
<ToolUseBlockHeader
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
padding: "10px 12px",
|
||||
}}>
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "8px" }}>
|
||||
<span style={{ fontWeight: "500", fontSize: "var(--vscode-font-size)" }}>
|
||||
/{slashCommandInfo.command}
|
||||
</span>
|
||||
{slashCommandInfo.source && (
|
||||
<VSCodeBadge style={{ fontSize: "calc(var(--vscode-font-size) - 2px)" }}>
|
||||
{slashCommandInfo.source}
|
||||
</VSCodeBadge>
|
||||
)}
|
||||
</div>
|
||||
<span className={`codicon codicon-chevron-${isExpanded ? "up" : "down"}`}></span>
|
||||
</ToolUseBlockHeader>
|
||||
{isExpanded && (slashCommandInfo.args || slashCommandInfo.description) && (
|
||||
<div
|
||||
style={{
|
||||
padding: "12px 16px",
|
||||
borderTop: "1px solid var(--vscode-editorGroup-border)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
gap: "8px",
|
||||
}}>
|
||||
{slashCommandInfo.args && (
|
||||
<div>
|
||||
<span style={{ fontWeight: "500" }}>Arguments: </span>
|
||||
<span style={{ color: "var(--vscode-descriptionForeground)" }}>
|
||||
{slashCommandInfo.args}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
{slashCommandInfo.description && (
|
||||
<div style={{ color: "var(--vscode-descriptionForeground)" }}>
|
||||
{slashCommandInfo.description}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
case "generateImage":
|
||||
return (
|
||||
<>
|
||||
|
|
@ -1159,6 +1228,80 @@ export const ChatRowContent = ({
|
|||
return <CodebaseSearchResultsDisplay results={results} />
|
||||
case "user_edit_todos":
|
||||
return <UpdateTodoListToolBlock userEdited onChange={() => {}} />
|
||||
case "tool" as any:
|
||||
// Handle say tool messages
|
||||
const sayTool = safeJsonParse<ClineSayTool>(message.text)
|
||||
if (!sayTool) return null
|
||||
|
||||
switch (sayTool.tool) {
|
||||
case "runSlashCommand": {
|
||||
const slashCommandInfo = sayTool
|
||||
return (
|
||||
<>
|
||||
<div style={headerStyle}>
|
||||
<span
|
||||
className="codicon codicon-terminal-cmd"
|
||||
style={{
|
||||
color: "var(--vscode-foreground)",
|
||||
marginBottom: "-1.5px",
|
||||
}}></span>
|
||||
<span style={{ fontWeight: "bold" }}>{t("chat:slashCommand.didRun")}</span>
|
||||
</div>
|
||||
<ToolUseBlock>
|
||||
<ToolUseBlockHeader
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "flex-start",
|
||||
gap: "4px",
|
||||
padding: "10px 12px",
|
||||
}}>
|
||||
<div
|
||||
style={{
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
gap: "8px",
|
||||
width: "100%",
|
||||
}}>
|
||||
<span
|
||||
style={{ fontWeight: "500", fontSize: "var(--vscode-font-size)" }}>
|
||||
/{slashCommandInfo.command}
|
||||
</span>
|
||||
{slashCommandInfo.args && (
|
||||
<span
|
||||
style={{
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "var(--vscode-font-size)",
|
||||
}}>
|
||||
{slashCommandInfo.args}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
{slashCommandInfo.description && (
|
||||
<div
|
||||
style={{
|
||||
color: "var(--vscode-descriptionForeground)",
|
||||
fontSize: "calc(var(--vscode-font-size) - 1px)",
|
||||
}}>
|
||||
{slashCommandInfo.description}
|
||||
</div>
|
||||
)}
|
||||
{slashCommandInfo.source && (
|
||||
<div style={{ display: "flex", alignItems: "center", gap: "4px" }}>
|
||||
<VSCodeBadge
|
||||
style={{ fontSize: "calc(var(--vscode-font-size) - 2px)" }}>
|
||||
{slashCommandInfo.source}
|
||||
</VSCodeBadge>
|
||||
</div>
|
||||
)}
|
||||
</ToolUseBlockHeader>
|
||||
</ToolUseBlock>
|
||||
</>
|
||||
)
|
||||
}
|
||||
default:
|
||||
return null
|
||||
}
|
||||
case "image":
|
||||
// Parse the JSON to get imageUri and imagePath
|
||||
const imageInfo = safeJsonParse<{ imageUri: string; imagePath: string }>(message.text || "{}")
|
||||
|
|
|
|||
|
|
@ -949,6 +949,7 @@ const ChatViewComponent: React.ForwardRefRenderFunction<ChatViewRef, ChatViewPro
|
|||
"listCodeDefinitionNames",
|
||||
"searchFiles",
|
||||
"codebaseSearch",
|
||||
"runSlashCommand",
|
||||
].includes(tool.tool)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,127 @@
|
|||
import React from "react"
|
||||
import { render } from "@/utils/test-utils"
|
||||
import { describe, it, expect, beforeEach, vi } from "vitest"
|
||||
import { QueryClient, QueryClientProvider } from "@tanstack/react-query"
|
||||
import { ExtensionStateContextProvider } from "@src/context/ExtensionStateContext"
|
||||
import { ChatRowContent } from "../ChatRow"
|
||||
|
||||
// Mock i18n
|
||||
vi.mock("react-i18next", () => ({
|
||||
useTranslation: () => ({
|
||||
t: (key: string) => {
|
||||
const translations: Record<string, string> = {
|
||||
"chat:slashCommand.wantsToRun": "Roo wants to run slash command:",
|
||||
"chat:slashCommand.didRun": "Roo ran slash command:",
|
||||
}
|
||||
return translations[key] || key
|
||||
},
|
||||
}),
|
||||
Trans: ({ i18nKey, children }: { i18nKey: string; children?: React.ReactNode }) => {
|
||||
return <>{children || i18nKey}</>
|
||||
},
|
||||
initReactI18next: {
|
||||
type: "3rdParty",
|
||||
init: () => {},
|
||||
},
|
||||
}))
|
||||
|
||||
// Mock VSCodeBadge
|
||||
vi.mock("@vscode/webview-ui-toolkit/react", () => ({
|
||||
VSCodeBadge: ({ children, ...props }: { children: React.ReactNode }) => <span {...props}>{children}</span>,
|
||||
}))
|
||||
|
||||
const queryClient = new QueryClient()
|
||||
|
||||
const renderChatRowWithProviders = (message: any, isExpanded = false) => {
|
||||
return render(
|
||||
<ExtensionStateContextProvider>
|
||||
<QueryClientProvider client={queryClient}>
|
||||
<ChatRowContent
|
||||
message={message}
|
||||
isExpanded={isExpanded}
|
||||
isLast={false}
|
||||
isStreaming={false}
|
||||
onToggleExpand={mockOnToggleExpand}
|
||||
onSuggestionClick={mockOnSuggestionClick}
|
||||
onBatchFileResponse={mockOnBatchFileResponse}
|
||||
onFollowUpUnmount={mockOnFollowUpUnmount}
|
||||
isFollowUpAnswered={false}
|
||||
/>
|
||||
</QueryClientProvider>
|
||||
</ExtensionStateContextProvider>,
|
||||
)
|
||||
}
|
||||
|
||||
const mockOnToggleExpand = vi.fn()
|
||||
const mockOnSuggestionClick = vi.fn()
|
||||
const mockOnBatchFileResponse = vi.fn()
|
||||
const mockOnFollowUpUnmount = vi.fn()
|
||||
|
||||
describe("ChatRow - runSlashCommand tool", () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("should display runSlashCommand ask message with command only", () => {
|
||||
const message: any = {
|
||||
type: "ask",
|
||||
ask: "tool",
|
||||
ts: Date.now(),
|
||||
text: JSON.stringify({
|
||||
tool: "runSlashCommand",
|
||||
command: "init",
|
||||
}),
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const { getByText } = renderChatRowWithProviders(message)
|
||||
|
||||
expect(getByText("Roo wants to run slash command:")).toBeInTheDocument()
|
||||
expect(getByText("/init")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should display runSlashCommand ask message with command and args", () => {
|
||||
const message: any = {
|
||||
type: "ask",
|
||||
ask: "tool",
|
||||
ts: Date.now(),
|
||||
text: JSON.stringify({
|
||||
tool: "runSlashCommand",
|
||||
command: "test",
|
||||
args: "focus on unit tests",
|
||||
description: "Run project tests",
|
||||
source: "project",
|
||||
}),
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const { getByText } = renderChatRowWithProviders(message, true) // Pass true to expand
|
||||
|
||||
expect(getByText("Roo wants to run slash command:")).toBeInTheDocument()
|
||||
expect(getByText("/test")).toBeInTheDocument()
|
||||
expect(getByText("Arguments:")).toBeInTheDocument()
|
||||
expect(getByText("focus on unit tests")).toBeInTheDocument()
|
||||
expect(getByText("Run project tests")).toBeInTheDocument()
|
||||
expect(getByText("project")).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it("should display runSlashCommand say message", () => {
|
||||
const message: any = {
|
||||
type: "say",
|
||||
say: "tool",
|
||||
ts: Date.now(),
|
||||
text: JSON.stringify({
|
||||
tool: "runSlashCommand",
|
||||
command: "deploy",
|
||||
source: "global",
|
||||
}),
|
||||
partial: false,
|
||||
}
|
||||
|
||||
const { getByText } = renderChatRowWithProviders(message)
|
||||
|
||||
expect(getByText("Roo ran slash command:")).toBeInTheDocument()
|
||||
expect(getByText("/deploy")).toBeInTheDocument()
|
||||
expect(getByText("global")).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
|
|
@ -231,6 +231,7 @@ describe("mergeExtensionState", () => {
|
|||
preventFocusDisruption: false,
|
||||
newTaskRequireTodos: false,
|
||||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
} as Record<ExperimentId, boolean>,
|
||||
}
|
||||
|
||||
|
|
@ -250,6 +251,7 @@ describe("mergeExtensionState", () => {
|
|||
preventFocusDisruption: false,
|
||||
newTaskRequireTodos: false,
|
||||
imageGeneration: false,
|
||||
runSlashCommand: false,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ca/chat.json
generated
4
webview-ui/src/i18n/locales/ca/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Missatges en cua:",
|
||||
"clickToEdit": "Feu clic per editar el missatge"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo vol executar una comanda slash:",
|
||||
"didRun": "Roo ha executat una comanda slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ca/settings.json
generated
4
webview-ui/src/i18n/locales/ca/settings.json
generated
|
|
@ -742,6 +742,10 @@
|
|||
"modelSelectionDescription": "Selecciona el model per a la generació d'imatges",
|
||||
"warningMissingKey": "⚠️ La clau API d'OpenRouter és necessària per a la generació d'imatges. Si us plau, configura-la a dalt.",
|
||||
"successConfigured": "✓ La generació d'imatges està configurada i llesta per utilitzar"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Habilitar comandes de barra diagonal iniciades pel model",
|
||||
"description": "Quan està habilitat, Roo pot executar les vostres comandes de barra diagonal per executar fluxos de treball."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/de/chat.json
generated
4
webview-ui/src/i18n/locales/de/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Warteschlange Nachrichten:",
|
||||
"clickToEdit": "Klicken zum Bearbeiten der Nachricht"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo möchte einen Slash-Befehl ausführen:",
|
||||
"didRun": "Roo hat einen Slash-Befehl ausgeführt:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/de/settings.json
generated
4
webview-ui/src/i18n/locales/de/settings.json
generated
|
|
@ -742,6 +742,10 @@
|
|||
"modelSelectionDescription": "Wähle das Modell für die Bildgenerierung aus",
|
||||
"warningMissingKey": "⚠️ OpenRouter API-Schlüssel ist für Bildgenerierung erforderlich. Bitte konfiguriere ihn oben.",
|
||||
"successConfigured": "✓ Bildgenerierung ist konfiguriert und einsatzbereit"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Modellinitierte Slash-Befehle aktivieren",
|
||||
"description": "Wenn aktiviert, kann Roo deine Slash-Befehle ausführen, um Workflows zu starten."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
|
|
@ -381,6 +381,10 @@
|
|||
"confirm": "Delete"
|
||||
}
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo wants to run a slash command:",
|
||||
"didRun": "Roo ran a slash command:"
|
||||
},
|
||||
"queuedMessages": {
|
||||
"title": "Queued Messages:",
|
||||
"clickToEdit": "Click to edit message"
|
||||
|
|
|
|||
|
|
@ -741,6 +741,10 @@
|
|||
"modelSelectionDescription": "Select the model to use for image generation",
|
||||
"warningMissingKey": "⚠️ OpenRouter API key is required for image generation. Please configure it above.",
|
||||
"successConfigured": "✓ Image generation is configured and ready to use"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Enable model-initiated slash commands",
|
||||
"description": "When enabled, Roo can run your slash commands to execute workflows."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/es/chat.json
generated
4
webview-ui/src/i18n/locales/es/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Mensajes en cola:",
|
||||
"clickToEdit": "Haz clic para editar el mensaje"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo quiere ejecutar un comando slash:",
|
||||
"didRun": "Roo ejecutó un comando slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/es/settings.json
generated
4
webview-ui/src/i18n/locales/es/settings.json
generated
|
|
@ -742,6 +742,10 @@
|
|||
"modelSelectionDescription": "Selecciona el modelo para la generación de imágenes",
|
||||
"warningMissingKey": "⚠️ La clave API de OpenRouter es requerida para la generación de imágenes. Por favor, configúrala arriba.",
|
||||
"successConfigured": "✓ La generación de imágenes está configurada y lista para usar"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Habilitar comandos slash iniciados por el modelo",
|
||||
"description": "Cuando está habilitado, Roo puede ejecutar tus comandos slash para ejecutar flujos de trabajo."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/fr/chat.json
generated
4
webview-ui/src/i18n/locales/fr/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Messages en file d'attente :",
|
||||
"clickToEdit": "Cliquez pour modifier le message"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo veut exécuter une commande slash:",
|
||||
"didRun": "Roo a exécuté une commande slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/fr/settings.json
generated
4
webview-ui/src/i18n/locales/fr/settings.json
generated
|
|
@ -742,6 +742,10 @@
|
|||
"modelSelectionDescription": "Sélectionnez le modèle pour la génération d'images",
|
||||
"warningMissingKey": "⚠️ Une clé API OpenRouter est requise pour la génération d'images. Veuillez la configurer ci-dessus.",
|
||||
"successConfigured": "✓ La génération d'images est configurée et prête à utiliser"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Activer les commandes slash initiées par le modèle",
|
||||
"description": "Lorsque activé, Roo peut exécuter tes commandes slash pour lancer des workflows."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/hi/chat.json
generated
4
webview-ui/src/i18n/locales/hi/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "कतार में संदेश:",
|
||||
"clickToEdit": "संदेश संपादित करने के लिए क्लिक करें"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo एक स्लैश कमांड चलाना चाहता है:",
|
||||
"didRun": "Roo ने एक स्लैश कमांड चलाया:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/hi/settings.json
generated
4
webview-ui/src/i18n/locales/hi/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "छवि निर्माण के लिए उपयोग करने वाला मॉडल चुनें",
|
||||
"warningMissingKey": "⚠️ छवि निर्माण के लिए OpenRouter API कुंजी आवश्यक है। कृपया इसे ऊपर कॉन्फ़िगर करें।",
|
||||
"successConfigured": "✓ छवि निर्माण कॉन्फ़िगर है और उपयोग के लिए तैयार है"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "मॉडल द्वारा शुरू किए गए स्लैश कमांड सक्षम करें",
|
||||
"description": "जब सक्षम होता है, Roo वर्कफ़्लो चलाने के लिए आपके स्लैश कमांड चला सकता है।"
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/id/chat.json
generated
4
webview-ui/src/i18n/locales/id/chat.json
generated
|
|
@ -397,5 +397,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Pesan Antrian:",
|
||||
"clickToEdit": "Klik untuk mengedit pesan"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo ingin menjalankan perintah slash:",
|
||||
"didRun": "Roo telah menjalankan perintah slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/id/settings.json
generated
4
webview-ui/src/i18n/locales/id/settings.json
generated
|
|
@ -772,6 +772,10 @@
|
|||
"modelSelectionDescription": "Pilih model untuk pembuatan gambar",
|
||||
"warningMissingKey": "⚠️ Kunci API OpenRouter diperlukan untuk pembuatan gambar. Silakan konfigurasi di atas.",
|
||||
"successConfigured": "✓ Pembuatan gambar dikonfigurasi dan siap digunakan"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Aktifkan perintah slash yang dimulai model",
|
||||
"description": "Ketika diaktifkan, Roo dapat menjalankan perintah slash Anda untuk mengeksekusi alur kerja."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/it/chat.json
generated
4
webview-ui/src/i18n/locales/it/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Messaggi in coda:",
|
||||
"clickToEdit": "Clicca per modificare il messaggio"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo vuole eseguire un comando slash:",
|
||||
"didRun": "Roo ha eseguito un comando slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/it/settings.json
generated
4
webview-ui/src/i18n/locales/it/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Seleziona il modello per la generazione di immagini",
|
||||
"warningMissingKey": "⚠️ La chiave API OpenRouter è richiesta per la generazione di immagini. Configurala sopra.",
|
||||
"successConfigured": "✓ La generazione di immagini è configurata e pronta per l'uso"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Abilita comandi slash avviati dal modello",
|
||||
"description": "Quando abilitato, Roo può eseguire i tuoi comandi slash per eseguire flussi di lavoro."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ja/chat.json
generated
4
webview-ui/src/i18n/locales/ja/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "キューメッセージ:",
|
||||
"clickToEdit": "クリックしてメッセージを編集"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Rooはスラッシュコマンドを実行したい:",
|
||||
"didRun": "Rooはスラッシュコマンドを実行しました:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ja/settings.json
generated
4
webview-ui/src/i18n/locales/ja/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "画像生成に使用するモデルを選択",
|
||||
"warningMissingKey": "⚠️ 画像生成にはOpenRouter APIキーが必要です。上記で設定してください。",
|
||||
"successConfigured": "✓ 画像生成が設定され、使用準備完了です"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "モデル開始スラッシュコマンドを有効にする",
|
||||
"description": "有効にすると、Rooがワークフローを実行するためにあなたのスラッシュコマンドを実行できます。"
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ko/chat.json
generated
4
webview-ui/src/i18n/locales/ko/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "대기열 메시지:",
|
||||
"clickToEdit": "클릭하여 메시지 편집"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo가 슬래시 명령어를 실행하려고 합니다:",
|
||||
"didRun": "Roo가 슬래시 명령어를 실행했습니다:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ko/settings.json
generated
4
webview-ui/src/i18n/locales/ko/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "이미지 생성에 사용할 모델을 선택하세요",
|
||||
"warningMissingKey": "⚠️ 이미지 생성에는 OpenRouter API 키가 필요합니다. 위에서 설정해주세요.",
|
||||
"successConfigured": "✓ 이미지 생성이 구성되었으며 사용할 준비가 되었습니다"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "모델 시작 슬래시 명령 활성화",
|
||||
"description": "활성화되면 Roo가 워크플로를 실행하기 위해 슬래시 명령을 실행할 수 있습니다."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/nl/chat.json
generated
4
webview-ui/src/i18n/locales/nl/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Berichten in wachtrij:",
|
||||
"clickToEdit": "Klik om bericht te bewerken"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo wil een slash commando uitvoeren:",
|
||||
"didRun": "Roo heeft een slash commando uitgevoerd:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/nl/settings.json
generated
4
webview-ui/src/i18n/locales/nl/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Selecteer het model voor afbeeldingsgeneratie",
|
||||
"warningMissingKey": "⚠️ OpenRouter API-sleutel is vereist voor afbeeldingsgeneratie. Configureer deze hierboven.",
|
||||
"successConfigured": "✓ Afbeeldingsgeneratie is geconfigureerd en klaar voor gebruik"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Model-geïnitieerde slash-commando's inschakelen",
|
||||
"description": "Wanneer ingeschakeld, kan Roo je slash-commando's uitvoeren om workflows uit te voeren."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pl/chat.json
generated
4
webview-ui/src/i18n/locales/pl/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Wiadomości w kolejce:",
|
||||
"clickToEdit": "Kliknij, aby edytować wiadomość"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo chce uruchomić komendę slash:",
|
||||
"didRun": "Roo uruchomił komendę slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pl/settings.json
generated
4
webview-ui/src/i18n/locales/pl/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Wybierz model do generowania obrazów",
|
||||
"warningMissingKey": "⚠️ Klucz API OpenRouter jest wymagany do generowania obrazów. Skonfiguruj go powyżej.",
|
||||
"successConfigured": "✓ Generowanie obrazów jest skonfigurowane i gotowe do użycia"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Włącz polecenia slash inicjowane przez model",
|
||||
"description": "Gdy włączone, Roo może uruchamiać twoje polecenia slash w celu wykonywania przepływów pracy."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
4
webview-ui/src/i18n/locales/pt-BR/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Mensagens na fila:",
|
||||
"clickToEdit": "Clique para editar a mensagem"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo quer executar um comando slash:",
|
||||
"didRun": "Roo executou um comando slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
4
webview-ui/src/i18n/locales/pt-BR/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Selecione o modelo para geração de imagens",
|
||||
"warningMissingKey": "⚠️ A chave de API do OpenRouter é necessária para geração de imagens. Configure-a acima.",
|
||||
"successConfigured": "✓ A geração de imagens está configurada e pronta para uso"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Ativar comandos slash iniciados pelo modelo",
|
||||
"description": "Quando ativado, Roo pode executar seus comandos slash para executar fluxos de trabalho."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ru/chat.json
generated
4
webview-ui/src/i18n/locales/ru/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Сообщения в очереди:",
|
||||
"clickToEdit": "Нажмите, чтобы редактировать сообщение"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo хочет выполнить слеш-команду:",
|
||||
"didRun": "Roo выполнил слеш-команду:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/ru/settings.json
generated
4
webview-ui/src/i18n/locales/ru/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Выберите модель для генерации изображений",
|
||||
"warningMissingKey": "⚠️ API-ключ OpenRouter необходим для генерации изображений. Настройте его выше.",
|
||||
"successConfigured": "✓ Генерация изображений настроена и готова к использованию"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Включить слэш-команды, инициированные моделью",
|
||||
"description": "Когда включено, Roo может выполнять ваши слэш-команды для выполнения рабочих процессов."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/tr/chat.json
generated
4
webview-ui/src/i18n/locales/tr/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Sıradaki Mesajlar:",
|
||||
"clickToEdit": "Mesajı düzenlemek için tıkla"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo bir slash komutu çalıştırmak istiyor:",
|
||||
"didRun": "Roo bir slash komutu çalıştırdı:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/tr/settings.json
generated
4
webview-ui/src/i18n/locales/tr/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Görüntü üretimi için kullanılacak modeli seçin",
|
||||
"warningMissingKey": "⚠️ Görüntü üretimi için OpenRouter API anahtarı gereklidir. Lütfen yukarıda yapılandırın.",
|
||||
"successConfigured": "✓ Görüntü üretimi yapılandırılmış ve kullanıma hazır"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Model tarafından başlatılan slash komutlarını etkinleştir",
|
||||
"description": "Etkinleştirildiğinde, Roo iş akışlarını yürütmek için slash komutlarınızı çalıştırabilir."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/vi/chat.json
generated
4
webview-ui/src/i18n/locales/vi/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "Tin nhắn trong hàng đợi:",
|
||||
"clickToEdit": "Nhấp để chỉnh sửa tin nhắn"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo muốn chạy lệnh slash:",
|
||||
"didRun": "Roo đã chạy lệnh slash:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/vi/settings.json
generated
4
webview-ui/src/i18n/locales/vi/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "Chọn mô hình để sử dụng cho việc tạo hình ảnh",
|
||||
"warningMissingKey": "⚠️ Khóa API OpenRouter là bắt buộc để tạo hình ảnh. Vui lòng cấu hình ở trên.",
|
||||
"successConfigured": "✓ Tạo hình ảnh đã được cấu hình và sẵn sàng sử dụng"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "Bật lệnh slash do mô hình khởi tạo",
|
||||
"description": "Khi được bật, Roo có thể chạy các lệnh slash của bạn để thực hiện các quy trình làm việc."
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
4
webview-ui/src/i18n/locales/zh-CN/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "队列消息:",
|
||||
"clickToEdit": "点击编辑消息"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo 想要运行斜杠命令:",
|
||||
"didRun": "Roo 运行了斜杠命令:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
4
webview-ui/src/i18n/locales/zh-CN/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "选择用于图像生成的模型",
|
||||
"warningMissingKey": "⚠️ 图像生成需要 OpenRouter API 密钥。请在上方配置。",
|
||||
"successConfigured": "✓ 图像生成已配置完成,可以使用"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "启用模型发起的斜杠命令",
|
||||
"description": "启用后 Roo 可运行斜杠命令执行工作流程。"
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
4
webview-ui/src/i18n/locales/zh-TW/chat.json
generated
|
|
@ -391,5 +391,9 @@
|
|||
"queuedMessages": {
|
||||
"title": "佇列中的訊息:",
|
||||
"clickToEdit": "點選以編輯訊息"
|
||||
},
|
||||
"slashCommand": {
|
||||
"wantsToRun": "Roo 想要執行斜線指令:",
|
||||
"didRun": "Roo 執行了斜線指令:"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
4
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
4
webview-ui/src/i18n/locales/zh-TW/settings.json
generated
|
|
@ -743,6 +743,10 @@
|
|||
"modelSelectionDescription": "選擇用於圖像生成的模型",
|
||||
"warningMissingKey": "⚠️ 圖像生成需要 OpenRouter API 金鑰。請在上方設定。",
|
||||
"successConfigured": "✓ 圖像生成已設定完成並準備使用"
|
||||
},
|
||||
"RUN_SLASH_COMMAND": {
|
||||
"name": "啟用模型啟動的斜線命令",
|
||||
"description": "啟用時,Roo 可以執行您的斜線命令來執行工作流程。"
|
||||
}
|
||||
},
|
||||
"promptCaching": {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue