diff --git a/packages/cli/src/config/policy.ts b/packages/cli/src/config/policy.ts index 85226540ee..63c0fee5a1 100644 --- a/packages/cli/src/config/policy.ts +++ b/packages/cli/src/config/policy.ts @@ -11,11 +11,10 @@ import { ApprovalMode, // Read-only tools GlobTool, - GrepTool, LSTool, - ReadFileTool, - ReadManyFilesTool, - RipGrepTool, + GREP_TOOL_NAME, + READ_MANY_FILES_TOOL_NAME, + READ_FILE_TOOL_NAME, // Write tools EditTool, MemoryTool, @@ -30,11 +29,10 @@ import type { Settings } from './settings.js'; // files or system state. const READ_ONLY_TOOLS = new Set([ GlobTool.Name, - GrepTool.Name, - RipGrepTool.Name, + GREP_TOOL_NAME, LSTool.Name, - ReadFileTool.Name, - ReadManyFilesTool.Name, + READ_FILE_TOOL_NAME, + READ_MANY_FILES_TOOL_NAME, WebSearchTool.Name, ]); diff --git a/packages/core/src/agents/codebase-investigator.ts b/packages/core/src/agents/codebase-investigator.ts index f49c2aba16..5195ada239 100644 --- a/packages/core/src/agents/codebase-investigator.ts +++ b/packages/core/src/agents/codebase-investigator.ts @@ -6,9 +6,11 @@ import type { AgentDefinition } from './types.js'; import { LSTool } from '../tools/ls.js'; -import { ReadFileTool } from '../tools/read-file.js'; -import { GLOB_TOOL_NAME } from '../tools/tool-names.js'; -import { GrepTool } from '../tools/grep.js'; +import { + GLOB_TOOL_NAME, + GREP_TOOL_NAME, + READ_FILE_TOOL_NAME, +} from '../tools/tool-names.js'; import { DEFAULT_GEMINI_MODEL } from '../config/models.js'; import { z } from 'zod'; @@ -80,7 +82,7 @@ export const CodebaseInvestigatorAgent: AgentDefinition< toolConfig: { // Grant access only to read-only tools. - tools: [LSTool.Name, ReadFileTool.Name, GLOB_TOOL_NAME, GrepTool.Name], + tools: [LSTool.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 9821b58476..c062edb703 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 { ReadFileTool } from '../tools/read-file.js'; +import { READ_FILE_TOOL_NAME } from '../tools/tool-names.js'; import { GeminiChat, StreamEventType, @@ -202,7 +202,9 @@ describe('AgentExecutor', () => { mockConfig = makeFakeConfig(); parentToolRegistry = new ToolRegistry(mockConfig); parentToolRegistry.registerTool(new LSTool(mockConfig)); - parentToolRegistry.registerTool(new ReadFileTool(mockConfig)); + parentToolRegistry.registerTool( + new MockTool({ name: READ_FILE_TOOL_NAME }), + ); parentToolRegistry.registerTool(MOCK_TOOL_NOT_ALLOWED); vi.spyOn(mockConfig, 'getToolRegistry').mockResolvedValue( @@ -242,7 +244,10 @@ describe('AgentExecutor', () => { }); it('should create an isolated ToolRegistry for the agent', async () => { - const definition = createTestDefinition([LSTool.Name, ReadFileTool.Name]); + const definition = createTestDefinition([ + LSTool.Name, + READ_FILE_TOOL_NAME, + ]); const executor = await AgentExecutor.create( definition, mockConfig, @@ -253,7 +258,7 @@ describe('AgentExecutor', () => { expect(agentRegistry).not.toBe(parentToolRegistry); expect(agentRegistry.getAllToolNames()).toEqual( - expect.arrayContaining([LSTool.Name, ReadFileTool.Name]), + expect.arrayContaining([LSTool.Name, READ_FILE_TOOL_NAME]), ); expect(agentRegistry.getAllToolNames()).toHaveLength(2); expect(agentRegistry.getTool(MOCK_TOOL_NOT_ALLOWED.name)).toBeUndefined(); @@ -801,7 +806,7 @@ describe('AgentExecutor', () => { const badCallId = 'bad_call_1'; mockModelResponse([ { - name: ReadFileTool.Name, + name: READ_FILE_TOOL_NAME, args: { path: 'secret.txt' }, id: badCallId, }, @@ -839,7 +844,7 @@ describe('AgentExecutor', () => { expect.objectContaining({ functionResponse: expect.objectContaining({ id: badCallId, - name: ReadFileTool.Name, + name: READ_FILE_TOOL_NAME, response: { error: expect.stringContaining('Unauthorized tool call'), }, @@ -853,7 +858,7 @@ describe('AgentExecutor', () => { type: 'ERROR', data: expect.objectContaining({ context: 'tool_call_unauthorized', - name: ReadFileTool.Name, + name: READ_FILE_TOOL_NAME, }), }), ); diff --git a/packages/core/src/agents/executor.ts b/packages/core/src/agents/executor.ts index 9bc5b6ba85..f7c54742f7 100644 --- a/packages/core/src/agents/executor.ts +++ b/packages/core/src/agents/executor.ts @@ -20,13 +20,15 @@ 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 { GrepTool } from '../tools/grep.js'; -import { RipGrepTool } from '../tools/ripGrep.js'; import { LSTool } from '../tools/ls.js'; import { MemoryTool } from '../tools/memoryTool.js'; -import { ReadFileTool } from '../tools/read-file.js'; -import { ReadManyFilesTool } from '../tools/read-many-files.js'; -import { GLOB_TOOL_NAME, WEB_SEARCH_TOOL_NAME } from '../tools/tool-names.js'; +import { + GLOB_TOOL_NAME, + GREP_TOOL_NAME, + READ_FILE_TOOL_NAME, + READ_MANY_FILES_TOOL_NAME, + WEB_SEARCH_TOOL_NAME, +} from '../tools/tool-names.js'; import { promptIdContext } from '../utils/promptIdContext.js'; import { logAgentStart, logAgentFinish } from '../telemetry/loggers.js'; import { AgentStartEvent, AgentFinishEvent } from '../telemetry/types.js'; @@ -709,11 +711,10 @@ Important Rules: // confirmations for subagents. const allowlist = new Set([ LSTool.Name, - ReadFileTool.Name, - GrepTool.Name, - RipGrepTool.Name, + READ_FILE_TOOL_NAME, + GREP_TOOL_NAME, GLOB_TOOL_NAME, - ReadManyFilesTool.Name, + READ_MANY_FILES_TOOL_NAME, MemoryTool.Name, WEB_SEARCH_TOOL_NAME, ]); diff --git a/packages/core/src/core/coreToolScheduler.ts b/packages/core/src/core/coreToolScheduler.ts index 57eee15359..e878e4e70d 100644 --- a/packages/core/src/core/coreToolScheduler.ts +++ b/packages/core/src/core/coreToolScheduler.ts @@ -22,13 +22,12 @@ import { ToolConfirmationOutcome, ApprovalMode, logToolCall, - ReadFileTool, ToolErrorType, ToolCallEvent, logToolOutputTruncated, ToolOutputTruncatedEvent, } from '../index.js'; -import { SHELL_TOOL_NAME } from '../tools/tool-names.js'; +import { READ_FILE_TOOL_NAME, SHELL_TOOL_NAME } from '../tools/tool-names.js'; import type { Part, PartListUnion } from '@google/genai'; import { getResponseTextFromParts } from '../utils/generateContentResponseUtilities.js'; import type { ModifyContext } from '../tools/modifiable-tool.js'; @@ -301,10 +300,10 @@ export async function truncateAndSaveToFile( return { content: `Tool output was too large and has been truncated. The full output has been saved to: ${outputFile} -To read the complete output, use the ${ReadFileTool.Name} tool with the absolute file path above. For large files, you can use the offset and limit parameters to read specific sections: -- ${ReadFileTool.Name} tool with offset=0, limit=100 to see the first 100 lines -- ${ReadFileTool.Name} tool with offset=N to skip N lines from the beginning -- ${ReadFileTool.Name} tool with limit=M to read only M lines at a time +To read the complete output, use the ${READ_FILE_TOOL_NAME} tool with the absolute file path above. For large files, you can use the offset and limit parameters to read specific sections: +- ${READ_FILE_TOOL_NAME} tool with offset=0, limit=100 to see the first 100 lines +- ${READ_FILE_TOOL_NAME} tool with offset=N to skip N lines from the beginning +- ${READ_FILE_TOOL_NAME} tool with limit=M to read only M lines at a time The truncated output below shows the beginning and end of the content. The marker '... [CONTENT TRUNCATED] ...' indicates where content was removed. This allows you to efficiently examine different parts of the output without loading the entire file. Truncated part of the output: diff --git a/packages/core/src/core/prompts.ts b/packages/core/src/core/prompts.ts index 2b3bbae4cc..76d2246bc1 100644 --- a/packages/core/src/core/prompts.ts +++ b/packages/core/src/core/prompts.ts @@ -8,11 +8,11 @@ import path from 'node:path'; import fs from 'node:fs'; import os from 'node:os'; import { GlobTool } from '../tools/glob.js'; -import { GrepTool } from '../tools/grep.js'; -import { ReadFileTool } from '../tools/read-file.js'; -import { ReadManyFilesTool } from '../tools/read-many-files.js'; import { EDIT_TOOL_NAME, + GREP_TOOL_NAME, + READ_FILE_TOOL_NAME, + READ_MANY_FILES_TOOL_NAME, SHELL_TOOL_NAME, WRITE_FILE_TOOL_NAME, } from '../tools/tool-names.js'; @@ -120,7 +120,7 @@ export function getCoreSystemPrompt( - **Proactiveness:** Fulfill the user's request thoroughly. When adding features or fixing bugs, this includes adding tests to ensure quality. Consider all created files, especially tests, to be permanent artifacts unless the user says otherwise. - **Confirm Ambiguity/Expansion:** Do not take significant actions beyond the clear scope of the request without confirming with the user. If asked *how* to do something, explain first, don't just do it. - **Explaining Changes:** After completing a code modification or file operation *do not* provide summaries unless asked. -- **Path Construction:** Before using any file system tool (e.g., ${ReadFileTool.Name}' or '${WRITE_FILE_TOOL_NAME}'), you must construct the full absolute path for the file_path argument. Always combine the absolute path of the project's root directory with the file's path relative to the root. For example, if the project root is /path/to/project/ and the file is foo/bar/baz.txt, the final path you must use is /path/to/project/foo/bar/baz.txt. If the user provides a relative path, you must resolve it against the root directory to create an absolute path. +- **Path Construction:** Before using any file system tool (e.g., ${READ_FILE_TOOL_NAME}' or '${WRITE_FILE_TOOL_NAME}'), you must construct the full absolute path for the file_path argument. Always combine the absolute path of the project's root directory with the file's path relative to the root. For example, if the project root is /path/to/project/ and the file is foo/bar/baz.txt, the final path you must use is /path/to/project/foo/bar/baz.txt. If the user provides a relative path, you must resolve it against the root directory to create an absolute path. - **Do Not revert changes:** Do not revert changes to the codebase unless asked to do so by the user. Only revert changes made by you if they have resulted in an error or if the user has explicitly asked you to revert the changes. @@ -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 '${GrepTool.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 '${GlobTool.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 '${GrepTool.Name}' and '${GlobTool.Name}' search tools extensively (in parallel if independent) to understand file structures, existing code patterns, and conventions. Use '${ReadFileTool.Name}' and '${ReadManyFilesTool.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 '${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. 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'). @@ -205,7 +205,7 @@ IT IS CRITICAL TO FOLLOW THESE GUIDELINES TO AVOID EXCESSIVE TOKEN CONSUMPTION. - **Security First:** Always apply security best practices. Never introduce code that exposes, logs, or commits secrets, API keys, or other sensitive information. ## Tool Usage -- **File Paths:** Always use absolute paths when referring to files with tools like '${ReadFileTool.Name}' or '${WRITE_FILE_TOOL_NAME}'. Relative paths are not supported. You must provide an absolute path. +- **File Paths:** Always use absolute paths when referring to files with tools like '${READ_FILE_TOOL_NAME}' or '${WRITE_FILE_TOOL_NAME}'. Relative paths are not supported. You must provide an absolute path. - **Parallelism:** Execute multiple independent tool calls in parallel when feasible (i.e. searching the codebase). - **Command Execution:** Use the '${SHELL_TOOL_NAME}' tool for running shell commands, remembering the safety rule to explain modifying commands first. - **Background Processes:** Use background processes (via \`&\`) for commands that are unlikely to stop on their own, e.g. \`node server.js &\`. If unsure, ask the user. @@ -269,7 +269,7 @@ ${(function () { })()} # Final Reminder -Your core function is efficient and safe assistance. Balance extreme conciseness with the crucial need for clarity, especially regarding safety and potential system modifications. Always prioritize user control and project conventions. Never make assumptions about the contents of files; instead use '${ReadFileTool.Name}' or '${ReadManyFilesTool.Name}' to ensure you aren't making broad assumptions. Finally, you are an agent - please keep going until the user's query is completely resolved. +Your core function is efficient and safe assistance. Balance extreme conciseness with the crucial need for clarity, especially regarding safety and potential system modifications. Always prioritize user control and project conventions. Never make assumptions about the contents of files; instead use '${READ_FILE_TOOL_NAME}' or '${READ_MANY_FILES_TOOL_NAME}' to ensure you aren't making broad assumptions. Finally, you are an agent - please keep going until the user's query is completely resolved. `.trim(); // if GEMINI_WRITE_SYSTEM_MD is set (and not 0|false), write base system prompt to file diff --git a/packages/core/src/tools/edit.ts b/packages/core/src/tools/edit.ts index 889dcc7f41..cf1ca62067 100644 --- a/packages/core/src/tools/edit.ts +++ b/packages/core/src/tools/edit.ts @@ -22,7 +22,6 @@ import type { Config } from '../config/config.js'; import { ApprovalMode } from '../config/config.js'; import { ensureCorrectEdit } from '../utils/editCorrector.js'; import { DEFAULT_DIFF_OPTIONS, getDiffStat } from './diffOptions.js'; -import { ReadFileTool } from './read-file.js'; import { logFileOperation } from '../telemetry/loggers.js'; import { FileOperationEvent } from '../telemetry/types.js'; import { FileOperation } from '../telemetry/metrics.js'; @@ -34,7 +33,7 @@ import type { } from './modifiable-tool.js'; import { IdeClient } from '../ide/ide-client.js'; import { safeLiteralReplace } from '../utils/textUtils.js'; -import { EDIT_TOOL_NAME } from './tool-names.js'; +import { EDIT_TOOL_NAME, READ_FILE_TOOL_NAME } from './tool-names.js'; export function applyReplacement( currentContent: string | null, @@ -182,7 +181,7 @@ class EditToolInvocation implements ToolInvocation { } else if (occurrences === 0) { error = { display: `Failed to edit, could not find the string to replace.`, - raw: `Failed to edit, 0 occurrences found for old_string in ${params.file_path}. No edits made. The exact text in old_string was not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context. Use ${ReadFileTool.Name} tool to verify.`, + raw: `Failed to edit, 0 occurrences found for old_string in ${params.file_path}. No edits made. The exact text in old_string was not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context. Use ${READ_FILE_TOOL_NAME} tool to verify.`, type: ToolErrorType.EDIT_NO_OCCURRENCE_FOUND, }; } else if (occurrences !== expectedReplacements) { @@ -470,7 +469,7 @@ export class EditTool super( EditTool.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 ${ReadFileTool.Name} tool to examine the file's current content before attempting a text replacement. + `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. The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response. diff --git a/packages/core/src/tools/grep.ts b/packages/core/src/tools/grep.ts index 3492cc15c6..56320cf237 100644 --- a/packages/core/src/tools/grep.ts +++ b/packages/core/src/tools/grep.ts @@ -18,6 +18,7 @@ import { isGitRepository } from '../utils/gitUtils.js'; import type { Config } from '../config/config.js'; import type { FileExclusions } from '../utils/ignorePatterns.js'; import { ToolErrorType } from './tool-error.js'; +import { GREP_TOOL_NAME } from './tool-names.js'; import { debugLogger } from '../utils/debugLogger.js'; // --- Interfaces --- @@ -563,11 +564,9 @@ class GrepToolInvocation extends BaseToolInvocation< * Implementation of the Grep tool logic (moved from CLI) */ export class GrepTool extends BaseDeclarativeTool { - static readonly Name = 'search_file_content'; // Keep static name - constructor(private readonly config: Config) { super( - GrepTool.Name, + GREP_TOOL_NAME, 'SearchText', 'Searches for a regular expression pattern within the content of files in a specified directory (or current working directory). Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers.', Kind.Search, diff --git a/packages/core/src/tools/read-file.ts b/packages/core/src/tools/read-file.ts index 9f14c1af6c..cb55220b5c 100644 --- a/packages/core/src/tools/read-file.ts +++ b/packages/core/src/tools/read-file.ts @@ -19,6 +19,7 @@ import { FileOperation } from '../telemetry/metrics.js'; import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js'; import { logFileOperation } from '../telemetry/loggers.js'; import { FileOperationEvent } from '../telemetry/types.js'; +import { READ_FILE_TOOL_NAME } from './tool-names.js'; /** * Parameters for the ReadFile tool @@ -112,7 +113,7 @@ ${result.llmContent}`; logFileOperation( this.config, new FileOperationEvent( - ReadFileTool.Name, + READ_FILE_TOOL_NAME, FileOperation.READ, lines, mimetype, @@ -135,11 +136,9 @@ export class ReadFileTool extends BaseDeclarativeTool< ReadFileToolParams, ToolResult > { - static readonly Name: string = 'read_file'; - constructor(private config: Config) { super( - ReadFileTool.Name, + READ_FILE_TOOL_NAME, 'ReadFile', `Reads and returns the content of a specified file. If the file is large, the content will be truncated. The tool's response will clearly indicate if truncation has occurred and will provide details on how to read more of the file using the 'offset' and 'limit' parameters. Handles text, images (PNG, JPG, GIF, WEBP, SVG, BMP), and PDF files. For text files, it can read specific line ranges.`, Kind.Read, diff --git a/packages/core/src/tools/read-many-files.ts b/packages/core/src/tools/read-many-files.ts index 0694413762..7bcfda07a1 100644 --- a/packages/core/src/tools/read-many-files.ts +++ b/packages/core/src/tools/read-many-files.ts @@ -27,6 +27,7 @@ import { getProgrammingLanguage } from '../telemetry/telemetry-utils.js'; import { logFileOperation } from '../telemetry/loggers.js'; import { FileOperationEvent } from '../telemetry/types.js'; import { ToolErrorType } from './tool-error.js'; +import { READ_MANY_FILES_TOOL_NAME } from './tool-names.js'; /** * Parameters for the ReadManyFilesTool. @@ -392,7 +393,7 @@ ${finalExclusionPatternsForDescription logFileOperation( this.config, new FileOperationEvent( - ReadManyFilesTool.Name, + READ_MANY_FILES_TOOL_NAME, FileOperation.READ, lines, mimetype, @@ -474,8 +475,6 @@ export class ReadManyFilesTool extends BaseDeclarativeTool< ReadManyFilesParams, ToolResult > { - static readonly Name: string = 'read_many_files'; - constructor(private config: Config) { const parameterSchema = { type: 'object', @@ -544,7 +543,7 @@ export class ReadManyFilesTool extends BaseDeclarativeTool< }; super( - ReadManyFilesTool.Name, + READ_MANY_FILES_TOOL_NAME, 'ReadManyFiles', `Reads content from multiple files specified by paths or glob patterns within a configured target directory. For text files, it concatenates their content into a single string. It is primarily designed for text-based files. However, it can also process image (e.g., .png, .jpg) and PDF (.pdf) files if their file names or extensions are explicitly included in the 'paths' argument. For these explicitly requested non-text files, their data is read and included in a format suitable for model consumption (e.g., base64 encoded). diff --git a/packages/core/src/tools/ripGrep.ts b/packages/core/src/tools/ripGrep.ts index 269fb37993..1e5e732c30 100644 --- a/packages/core/src/tools/ripGrep.ts +++ b/packages/core/src/tools/ripGrep.ts @@ -17,6 +17,7 @@ import { getErrorMessage, isNodeError } from '../utils/errors.js'; import type { Config } from '../config/config.js'; import { fileExists } from '../utils/fileUtils.js'; import { Storage } from '../config/storage.js'; +import { GREP_TOOL_NAME } from './tool-names.js'; const DEFAULT_TOTAL_MAX_MATCHES = 20000; @@ -419,11 +420,9 @@ export class RipGrepTool extends BaseDeclarativeTool< RipGrepToolParams, ToolResult > { - static readonly Name = 'search_file_content'; - constructor(private readonly config: Config) { super( - RipGrepTool.Name, + GREP_TOOL_NAME, 'SearchText', 'Searches for a regular expression pattern within the content of files in a specified directory (or current working directory). Can filter files by a glob pattern. Returns the lines containing matches, along with their file paths and line numbers. Total results limited to 20,000 matches like VSCode.', Kind.Search, diff --git a/packages/core/src/tools/smart-edit.ts b/packages/core/src/tools/smart-edit.ts index f4a01eb990..16c770a9f6 100644 --- a/packages/core/src/tools/smart-edit.ts +++ b/packages/core/src/tools/smart-edit.ts @@ -24,7 +24,6 @@ import { makeRelative, shortenPath } from '../utils/paths.js'; import { isNodeError } from '../utils/errors.js'; import { type Config, ApprovalMode } from '../config/config.js'; import { DEFAULT_DIFF_OPTIONS, getDiffStat } from './diffOptions.js'; -import { ReadFileTool } from './read-file.js'; import { type ModifiableDeclarativeTool, type ModifyContext, @@ -39,7 +38,7 @@ import { SmartEditCorrectionEvent } from '../telemetry/types.js'; import { logSmartEditCorrectionEvent } from '../telemetry/loggers.js'; import { correctPath } from '../utils/pathCorrector.js'; -import { EDIT_TOOL_NAME } from './tool-names.js'; +import { EDIT_TOOL_NAME, READ_FILE_TOOL_NAME } from './tool-names.js'; interface ReplacementContext { params: EditToolParams; currentContent: string; @@ -303,7 +302,7 @@ export function getErrorReplaceResult( if (occurrences === 0) { error = { display: `Failed to edit, could not find the string to replace.`, - raw: `Failed to edit, 0 occurrences found for old_string (${finalOldString}). Original old_string was (${params.old_string}) in ${params.file_path}. No edits made. The exact text in old_string was not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context. Use ${ReadFileTool.Name} tool to verify.`, + raw: `Failed to edit, 0 occurrences found for old_string (${finalOldString}). Original old_string was (${params.old_string}) in ${params.file_path}. No edits made. The exact text in old_string was not found. Ensure you're not escaping content incorrectly and check whitespace, indentation, and context. Use ${READ_FILE_TOOL_NAME} tool to verify.`, type: ToolErrorType.EDIT_NO_OCCURRENCE_FOUND, }; } else if (occurrences !== expectedReplacements) { @@ -822,7 +821,7 @@ export class SmartEditTool super( SmartEditTool.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 ${ReadFileTool.Name} tool to examine the file's current content before attempting a text replacement. + `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. The user has the ability to modify the \`new_string\` content. If modified, this will be stated in the response. diff --git a/packages/core/src/tools/tool-names.ts b/packages/core/src/tools/tool-names.ts index 9ad1ce4408..8dbe1ca051 100644 --- a/packages/core/src/tools/tool-names.ts +++ b/packages/core/src/tools/tool-names.ts @@ -15,9 +15,10 @@ export const WEB_SEARCH_TOOL_NAME = 'google_web_search'; export const WEB_FETCH_TOOL_NAME = 'web_fetch'; export const EDIT_TOOL_NAME = 'replace'; 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') -// - ReadFileTool ('read_file') -// - GrepTool ('search_file_content') diff --git a/packages/core/src/utils/editCorrector.ts b/packages/core/src/utils/editCorrector.ts index 38bd64c47a..1600e6aa68 100644 --- a/packages/core/src/utils/editCorrector.ts +++ b/packages/core/src/utils/editCorrector.ts @@ -8,10 +8,13 @@ import type { Content, GenerateContentConfig } from '@google/genai'; import type { GeminiClient } from '../core/client.js'; import type { BaseLlmClient } from '../core/baseLlmClient.js'; import type { EditToolParams } from '../tools/edit.js'; -import { EDIT_TOOL_NAME, WRITE_FILE_TOOL_NAME } from '../tools/tool-names.js'; -import { ReadFileTool } from '../tools/read-file.js'; -import { ReadManyFilesTool } from '../tools/read-many-files.js'; -import { GrepTool } from '../tools/grep.js'; +import { + EDIT_TOOL_NAME, + GREP_TOOL_NAME, + READ_FILE_TOOL_NAME, + READ_MANY_FILES_TOOL_NAME, + WRITE_FILE_TOOL_NAME, +} from '../tools/tool-names.js'; import { LruCache } from './LruCache.js'; import { DEFAULT_GEMINI_FLASH_LITE_MODEL } from '../config/models.js'; import { @@ -101,11 +104,11 @@ async function findLastEditTimestamp( const toolsInResp = new Set([ WRITE_FILE_TOOL_NAME, EDIT_TOOL_NAME, - ReadManyFilesTool.Name, - GrepTool.Name, + READ_MANY_FILES_TOOL_NAME, + GREP_TOOL_NAME, ]); // Tools that may reference the file path in their FunctionCall `args`. - const toolsInCall = new Set([...toolsInResp, ReadFileTool.Name]); + const toolsInCall = new Set([...toolsInResp, READ_FILE_TOOL_NAME]); // Iterate backwards to find the most recent relevant action. for (const entry of history.slice().reverse()) {