From c8518d6a630b182e4c6e90819b26ca19089ee395 Mon Sep 17 00:00:00 2001 From: Abhi <43648792+abhipatel12@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:53:53 -0400 Subject: [PATCH] refactor(tools): Move all tool names into tool-names.ts (#11493) --- packages/cli/src/config/config.test.ts | 38 +++++++------ packages/cli/src/config/config.ts | 4 +- packages/cli/src/config/policy.ts | 22 ++++---- .../core/src/agents/codebase-investigator.ts | 4 +- packages/core/src/agents/executor.test.ts | 56 ++++++++++--------- packages/core/src/agents/executor.ts | 8 +-- packages/core/src/core/prompts.ts | 10 ++-- packages/core/src/tools/edit.ts | 5 +- packages/core/src/tools/glob.ts | 4 +- packages/core/src/tools/ls.ts | 5 +- packages/core/src/tools/memoryTool.ts | 6 +- packages/core/src/tools/smart-edit.ts | 4 +- packages/core/src/tools/tool-names.ts | 6 +- packages/core/src/tools/web-fetch.ts | 4 +- packages/core/src/tools/web-search.ts | 4 +- packages/core/src/tools/write-file.ts | 6 +- packages/core/src/tools/write-todos.ts | 4 +- 17 files changed, 90 insertions(+), 100 deletions(-) diff --git a/packages/cli/src/config/config.test.ts b/packages/cli/src/config/config.test.ts index 69d6502736..e3b0d7a793 100644 --- a/packages/cli/src/config/config.test.ts +++ b/packages/cli/src/config/config.test.ts @@ -8,13 +8,13 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; import * as os from 'node:os'; import * as path from 'node:path'; import { - EditTool, - WriteFileTool, DEFAULT_GEMINI_MODEL, DEFAULT_GEMINI_MODEL_AUTO, OutputFormat, type GeminiCLIExtension, SHELL_TOOL_NAME, + WRITE_FILE_TOOL_NAME, + EDIT_TOOL_NAME, } from '@google/gemini-cli-core'; import { loadCliConfig, parseArguments, type CliArgs } from './config.js'; import type { Settings } from './settings.js'; @@ -739,7 +739,11 @@ describe('mergeMcpServers', () => { }); describe('mergeExcludeTools', () => { - const defaultExcludes = [SHELL_TOOL_NAME, EditTool.Name, WriteFileTool.Name]; + const defaultExcludes = [ + SHELL_TOOL_NAME, + EDIT_TOOL_NAME, + WRITE_FILE_TOOL_NAME, + ]; const originalIsTTY = process.stdin.isTTY; beforeEach(() => { @@ -981,8 +985,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).toContain(SHELL_TOOL_NAME); - expect(excludedTools).toContain(EditTool.Name); - expect(excludedTools).toContain(WriteFileTool.Name); + expect(excludedTools).toContain(EDIT_TOOL_NAME); + expect(excludedTools).toContain(WRITE_FILE_TOOL_NAME); }); it('should exclude all interactive tools in non-interactive mode with explicit default approval mode', async () => { @@ -1008,8 +1012,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).toContain(SHELL_TOOL_NAME); - expect(excludedTools).toContain(EditTool.Name); - expect(excludedTools).toContain(WriteFileTool.Name); + expect(excludedTools).toContain(EDIT_TOOL_NAME); + expect(excludedTools).toContain(WRITE_FILE_TOOL_NAME); }); it('should exclude only shell tools in non-interactive mode with auto_edit approval mode', async () => { @@ -1035,8 +1039,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).toContain(SHELL_TOOL_NAME); - expect(excludedTools).not.toContain(EditTool.Name); - expect(excludedTools).not.toContain(WriteFileTool.Name); + expect(excludedTools).not.toContain(EDIT_TOOL_NAME); + expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); }); it('should exclude no interactive tools in non-interactive mode with yolo approval mode', async () => { @@ -1062,8 +1066,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).not.toContain(SHELL_TOOL_NAME); - expect(excludedTools).not.toContain(EditTool.Name); - expect(excludedTools).not.toContain(WriteFileTool.Name); + expect(excludedTools).not.toContain(EDIT_TOOL_NAME); + expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); }); it('should exclude no interactive tools in non-interactive mode with legacy yolo flag', async () => { @@ -1082,8 +1086,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).not.toContain(SHELL_TOOL_NAME); - expect(excludedTools).not.toContain(EditTool.Name); - expect(excludedTools).not.toContain(WriteFileTool.Name); + expect(excludedTools).not.toContain(EDIT_TOOL_NAME); + expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); }); it('should not exclude interactive tools in interactive mode regardless of approval mode', async () => { @@ -1113,8 +1117,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).not.toContain(SHELL_TOOL_NAME); - expect(excludedTools).not.toContain(EditTool.Name); - expect(excludedTools).not.toContain(WriteFileTool.Name); + expect(excludedTools).not.toContain(EDIT_TOOL_NAME); + expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); } }); @@ -1142,8 +1146,8 @@ describe('Approval mode tool exclusion logic', () => { const excludedTools = config.getExcludeTools(); expect(excludedTools).toContain('custom_tool'); // From settings expect(excludedTools).toContain(SHELL_TOOL_NAME); // From approval mode - expect(excludedTools).not.toContain(EditTool.Name); // Should be allowed in auto_edit - expect(excludedTools).not.toContain(WriteFileTool.Name); // Should be allowed in auto_edit + expect(excludedTools).not.toContain(EDIT_TOOL_NAME); // Should be allowed in auto_edit + expect(excludedTools).not.toContain(WRITE_FILE_TOOL_NAME); // Should be allowed in auto_edit }); it('should throw an error for invalid approval mode values in loadCliConfig', async () => { diff --git a/packages/cli/src/config/config.ts b/packages/cli/src/config/config.ts index 0c13dd4b3a..79cb519ab1 100755 --- a/packages/cli/src/config/config.ts +++ b/packages/cli/src/config/config.ts @@ -29,13 +29,13 @@ import { DEFAULT_GEMINI_EMBEDDING_MODEL, DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, FileDiscoveryService, - EditTool, WRITE_FILE_TOOL_NAME, SHELL_TOOL_NAMES, SHELL_TOOL_NAME, resolveTelemetrySettings, FatalConfigError, getPty, + EDIT_TOOL_NAME, } from '@google/gemini-cli-core'; import type { Settings } from './settings.js'; @@ -506,7 +506,7 @@ export async function loadCliConfig( if (!interactive && !argv.experimentalAcp) { const defaultExcludes = [ SHELL_TOOL_NAME, - EditTool.Name, + EDIT_TOOL_NAME, WRITE_FILE_TOOL_NAME, ]; const autoEditExcludes = [SHELL_TOOL_NAME]; diff --git a/packages/cli/src/config/policy.ts b/packages/cli/src/config/policy.ts index 63c0fee5a1..75e9b4315a 100644 --- a/packages/cli/src/config/policy.ts +++ b/packages/cli/src/config/policy.ts @@ -10,30 +10,30 @@ import { type PolicyRule, ApprovalMode, // Read-only tools - GlobTool, - LSTool, GREP_TOOL_NAME, + LS_TOOL_NAME, READ_MANY_FILES_TOOL_NAME, READ_FILE_TOOL_NAME, // Write tools - EditTool, - MemoryTool, SHELL_TOOL_NAME, WRITE_FILE_TOOL_NAME, WEB_FETCH_TOOL_NAME, - WebSearchTool, + GLOB_TOOL_NAME, + EDIT_TOOL_NAME, + MEMORY_TOOL_NAME, + WEB_SEARCH_TOOL_NAME, } from '@google/gemini-cli-core'; import type { Settings } from './settings.js'; // READ_ONLY_TOOLS is a list of built-in tools that do not modify the user's // files or system state. const READ_ONLY_TOOLS = new Set([ - GlobTool.Name, + GLOB_TOOL_NAME, GREP_TOOL_NAME, - LSTool.Name, + LS_TOOL_NAME, READ_FILE_TOOL_NAME, READ_MANY_FILES_TOOL_NAME, - WebSearchTool.Name, + WEB_SEARCH_TOOL_NAME, ]); // WRITE_TOOLS is a list of built-in tools that can modify the user's files or @@ -43,8 +43,8 @@ const READ_ONLY_TOOLS = new Set([ // any tool that isn't read only will require a confirmation unless altered by // config and policy. const WRITE_TOOLS = new Set([ - EditTool.Name, - MemoryTool.Name, + EDIT_TOOL_NAME, + MEMORY_TOOL_NAME, SHELL_TOOL_NAME, WRITE_FILE_TOOL_NAME, WEB_FETCH_TOOL_NAME, @@ -168,7 +168,7 @@ export function createPolicyEngineConfig( }); } else if (approvalMode === ApprovalMode.AUTO_EDIT) { rules.push({ - toolName: EditTool.Name, + toolName: EDIT_TOOL_NAME, decision: PolicyDecision.ALLOW, priority: 15, // Higher than write tools (10) to override ASK_USER }); diff --git a/packages/core/src/agents/codebase-investigator.ts b/packages/core/src/agents/codebase-investigator.ts index 5195ada239..94181205db 100644 --- a/packages/core/src/agents/codebase-investigator.ts +++ b/packages/core/src/agents/codebase-investigator.ts @@ -5,10 +5,10 @@ */ import type { AgentDefinition } from './types.js'; -import { LSTool } from '../tools/ls.js'; import { GLOB_TOOL_NAME, GREP_TOOL_NAME, + LS_TOOL_NAME, READ_FILE_TOOL_NAME, } from '../tools/tool-names.js'; import { DEFAULT_GEMINI_MODEL } from '../config/models.js'; @@ -82,7 +82,7 @@ export const CodebaseInvestigatorAgent: AgentDefinition< toolConfig: { // Grant access only to read-only tools. - tools: [LSTool.Name, READ_FILE_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME], + tools: [LS_TOOL_NAME, READ_FILE_TOOL_NAME, GLOB_TOOL_NAME, GREP_TOOL_NAME], }, promptConfig: { diff --git a/packages/core/src/agents/executor.test.ts b/packages/core/src/agents/executor.test.ts index c062edb703..8395ff9f50 100644 --- a/packages/core/src/agents/executor.test.ts +++ b/packages/core/src/agents/executor.test.ts @@ -9,7 +9,7 @@ import { AgentExecutor, type ActivityCallback } from './executor.js'; import { makeFakeConfig } from '../test-utils/config.js'; import { ToolRegistry } from '../tools/tool-registry.js'; import { LSTool } from '../tools/ls.js'; -import { READ_FILE_TOOL_NAME } from '../tools/tool-names.js'; +import { LS_TOOL_NAME, READ_FILE_TOOL_NAME } from '../tools/tool-names.js'; import { GeminiChat, StreamEventType, @@ -146,7 +146,7 @@ let parentToolRegistry: ToolRegistry; * Type-safe helper to create agent definitions for tests. */ const createTestDefinition = ( - tools: Array = [LSTool.Name], + tools: Array = [LS_TOOL_NAME], runConfigOverrides: Partial['runConfig']> = {}, outputConfigMode: 'default' | 'none' = 'default', schema: TOutput = z.string() as unknown as TOutput, @@ -227,7 +227,7 @@ describe('AgentExecutor', () => { describe('create (Initialization and Validation)', () => { it('should create successfully with allowed tools', async () => { - const definition = createTestDefinition([LSTool.Name]); + const definition = createTestDefinition([LS_TOOL_NAME]); const executor = await AgentExecutor.create( definition, mockConfig, @@ -245,7 +245,7 @@ describe('AgentExecutor', () => { it('should create an isolated ToolRegistry for the agent', async () => { const definition = createTestDefinition([ - LSTool.Name, + LS_TOOL_NAME, READ_FILE_TOOL_NAME, ]); const executor = await AgentExecutor.create( @@ -258,7 +258,7 @@ describe('AgentExecutor', () => { expect(agentRegistry).not.toBe(parentToolRegistry); expect(agentRegistry.getAllToolNames()).toEqual( - expect.arrayContaining([LSTool.Name, READ_FILE_TOOL_NAME]), + expect.arrayContaining([LS_TOOL_NAME, READ_FILE_TOOL_NAME]), ); expect(agentRegistry.getAllToolNames()).toHaveLength(2); expect(agentRegistry.getTool(MOCK_TOOL_NOT_ALLOWED.name)).toBeUndefined(); @@ -320,14 +320,14 @@ describe('AgentExecutor', () => { // Turn 1: Model calls ls mockModelResponse( - [{ name: LSTool.Name, args: { path: '.' }, id: 'call1' }], + [{ name: LS_TOOL_NAME, args: { path: '.' }, id: 'call1' }], 'T1: Listing', ); mockExecuteToolCall.mockResolvedValueOnce({ status: 'success', request: { callId: 'call1', - name: LSTool.Name, + name: LS_TOOL_NAME, args: { path: '.' }, isClientInitiated: false, prompt_id: 'test-prompt', @@ -340,7 +340,7 @@ describe('AgentExecutor', () => { responseParts: [ { functionResponse: { - name: LSTool.Name, + name: LS_TOOL_NAME, response: { result: 'file1.txt' }, id: 'call1', }, @@ -390,7 +390,7 @@ describe('AgentExecutor', () => { expect(sentTools).toEqual( expect.arrayContaining([ - expect.objectContaining({ name: LSTool.Name }), + expect.objectContaining({ name: LS_TOOL_NAME }), expect.objectContaining({ name: TASK_COMPLETE_TOOL_NAME }), ]), ); @@ -439,7 +439,7 @@ describe('AgentExecutor', () => { }), expect.objectContaining({ type: 'TOOL_CALL_END', - data: { name: LSTool.Name, output: 'file1.txt' }, + data: { name: LS_TOOL_NAME, output: 'file1.txt' }, }), expect.objectContaining({ type: 'TOOL_CALL_START', @@ -460,7 +460,7 @@ describe('AgentExecutor', () => { }); it('should execute successfully when model calls complete_task without output (Happy Path No Output)', async () => { - const definition = createTestDefinition([LSTool.Name], {}, 'none'); + const definition = createTestDefinition([LS_TOOL_NAME], {}, 'none'); const executor = await AgentExecutor.create( definition, mockConfig, @@ -468,13 +468,13 @@ describe('AgentExecutor', () => { ); mockModelResponse([ - { name: LSTool.Name, args: { path: '.' }, id: 'call1' }, + { name: LS_TOOL_NAME, args: { path: '.' }, id: 'call1' }, ]); mockExecuteToolCall.mockResolvedValueOnce({ status: 'success', request: { callId: 'call1', - name: LSTool.Name, + name: LS_TOOL_NAME, args: { path: '.' }, isClientInitiated: false, prompt_id: 'test-prompt', @@ -487,7 +487,7 @@ describe('AgentExecutor', () => { responseParts: [ { functionResponse: { - name: LSTool.Name, + name: LS_TOOL_NAME, response: {}, id: 'call1', }, @@ -540,13 +540,13 @@ describe('AgentExecutor', () => { ); mockModelResponse([ - { name: LSTool.Name, args: { path: '.' }, id: 'call1' }, + { name: LS_TOOL_NAME, args: { path: '.' }, id: 'call1' }, ]); mockExecuteToolCall.mockResolvedValueOnce({ status: 'success', request: { callId: 'call1', - name: LSTool.Name, + name: LS_TOOL_NAME, args: { path: '.' }, isClientInitiated: false, prompt_id: 'test-prompt', @@ -559,7 +559,7 @@ describe('AgentExecutor', () => { responseParts: [ { functionResponse: { - name: LSTool.Name, + name: LS_TOOL_NAME, response: {}, id: 'call1', }, @@ -700,7 +700,7 @@ describe('AgentExecutor', () => { }); it('should execute parallel tool calls and then complete', async () => { - const definition = createTestDefinition([LSTool.Name]); + const definition = createTestDefinition([LS_TOOL_NAME]); const executor = await AgentExecutor.create( definition, mockConfig, @@ -708,12 +708,12 @@ describe('AgentExecutor', () => { ); const call1: FunctionCall = { - name: LSTool.Name, + name: LS_TOOL_NAME, args: { path: '/a' }, id: 'c1', }; const call2: FunctionCall = { - name: LSTool.Name, + name: LS_TOOL_NAME, args: { path: '/b' }, id: 'c2', }; @@ -795,7 +795,7 @@ describe('AgentExecutor', () => { }); it('SECURITY: should block unauthorized tools and provide explicit failure to model', async () => { - const definition = createTestDefinition([LSTool.Name]); + const definition = createTestDefinition([LS_TOOL_NAME]); const executor = await AgentExecutor.create( definition, mockConfig, @@ -867,12 +867,12 @@ describe('AgentExecutor', () => { describe('run (Termination Conditions)', () => { const mockWorkResponse = (id: string) => { - mockModelResponse([{ name: LSTool.Name, args: { path: '.' }, id }]); + mockModelResponse([{ name: LS_TOOL_NAME, args: { path: '.' }, id }]); mockExecuteToolCall.mockResolvedValueOnce({ status: 'success', request: { callId: id, - name: LSTool.Name, + name: LS_TOOL_NAME, args: { path: '.' }, isClientInitiated: false, prompt_id: 'test-prompt', @@ -883,7 +883,7 @@ describe('AgentExecutor', () => { callId: id, resultDisplay: 'ok', responseParts: [ - { functionResponse: { name: LSTool.Name, response: {}, id } }, + { functionResponse: { name: LS_TOOL_NAME, response: {}, id } }, ], error: undefined, errorType: undefined, @@ -894,7 +894,7 @@ describe('AgentExecutor', () => { it('should terminate when max_turns is reached', async () => { const MAX = 2; - const definition = createTestDefinition([LSTool.Name], { + const definition = createTestDefinition([LS_TOOL_NAME], { max_turns: MAX, }); const executor = await AgentExecutor.create(definition, mockConfig); @@ -909,12 +909,14 @@ describe('AgentExecutor', () => { }); it('should terminate if timeout is reached', async () => { - const definition = createTestDefinition([LSTool.Name], { + const definition = createTestDefinition([LS_TOOL_NAME], { max_time_minutes: 1, }); const executor = await AgentExecutor.create(definition, mockConfig); - mockModelResponse([{ name: LSTool.Name, args: { path: '.' }, id: 't1' }]); + mockModelResponse([ + { name: LS_TOOL_NAME, args: { path: '.' }, id: 't1' }, + ]); // Long running tool mockExecuteToolCall.mockImplementationOnce(async (_ctx, reqInfo) => { diff --git a/packages/core/src/agents/executor.ts b/packages/core/src/agents/executor.ts index f7c54742f7..6dab56973d 100644 --- a/packages/core/src/agents/executor.ts +++ b/packages/core/src/agents/executor.ts @@ -20,11 +20,11 @@ import { executeToolCall } from '../core/nonInteractiveToolExecutor.js'; import { ToolRegistry } from '../tools/tool-registry.js'; import type { ToolCallRequestInfo } from '../core/turn.js'; import { getDirectoryContextString } from '../utils/environmentContext.js'; -import { LSTool } from '../tools/ls.js'; -import { MemoryTool } from '../tools/memoryTool.js'; import { GLOB_TOOL_NAME, GREP_TOOL_NAME, + LS_TOOL_NAME, + MEMORY_TOOL_NAME, READ_FILE_TOOL_NAME, READ_MANY_FILES_TOOL_NAME, WEB_SEARCH_TOOL_NAME, @@ -710,12 +710,12 @@ Important Rules: // Tools that are non-interactive. This is temporary until we have tool // confirmations for subagents. const allowlist = new Set([ - LSTool.Name, + LS_TOOL_NAME, READ_FILE_TOOL_NAME, GREP_TOOL_NAME, GLOB_TOOL_NAME, READ_MANY_FILES_TOOL_NAME, - MemoryTool.Name, + MEMORY_TOOL_NAME, WEB_SEARCH_TOOL_NAME, ]); for (const tool of toolRegistry.getAllTools()) { diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts index 76d2246bc1..727593456b 100644 --- a/packages/core/src/core/prompts.ts +++ b/packages/core/src/core/prompts.ts @@ -7,10 +7,11 @@ import path from 'node:path'; import fs from 'node:fs'; import os from 'node:os'; -import { GlobTool } from '../tools/glob.js'; import { EDIT_TOOL_NAME, + GLOB_TOOL_NAME, GREP_TOOL_NAME, + MEMORY_TOOL_NAME, READ_FILE_TOOL_NAME, READ_MANY_FILES_TOOL_NAME, SHELL_TOOL_NAME, @@ -18,7 +19,6 @@ import { } from '../tools/tool-names.js'; import process from 'node:process'; import { isGitRepository } from '../utils/gitUtils.js'; -import { MemoryTool } from '../tools/memoryTool.js'; import { CodebaseInvestigatorAgent } from '../agents/codebase-investigator.js'; import type { Config } from '../config/config.js'; import { GEMINI_DIR } from '../utils/paths.js'; @@ -131,11 +131,11 @@ When requested to perform tasks like fixing bugs, adding features, refactoring, ${(function () { if (enableCodebaseInvestigator) { return ` -1. **Understand & Strategize:** Think about the user's request and the relevant codebase context. When the task involves **complex refactoring, codebase exploration or system-wide analysis**, your **first and primary tool** must be '${CodebaseInvestigatorAgent.name}'. Use it to build a comprehensive understanding of the code, its structure, and dependencies. For **simple, targeted searches** (like finding a specific function name, file path, or variable declaration), you should use '${GREP_TOOL_NAME}' or '${GlobTool.Name}' directly. +1. **Understand & Strategize:** Think about the user's request and the relevant codebase context. When the task involves **complex refactoring, codebase exploration or system-wide analysis**, your **first and primary tool** must be '${CodebaseInvestigatorAgent.name}'. Use it to build a comprehensive understanding of the code, its structure, and dependencies. For **simple, targeted searches** (like finding a specific function name, file path, or variable declaration), you should use '${GREP_TOOL_NAME}' or '${GLOB_TOOL_NAME}' directly. 2. **Plan:** Build a coherent and grounded (based on the understanding in step 1) plan for how you intend to resolve the user's task. If '${CodebaseInvestigatorAgent.name}' was used, do not ignore the output of '${CodebaseInvestigatorAgent.name}', you must use it as the foundation of your plan. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.`; } return ` -1. **Understand:** Think about the user's request and the relevant codebase context. Use '${GREP_TOOL_NAME}' and '${GlobTool.Name}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${READ_FILE_TOOL_NAME}' and '${READ_MANY_FILES_TOOL_NAME}' to understand context and validate any assumptions you may have. +1. **Understand:** Think about the user's request and the relevant codebase context. Use '${GREP_TOOL_NAME}' and '${GLOB_TOOL_NAME}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${READ_FILE_TOOL_NAME}' and '${READ_MANY_FILES_TOOL_NAME}' to understand context and validate any assumptions you may have. 2. **Plan:** Build a coherent and grounded (based on the understanding in step 1) plan for how you intend to resolve the user's task. Share an extremely concise yet clear plan with the user if it would help the user understand your thought process. As part of the plan, you should use an iterative development process that includes writing unit tests to verify your changes. Use output logs or debug statements as part of this process to arrive at a solution.`; })()} 3. **Implement:** Use the available tools (e.g., '${EDIT_TOOL_NAME}', '${WRITE_FILE_TOOL_NAME}' '${SHELL_TOOL_NAME}' ...) to act on the plan, strictly adhering to the project's established conventions (detailed under 'Core Mandates'). @@ -216,7 +216,7 @@ ${(function () { return `- **Interactive Commands:** Prefer non-interactive commands when it makes sense; however, some commands are only interactive and expect user input during their execution (e.g. ssh, vim). If you choose to execute an interactive command consider letting the user know they can press \`ctrl + f\` to focus into the shell to provide input.`; } })()} -- **Remembering Facts:** Use the '${MemoryTool.Name}' tool to remember specific, *user-related* facts or preferences when the user explicitly asks, or when they state a clear, concise piece of information that would help personalize or streamline *your future interactions with them* (e.g., preferred coding style, common project paths they use, personal tool aliases). This tool is for user-specific information that should persist across sessions. Do *not* use it for general project context or information. If unsure whether to save something, you can ask the user, "Should I remember that for you?" +- **Remembering Facts:** Use the '${MEMORY_TOOL_NAME}' tool to remember specific, *user-related* facts or preferences when the user explicitly asks, or when they state a clear, concise piece of information that would help personalize or streamline *your future interactions with them* (e.g., preferred coding style, common project paths they use, personal tool aliases). This tool is for user-specific information that should persist across sessions. Do *not* use it for general project context or information. If unsure whether to save something, you can ask the user, "Should I remember that for you?" - **Respect User Confirmations:** Most tool calls (also denoted as 'function calls') will first require confirmation from the user, where they will either approve or cancel the function call. If a user cancels a function call, respect their choice and do _not_ try to make the function call again. It is okay to request the tool call again _only_ if the user requests that same tool call on a subsequent prompt. When a user cancels a function call, assume best intentions from the user and consider inquiring if they prefer any alternative paths forward. ## Interaction Details diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts index cf1ca62067..3fca290762 100644 --- a/packages/core/src/tools/edit.ts +++ b/packages/core/src/tools/edit.ts @@ -409,7 +409,7 @@ class EditToolInvocation implements ToolInvocation { logFileOperation( this.config, new FileOperationEvent( - EditTool.Name, + EDIT_TOOL_NAME, operation, editData.newContent.split('\n').length, mimetype, @@ -464,10 +464,9 @@ export class EditTool extends BaseDeclarativeTool implements ModifiableDeclarativeTool { - static readonly Name = EDIT_TOOL_NAME; constructor(private readonly config: Config) { super( - EditTool.Name, + EDIT_TOOL_NAME, 'Edit', `Replaces text within a file. By default, replaces a single occurrence, but can replace multiple occurrences when \`expected_replacements\` is specified. This tool requires providing significant context around the change to ensure precise targeting. Always use the ${READ_FILE_TOOL_NAME} tool to examine the file's current content before attempting a text replacement. diff --git a/packages/core/src/tools/glob.ts b/packages/core/src/tools/glob.ts index dadf282536..1354cf690c 100644 --- a/packages/core/src/tools/glob.ts +++ b/packages/core/src/tools/glob.ts @@ -260,11 +260,9 @@ class GlobToolInvocation extends BaseToolInvocation< * Implementation of the Glob tool logic */ export class GlobTool extends BaseDeclarativeTool { - static readonly Name = GLOB_TOOL_NAME; - constructor(private config: Config) { super( - GlobTool.Name, + GLOB_TOOL_NAME, 'FindFiles', 'Efficiently finds files matching specific glob patterns (e.g., `src/**/*.ts`, `**/*.md`), returning absolute paths sorted by modification time (newest first). Ideal for quickly locating files based on their name or path structure, especially in large codebases.', Kind.Search, diff --git a/packages/core/src/tools/ls.ts b/packages/core/src/tools/ls.ts index 2f60f6ea19..fb6aa09131 100644 --- a/packages/core/src/tools/ls.ts +++ b/packages/core/src/tools/ls.ts @@ -12,6 +12,7 @@ import { makeRelative, shortenPath } from '../utils/paths.js'; import type { Config } from '../config/config.js'; import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/constants.js'; import { ToolErrorType } from './tool-error.js'; +import { LS_TOOL_NAME } from './tool-names.js'; /** * Parameters for the LS tool @@ -252,11 +253,9 @@ class LSToolInvocation extends BaseToolInvocation { * Implementation of the LS tool logic */ export class LSTool extends BaseDeclarativeTool { - static readonly Name = 'list_directory'; - constructor(private config: Config) { super( - LSTool.Name, + LS_TOOL_NAME, 'ReadFolder', 'Lists the names of files and subdirectories directly within a specified directory path. Can optionally ignore entries matching provided glob patterns.', Kind.Search, diff --git a/packages/core/src/tools/memoryTool.ts b/packages/core/src/tools/memoryTool.ts index eb53630d64..2232ff3de4 100644 --- a/packages/core/src/tools/memoryTool.ts +++ b/packages/core/src/tools/memoryTool.ts @@ -23,9 +23,10 @@ import type { ModifyContext, } from './modifiable-tool.js'; import { ToolErrorType } from './tool-error.js'; +import { MEMORY_TOOL_NAME } from './tool-names.js'; const memoryToolSchemaData: FunctionDeclaration = { - name: 'save_memory', + name: MEMORY_TOOL_NAME, description: 'Saves a specific piece of information or fact to your long-term memory. Use this when the user explicitly asks you to remember something, or when they state a clear, concise fact that seems important to retain for future interactions.', parametersJsonSchema: { @@ -288,10 +289,9 @@ export class MemoryTool extends BaseDeclarativeTool implements ModifiableDeclarativeTool { - static readonly Name: string = memoryToolSchemaData.name!; constructor() { super( - MemoryTool.Name, + MEMORY_TOOL_NAME, 'Save Memory', memoryToolDescription, Kind.Think, diff --git a/packages/core/src/tools/smart-edit.ts b/packages/core/src/tools/smart-edit.ts index 16c770a9f6..437454f034 100644 --- a/packages/core/src/tools/smart-edit.ts +++ b/packages/core/src/tools/smart-edit.ts @@ -815,11 +815,9 @@ export class SmartEditTool extends BaseDeclarativeTool implements ModifiableDeclarativeTool { - static readonly Name = EDIT_TOOL_NAME; - constructor(private readonly config: Config) { super( - SmartEditTool.Name, + EDIT_TOOL_NAME, 'Edit', `Replaces text within a file. Replaces a single occurrence. This tool requires providing significant context around the change to ensure precise targeting. Always use the ${READ_FILE_TOOL_NAME} tool to examine the file's current content before attempting a text replacement. diff --git a/packages/core/src/tools/tool-names.ts b/packages/core/src/tools/tool-names.ts index 8dbe1ca051..77ce28fe8f 100644 --- a/packages/core/src/tools/tool-names.ts +++ b/packages/core/src/tools/tool-names.ts @@ -18,7 +18,5 @@ export const SHELL_TOOL_NAME = 'run_shell_command'; export const GREP_TOOL_NAME = 'search_file_content'; export const READ_MANY_FILES_TOOL_NAME = 'read_many_files'; export const READ_FILE_TOOL_NAME = 'read_file'; - -// TODO: Migrate other tool names here to follow this pattern and prevent future circular dependencies. -// Candidates for migration: -// - LSTool ('list_directory') +export const LS_TOOL_NAME = 'list_directory'; +export const MEMORY_TOOL_NAME = 'save_memory'; diff --git a/packages/core/src/tools/web-fetch.ts b/packages/core/src/tools/web-fetch.ts index 3a865783d5..7021c9ef25 100644 --- a/packages/core/src/tools/web-fetch.ts +++ b/packages/core/src/tools/web-fetch.ts @@ -395,14 +395,12 @@ export class WebFetchTool extends BaseDeclarativeTool< WebFetchToolParams, ToolResult > { - static readonly Name: string = WEB_FETCH_TOOL_NAME; - constructor( private readonly config: Config, messageBus?: MessageBus, ) { super( - WebFetchTool.Name, + WEB_FETCH_TOOL_NAME, 'WebFetch', "Processes content from URL(s), including local and private network addresses (e.g., localhost), embedded in a prompt. Include up to 20 URLs and instructions (e.g., summarize, extract specific data) directly in the 'prompt' parameter.", Kind.Fetch, diff --git a/packages/core/src/tools/web-search.ts b/packages/core/src/tools/web-search.ts index 251fe61b85..09fe7de2ac 100644 --- a/packages/core/src/tools/web-search.ts +++ b/packages/core/src/tools/web-search.ts @@ -185,11 +185,9 @@ export class WebSearchTool extends BaseDeclarativeTool< WebSearchToolParams, WebSearchToolResult > { - static readonly Name: string = WEB_SEARCH_TOOL_NAME; - constructor(private readonly config: Config) { super( - WebSearchTool.Name, + WEB_SEARCH_TOOL_NAME, 'GoogleSearch', 'Performs a web search using Google Search (via the Gemini API) and returns the results. This tool is useful for finding information on the internet based on a query.', Kind.Search, diff --git a/packages/core/src/tools/write-file.ts b/packages/core/src/tools/write-file.ts index f2f58565cf..90300b1fbb 100644 --- a/packages/core/src/tools/write-file.ts +++ b/packages/core/src/tools/write-file.ts @@ -319,7 +319,7 @@ class WriteFileToolInvocation extends BaseToolInvocation< logFileOperation( this.config, new FileOperationEvent( - WriteFileTool.Name, + WRITE_FILE_TOOL_NAME, operation, fileContent.split('\n').length, mimetype, @@ -390,11 +390,9 @@ export class WriteFileTool extends BaseDeclarativeTool implements ModifiableDeclarativeTool { - static readonly Name: string = WRITE_FILE_TOOL_NAME; - constructor(private readonly config: Config) { super( - WriteFileTool.Name, + WRITE_FILE_TOOL_NAME, 'WriteFile', `Writes content to a specified file in the local filesystem. diff --git a/packages/core/src/tools/write-todos.ts b/packages/core/src/tools/write-todos.ts index f627504f4c..9b077e47f7 100644 --- a/packages/core/src/tools/write-todos.ts +++ b/packages/core/src/tools/write-todos.ts @@ -126,11 +126,9 @@ export class WriteTodosTool extends BaseDeclarativeTool< WriteTodosToolParams, ToolResult > { - static readonly Name: string = WRITE_TODOS_TOOL_NAME; - constructor() { super( - WriteTodosTool.Name, + WRITE_TODOS_TOOL_NAME, 'Write Todos', WRITE_TODOS_DESCRIPTION, Kind.Other,