From bb59ca8b24adcc46d0d52975ada160849808b4b4 Mon Sep 17 00:00:00 2001 From: Aishanee Shah Date: Fri, 6 Feb 2026 20:15:21 +0000 Subject: [PATCH] refactor: centralize all tool definitions in coreTools.ts --- .../core/src/tools/definitions/coreTools.ts | 96 +++++++++++++------ packages/core/src/tools/shell.ts | 55 ++--------- 2 files changed, 75 insertions(+), 76 deletions(-) diff --git a/packages/core/src/tools/definitions/coreTools.ts b/packages/core/src/tools/definitions/coreTools.ts index 9678ab81d3..4a585ebd1f 100644 --- a/packages/core/src/tools/definitions/coreTools.ts +++ b/packages/core/src/tools/definitions/coreTools.ts @@ -7,7 +7,41 @@ import { Type } from '@google/genai'; import type { ToolDefinition } from './types.js'; import { READ_FILE_TOOL_NAME, SHELL_TOOL_NAME } from '../tool-names.js'; -import { getCommandDescription } from '../shell.js'; +import * as os from 'node:os'; + +export function getShellToolDescription( + enableInteractiveShell: boolean, +): string { + const returnedInfo = ` + + The following information is returned: + + Output: Combined stdout/stderr. Can be \`(empty)\` or partial on error and for any unwaited background processes. + Exit Code: Only included if non-zero (command failed). + Error: Only included if a process-level error occurred (e.g., spawn failure). + Signal: Only included if process was terminated by a signal. + Background PIDs: Only included if background processes were started. + Process Group PGID: Only included if available.`; + + if (os.platform() === 'win32') { + const backgroundInstructions = enableInteractiveShell + ? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use PowerShell background constructs.' + : 'Command can start background processes using PowerShell constructs such as `Start-Process -NoNewWindow` or `Start-Job`.'; + return `This tool executes a given shell command as \`powershell.exe -NoProfile -Command \`. ${backgroundInstructions}${returnedInfo}`; + } else { + const backgroundInstructions = enableInteractiveShell + ? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use `&` to background commands.' + : 'Command can start background processes using `&`.'; + return `This tool executes a given shell command as \`bash -c \`. ${backgroundInstructions} Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.${returnedInfo}`; + } +} + +export function getCommandDescription(): string { + if (os.platform() === 'win32') { + return 'Exact command to execute as `powershell.exe -NoProfile -Command `'; + } + return 'Exact bash command to execute as `bash -c `'; +} export const READ_FILE_DEFINITION: ToolDefinition = { base: { @@ -36,34 +70,38 @@ export const READ_FILE_DEFINITION: ToolDefinition = { }, }; -export const SHELL_DEFINITION: ToolDefinition = { - base: { - name: SHELL_TOOL_NAME, - description: 'Executes a shell command.', - parameters: { - type: Type.OBJECT, - properties: { - command: { - type: Type.STRING, - description: getCommandDescription(), - }, - description: { - type: Type.STRING, - description: - 'Brief description of the command for the user. Be specific and concise. Ideally a single sentence. Can be up to 3 sentences for clarity. No line breaks.', - }, - dir_path: { - type: Type.STRING, - description: - '(OPTIONAL) The path of the directory to run the command in. If not provided, the project root directory is used. Must be a directory within the workspace and must already exist.', - }, - is_background: { - type: Type.BOOLEAN, - description: - 'Set to true if this command should be run in the background (e.g. for long-running servers or watchers). The command will be started, allowed to run for a brief moment to check for immediate errors, and then moved to the background.', +export function getShellDefinition( + enableInteractiveShell: boolean, +): ToolDefinition { + return { + base: { + name: SHELL_TOOL_NAME, + description: getShellToolDescription(enableInteractiveShell), + parameters: { + type: Type.OBJECT, + properties: { + command: { + type: Type.STRING, + description: getCommandDescription(), + }, + description: { + type: Type.STRING, + description: + 'Brief description of the command for the user. Be specific and concise. Ideally a single sentence. Can be up to 3 sentences for clarity. No line breaks.', + }, + dir_path: { + type: Type.STRING, + description: + '(OPTIONAL) The path of the directory to run the command in. If not provided, the project root directory is used. Must be a directory within the workspace and must already exist.', + }, + is_background: { + type: Type.BOOLEAN, + description: + 'Set to true if this command should be run in the background (e.g. for long-running servers or watchers). The command will be started, allowed to run for a brief moment to check for immediate errors, and then moved to the background.', + }, }, + required: ['command'], }, - required: ['command'], }, - }, -}; + }; +} diff --git a/packages/core/src/tools/shell.ts b/packages/core/src/tools/shell.ts index 87c8912fde..38e58f85c9 100644 --- a/packages/core/src/tools/shell.ts +++ b/packages/core/src/tools/shell.ts @@ -43,7 +43,7 @@ import { } from '../utils/shell-utils.js'; import { SHELL_TOOL_NAME } from './tool-names.js'; import type { MessageBus } from '../confirmation-bus/message-bus.js'; -import { SHELL_DEFINITION } from './definitions/coreTools.js'; +import { getShellDefinition } from './definitions/coreTools.js'; import { resolveToolDeclaration } from './definitions/resolver.js'; export const OUTPUT_UPDATE_INTERVAL_MS = 1000; @@ -453,41 +453,6 @@ export class ShellToolInvocation extends BaseToolInvocation< } } -export function getShellToolDescription( - enableInteractiveShell: boolean, -): string { - const returnedInfo = ` - - The following information is returned: - - Output: Combined stdout/stderr. Can be \`(empty)\` or partial on error and for any unwaited background processes. - Exit Code: Only included if non-zero (command failed). - Error: Only included if a process-level error occurred (e.g., spawn failure). - Signal: Only included if process was terminated by a signal. - Background PIDs: Only included if background processes were started. - Process Group PGID: Only included if available.`; - - if (os.platform() === 'win32') { - const backgroundInstructions = enableInteractiveShell - ? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use PowerShell background constructs.' - : 'Command can start background processes using PowerShell constructs such as `Start-Process -NoNewWindow` or `Start-Job`.'; - return `This tool executes a given shell command as \`powershell.exe -NoProfile -Command \`. ${backgroundInstructions}${returnedInfo}`; - } else { - const backgroundInstructions = enableInteractiveShell - ? 'To run a command in the background, set the `is_background` parameter to true. Do NOT use `&` to background commands.' - : 'Command can start background processes using `&`.'; - return `This tool executes a given shell command as \`bash -c \`. ${backgroundInstructions} Command is executed as a subprocess that leads its own process group. Command process group can be terminated as \`kill -- -PGID\` or signaled as \`kill -s SIGNAL -- -PGID\`.${returnedInfo}`; - } -} - -export function getCommandDescription(): string { - if (os.platform() === 'win32') { - return 'Exact command to execute as `powershell.exe -NoProfile -Command `'; - } else { - return 'Exact bash command to execute as `bash -c `'; - } -} - export class ShellTool extends BaseDeclarativeTool< ShellToolParams, ToolResult @@ -501,12 +466,13 @@ export class ShellTool extends BaseDeclarativeTool< void initializeShellParsers().catch(() => { // Errors are surfaced when parsing commands. }); + const definition = getShellDefinition(config.getEnableInteractiveShell()); super( ShellTool.Name, 'Shell', - getShellToolDescription(config.getEnableInteractiveShell()), + definition.base.description!, Kind.Execute, - SHELL_DEFINITION.base.parameters!, + definition.base.parameters!, messageBus, false, // output is not markdown true, // output can be updated @@ -546,18 +512,13 @@ export class ShellTool extends BaseDeclarativeTool< } override getSchema(modelId?: string) { + const definition = getShellDefinition( + this.config.getEnableInteractiveShell(), + ); if (!modelId) { return super.getSchema(); } - const declaration = resolveToolDeclaration(SHELL_DEFINITION, modelId); - const schema = super.getSchema(); - - // We use the resolved declaration but preserve the dynamic platform-specific description - // from the tool instance to ensure behavior remains 100% identical for now. - return { - ...declaration, - description: schema.description, - }; + return resolveToolDeclaration(definition, modelId); } }