Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/types/src/global-settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ export const globalSettingsSchema = z.object({
terminalZshP10k: z.boolean().optional(),
terminalZdotdir: z.boolean().optional(),
terminalCompressProgressBar: z.boolean().optional(),
windowsScriptExecutionEnabled: z.boolean().optional(),

diagnosticsEnabled: z.boolean().optional(),

Expand Down
17 changes: 15 additions & 2 deletions src/core/prompts/system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,19 @@ async function generatePrompt(
throw new Error("Extension context is required for generating system prompt")
}

const promptSettings: SystemPromptSettings = settings
? { ...settings, isWindows: settings.isWindows ?? os.platform() === "win32" }
: {
maxConcurrentFileReads: 5,
todoListEnabled: true,
browserToolEnabled: true,
useAgentRules: true,
newTaskRequireTodos: false,
toolProtocol: undefined,
windowsScriptExecutionEnabled: true,
isWindows: os.platform() === "win32",
}

// If diff is disabled, don't pass the diffStrategy
const effectiveDiffStrategy = diffEnabled ? diffStrategy : undefined

Expand Down Expand Up @@ -111,7 +124,7 @@ async function generatePrompt(
customModeConfigs,
experiments,
partialReadsEnabled,
settings,
promptSettings,
enableMcpServerCreation,
modelId,
)}`
Expand Down Expand Up @@ -139,7 +152,7 @@ ${getObjectiveSection()}
${await addCustomInstructions(baseInstructions, globalCustomInstructions || "", cwd, mode, {
language: language ?? formatLanguage(vscode.env.language),
rooIgnoreInstructions,
settings,
settings: promptSettings,
})}`

return basePrompt
Expand Down
25 changes: 24 additions & 1 deletion src/core/prompts/tools/execute-command.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
import { ToolArgs } from "./types"

export function getExecuteCommandDescription(args: ToolArgs): string | undefined {
return `## execute_command
const isWindows = args.settings?.isWindows ?? args.isWindows ?? process.platform === "win32"
const scriptModeEnabled = isWindows && (args.settings?.windowsScriptExecutionEnabled ?? true)

const base = `## execute_command
Description: Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Prefer relative commands and paths that avoid location sensitivity for terminal consistency, e.g: \`touch ./testdata/example.file\`, \`dir ./examples/model1/data/yaml\`, or \`go test ./cmd/front --config ./cmd/front/config.yml\`. If directed by the user, you may open a terminal in a different directory by using the \`cwd\` parameter.
Parameters:
- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
Expand All @@ -21,5 +24,25 @@ Example: Requesting to execute ls in a specific directory if directed
<execute_command>
<command>ls -la</command>
<cwd>/home/user/projects</cwd>
</execute_command>`

if (!scriptModeEnabled) {
return base
}

return `${base}

Windows-only script mode for long inputs:
- If the command would exceed Windows command-length limits or is heavy on quotes/percent signs (cmd expands %VAR% and requires %% inside FOR), send the script body in \`script_content\` and the interpreter/shell in \`script_runner\` (for example \`powershell.exe\`, \`cmd.exe\`, \`python\`).
- Roo will write the script to a temporary file, run it with the provided runner, and delete the file automatically. Do not provide filenames, args, or env separately—put everything needed inside the script body.
- Use multi-line bodies (here-strings) instead of one giant inline command to avoid escaping issues, especially with cmd percent signs and PowerShell quotes.

Example: Running a long PowerShell snippet
<execute_command>
<script_content>@'
Write-Output "Hello from script"
Write-Output "Another line without needing to escape % or quotes"
'@</script_content>
<script_runner>powershell.exe</script_runner>
</execute_command>`
}
1 change: 1 addition & 0 deletions src/core/prompts/tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ export function getToolDescriptionsForMode(
modelId,
},
experiments,
isWindows: settings?.isWindows,
}

const tools = new Set<string>()
Expand Down
97 changes: 69 additions & 28 deletions src/core/prompts/tools/native-tools/execute_command.ts
Original file line number Diff line number Diff line change
@@ -1,44 +1,85 @@
import type OpenAI from "openai"

const EXECUTE_COMMAND_DESCRIPTION = `Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Prefer relative commands and paths that avoid location sensitivity for terminal consistency.
const buildDescription = (enableScriptMode: boolean) => `Request to execute a CLI command on the system. Use this when you need to perform system operations or run specific commands to accomplish any step in the user's task. You must tailor your command to the user's system and provide a clear explanation of what the command does. For command chaining, use the appropriate chaining syntax for the user's shell. Prefer to execute complex CLI commands over creating executable scripts, as they are more flexible and easier to run. Prefer relative commands and paths that avoid location sensitivity for terminal consistency.${
enableScriptMode
? " On Windows you can also provide a script body when the command would be too long or hard to escape (percent signs in cmd, nested quotes, here-strings): supply the script text via script_content and the interpreter/shell via script_runner; Roo will write it to a temporary file, run it, and remove it."
: ""
}
Parameters:
- command: (required) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
- cwd: (optional) The working directory to execute the command in
- command: (required unless script_content is used) The CLI command to execute. This should be valid for the current operating system. Ensure the command is properly formatted and does not contain any harmful instructions.
- cwd: (optional) The working directory to execute the command in${
enableScriptMode
? `
- script_content: (Windows-only, optional) The full text of the script to run when the command would be too long or requires heavy quoting. Do not pass arguments separately—put them in the script. Use multi-line text (here-strings) to avoid escaping percent signs and quotes.
- script_runner: (Windows-only, optional) The interpreter or shell to execute the temporary script file (e.g., powershell.exe, cmd.exe, python).`
: ""
}
Example: Executing npm run dev
{ "command": "npm run dev", "cwd": null }
Example: Executing ls in a specific directory if directed
{ "command": "ls -la", "cwd": "/home/user/projects" }
{ "command": "ls -la", "cwd": "/home/user/projects" }${
enableScriptMode
? `
Example: Using relative paths
{ "command": "touch ./testdata/example.file", "cwd": null }`
Example: Long script executed via temporary file on Windows
{ "script_content": "@'
Write-Output \"Hello\"
Write-Output \"Handles %PATH% safely\"
'@", "script_runner": "powershell.exe" }`
: ""
}`

const COMMAND_PARAMETER_DESCRIPTION = `Shell command to execute`

const CWD_PARAMETER_DESCRIPTION = `Optional working directory for the command, relative or absolute`
const SCRIPT_CONTENT_DESCRIPTION = `Windows-only: script body to write into a temporary file when the command is too long or hard to escape (percent signs, nested quotes)`
const SCRIPT_RUNNER_DESCRIPTION = `Windows-only: program/shell that should run the temporary script file (e.g., powershell.exe, cmd.exe, python)`

export function getExecuteCommandTool(options: { enableScriptMode: boolean }): OpenAI.Chat.ChatCompletionTool {
const properties: Record<string, any> = {
command: {
type: "string",
description: COMMAND_PARAMETER_DESCRIPTION,
},
cwd: {
type: ["string", "null"],
description: CWD_PARAMETER_DESCRIPTION,
},
}

if (options.enableScriptMode) {
properties["script_content"] = {
type: "string",
description: SCRIPT_CONTENT_DESCRIPTION,
}
properties["script_runner"] = {
type: "string",
description: SCRIPT_RUNNER_DESCRIPTION,
}
}

export default {
type: "function",
function: {
name: "execute_command",
description: EXECUTE_COMMAND_DESCRIPTION,
strict: true,
parameters: {
type: "object",
properties: {
command: {
type: "string",
description: COMMAND_PARAMETER_DESCRIPTION,
},
cwd: {
type: ["string", "null"],
description: CWD_PARAMETER_DESCRIPTION,
},
return {
type: "function",
function: {
name: "execute_command",
description: buildDescription(options.enableScriptMode),
strict: true,
parameters: {
type: "object",
properties,
required: options.enableScriptMode ? [] : ["command"],
additionalProperties: false,
...(options.enableScriptMode
? {
oneOf: [
{ required: ["command"] },
{ required: ["script_content", "script_runner"] },
],
}
: {}),
},
required: ["command", "cwd"],
additionalProperties: false,
},
},
} satisfies OpenAI.Chat.ChatCompletionTool
} satisfies OpenAI.Chat.ChatCompletionTool
}
10 changes: 8 additions & 2 deletions src/core/prompts/tools/native-tools/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import askFollowupQuestion from "./ask_followup_question"
import attemptCompletion from "./attempt_completion"
import browserAction from "./browser_action"
import codebaseSearch from "./codebase_search"
import executeCommand from "./execute_command"
import { getExecuteCommandTool } from "./execute_command"
import fetchInstructions from "./fetch_instructions"
import generateImage from "./generate_image"
import listCodeDefinitionNames from "./list_code_definition_names"
Expand All @@ -27,9 +27,15 @@ export { convertOpenAIToolToAnthropic, convertOpenAIToolsToAnthropic } from "./c
* Get native tools array, optionally customizing based on settings.
*
* @param partialReadsEnabled - Whether to include line_ranges support in read_file tool (default: true)
* @param options - Optional flags (e.g., enable Windows script mode for execute_command)
* @returns Array of native tool definitions
*/
export function getNativeTools(partialReadsEnabled: boolean = true): OpenAI.Chat.ChatCompletionTool[] {
export function getNativeTools(
partialReadsEnabled: boolean = true,
options: { windowsScriptModeEnabled?: boolean } = {},
): OpenAI.Chat.ChatCompletionTool[] {
const executeCommand = getExecuteCommandTool({ enableScriptMode: options.windowsScriptModeEnabled ?? false })

return [
accessMcpResource,
apply_diff,
Expand Down
1 change: 1 addition & 0 deletions src/core/prompts/tools/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ export type ToolArgs = {
partialReadsEnabled?: boolean
settings?: Record<string, any>
experiments?: Record<string, boolean>
isWindows?: boolean
}
2 changes: 2 additions & 0 deletions src/core/prompts/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,4 +12,6 @@ export interface SystemPromptSettings {
toolProtocol?: ToolProtocol
/** When true, model should hide vendor/company identity in responses */
isStealthModel?: boolean
windowsScriptExecutionEnabled?: boolean
isWindows?: boolean
}
3 changes: 3 additions & 0 deletions src/core/task/Task.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3323,6 +3323,8 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
.get<boolean>("newTaskRequireTodos", false),
toolProtocol,
isStealthModel: modelInfo?.isStealthModel,
windowsScriptExecutionEnabled: state?.windowsScriptExecutionEnabled ?? true,
isWindows: process.platform === "win32",
},
undefined, // todoList
this.api.getModel().id,
Expand Down Expand Up @@ -3659,6 +3661,7 @@ export class Task extends EventEmitter<TaskEvents> implements TaskLike {
maxReadFileLine: state?.maxReadFileLine ?? -1,
browserToolEnabled: state?.browserToolEnabled ?? true,
modelInfo,
windowsScriptExecutionEnabled: state?.windowsScriptExecutionEnabled ?? true,
})
}

Expand Down
5 changes: 4 additions & 1 deletion src/core/task/build-tools.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ interface BuildToolsOptions {
maxReadFileLine: number
browserToolEnabled: boolean
modelInfo?: ModelInfo
windowsScriptExecutionEnabled?: boolean
}

/**
Expand Down Expand Up @@ -53,7 +54,9 @@ export async function buildNativeToolsArray(options: BuildToolsOptions): Promise
const partialReadsEnabled = maxReadFileLine !== -1

// Build native tools with dynamic read_file tool based on partialReadsEnabled
const nativeTools = getNativeTools(partialReadsEnabled)
const nativeTools = getNativeTools(partialReadsEnabled, {
windowsScriptModeEnabled: process.platform === "win32" && (options.windowsScriptExecutionEnabled ?? true),
})

// Filter native tools based on mode restrictions
const filteredNativeTools = filterNativeToolsForMode(
Expand Down
Loading
Loading