From a153ff587b76d85dd2c8b698f8710536d87279a4 Mon Sep 17 00:00:00 2001 From: Sandy Tao Date: Sat, 28 Feb 2026 13:27:54 -0800 Subject: [PATCH] refactor(core): Extract tool parameter names as constants (#20460) --- packages/core/src/prompts/snippets.ts | 22 +- .../tools/definitions/base-declarations.ts | 96 +++++++- .../core/src/tools/definitions/coreTools.ts | 53 +++++ .../dynamic-declaration-helpers.ts | 30 ++- .../model-family-sets/default-legacy.ts | 220 +++++++++++------- .../definitions/model-family-sets/gemini-3.ts | 220 +++++++++++------- packages/core/src/tools/tool-names.ts | 106 +++++++++ 7 files changed, 567 insertions(+), 180 deletions(-) diff --git a/packages/core/src/prompts/snippets.ts b/packages/core/src/prompts/snippets.ts index 0de9b11e25..bebd3c9146 100644 --- a/packages/core/src/prompts/snippets.ts +++ b/packages/core/src/prompts/snippets.ts @@ -17,6 +17,16 @@ import { SHELL_TOOL_NAME, WRITE_FILE_TOOL_NAME, WRITE_TODOS_TOOL_NAME, + GREP_PARAM_TOTAL_MAX_MATCHES, + GREP_PARAM_INCLUDE_PATTERN, + GREP_PARAM_EXCLUDE_PATTERN, + GREP_PARAM_CONTEXT, + GREP_PARAM_BEFORE, + GREP_PARAM_AFTER, + READ_FILE_PARAM_START_LINE, + READ_FILE_PARAM_END_LINE, + SHELL_PARAM_IS_BACKGROUND, + EDIT_PARAM_OLD_STRING, } from '../tools/tool-names.js'; import type { HierarchicalMemory } from '../config/memory.js'; import { DEFAULT_CONTEXT_FILENAME } from '../tools/memoryTool.js'; @@ -183,16 +193,16 @@ Use the following guidelines to optimize your search and read patterns. - Prefer using tools like ${GREP_TOOL_NAME} to identify points of interest instead of reading lots of files individually. - If you need to read multiple ranges in a file, do so parallel, in as few turns as possible. - It is more important to reduce extra turns, but please also try to minimize unnecessarily large file reads and search results, when doing so doesn't result in extra turns. Do this by always providing conservative limits and scopes to tools like ${READ_FILE_TOOL_NAME} and ${GREP_TOOL_NAME}. -- ${READ_FILE_TOOL_NAME} fails if old_string is ambiguous, causing extra turns. Take care to read enough with ${READ_FILE_TOOL_NAME} and ${GREP_TOOL_NAME} to make the edit unambiguous. +- ${READ_FILE_TOOL_NAME} fails if ${EDIT_PARAM_OLD_STRING} is ambiguous, causing extra turns. Take care to read enough with ${READ_FILE_TOOL_NAME} and ${GREP_TOOL_NAME} to make the edit unambiguous. - You can compensate for the risk of missing results with scoped or limited searches by doing multiple searches in parallel. - Your primary goal is still to do your best quality work. Efficiency is an important, but secondary concern. -- **Searching:** utilize search tools like ${GREP_TOOL_NAME} and ${GLOB_TOOL_NAME} with a conservative result count (\`total_max_matches\`) and a narrow scope (\`include_pattern\` and \`exclude_pattern\` parameters). -- **Searching and editing:** utilize search tools like ${GREP_TOOL_NAME} with a conservative result count and a narrow scope. Use \`context\`, \`before\`, and/or \`after\` to request enough context to avoid the need to read the file before editing matches. +- **Searching:** utilize search tools like ${GREP_TOOL_NAME} and ${GLOB_TOOL_NAME} with a conservative result count (\`${GREP_PARAM_TOTAL_MAX_MATCHES}\`) and a narrow scope (\`${GREP_PARAM_INCLUDE_PATTERN}\` and \`${GREP_PARAM_EXCLUDE_PATTERN}\` parameters). +- **Searching and editing:** utilize search tools like ${GREP_TOOL_NAME} with a conservative result count and a narrow scope. Use \`${GREP_PARAM_CONTEXT}\`, \`${GREP_PARAM_BEFORE}\`, and/or \`${GREP_PARAM_AFTER}\` to request enough context to avoid the need to read the file before editing matches. - **Understanding:** minimize turns needed to understand a file. It's most efficient to read small files in their entirety. -- **Large files:** utilize search tools like ${GREP_TOOL_NAME} and/or ${READ_FILE_TOOL_NAME} called in parallel with 'start_line' and 'end_line' to reduce the impact on context. Minimize extra turns, unless unavoidable due to the file being too large. +- **Large files:** utilize search tools like ${GREP_TOOL_NAME} and/or ${READ_FILE_TOOL_NAME} called in parallel with '${READ_FILE_PARAM_START_LINE}' and '${READ_FILE_PARAM_END_LINE}' to reduce the impact on context. Minimize extra turns, unless unavoidable due to the file being too large. - **Navigating:** read the minimum required to not require additional turns spent reading the file. @@ -659,11 +669,11 @@ function toolUsageInteractive( ? ' 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.' : ''; return ` -- **Background Processes:** To run a command in the background, set the \`is_background\` parameter to true. If unsure, ask the user. +- **Background Processes:** To run a command in the background, set the \`${SHELL_PARAM_IS_BACKGROUND}\` parameter to true. If unsure, ask the user. - **Interactive Commands:** Always prefer non-interactive commands (e.g., using 'run once' or 'CI' flags for test runners to avoid persistent watch modes or 'git --no-pager') unless a persistent process is specifically required; however, some commands are only interactive and expect user input during their execution (e.g. ssh, vim).${ctrlF}`; } return ` -- **Background Processes:** To run a command in the background, set the \`is_background\` parameter to true. +- **Background Processes:** To run a command in the background, set the \`${SHELL_PARAM_IS_BACKGROUND}\` parameter to true. - **Interactive Commands:** Always prefer non-interactive commands (e.g., using 'run once' or 'CI' flags for test runners to avoid persistent watch modes or 'git --no-pager') unless a persistent process is specifically required; however, some commands are only interactive and expect user input during their execution (e.g. ssh, vim).`; } diff --git a/packages/core/src/tools/definitions/base-declarations.ts b/packages/core/src/tools/definitions/base-declarations.ts index 9bea33cda0..b39dc42286 100644 --- a/packages/core/src/tools/definitions/base-declarations.ts +++ b/packages/core/src/tools/definitions/base-declarations.ts @@ -10,25 +10,115 @@ */ // ============================================================================ -// TOOL NAMES +// SHARED PARAMETER NAMES (used by multiple tools) // ============================================================================ +export const PARAM_FILE_PATH = 'file_path'; +export const PARAM_DIR_PATH = 'dir_path'; +export const PARAM_PATTERN = 'pattern'; +export const PARAM_CASE_SENSITIVE = 'case_sensitive'; +export const PARAM_RESPECT_GIT_IGNORE = 'respect_git_ignore'; +export const PARAM_RESPECT_GEMINI_IGNORE = 'respect_gemini_ignore'; +export const PARAM_FILE_FILTERING_OPTIONS = 'file_filtering_options'; +export const PARAM_DESCRIPTION = 'description'; + +// ============================================================================ +// TOOL NAMES & TOOL-SPECIFIC PARAMETER NAMES +// ============================================================================ + +// -- glob -- export const GLOB_TOOL_NAME = 'glob'; + +// -- grep_search -- export const GREP_TOOL_NAME = 'grep_search'; +export const GREP_PARAM_INCLUDE_PATTERN = 'include_pattern'; +export const GREP_PARAM_EXCLUDE_PATTERN = 'exclude_pattern'; +export const GREP_PARAM_NAMES_ONLY = 'names_only'; +export const GREP_PARAM_MAX_MATCHES_PER_FILE = 'max_matches_per_file'; +export const GREP_PARAM_TOTAL_MAX_MATCHES = 'total_max_matches'; +// ripgrep only +export const GREP_PARAM_FIXED_STRINGS = 'fixed_strings'; +export const GREP_PARAM_CONTEXT = 'context'; +export const GREP_PARAM_AFTER = 'after'; +export const GREP_PARAM_BEFORE = 'before'; +export const GREP_PARAM_NO_IGNORE = 'no_ignore'; + +// -- list_directory -- export const LS_TOOL_NAME = 'list_directory'; +export const LS_PARAM_IGNORE = 'ignore'; + +// -- read_file -- export const READ_FILE_TOOL_NAME = 'read_file'; +export const READ_FILE_PARAM_START_LINE = 'start_line'; +export const READ_FILE_PARAM_END_LINE = 'end_line'; + +// -- run_shell_command -- export const SHELL_TOOL_NAME = 'run_shell_command'; +export const SHELL_PARAM_COMMAND = 'command'; +export const SHELL_PARAM_IS_BACKGROUND = 'is_background'; + +// -- write_file -- export const WRITE_FILE_TOOL_NAME = 'write_file'; +export const WRITE_FILE_PARAM_CONTENT = 'content'; + +// -- replace (edit) -- export const EDIT_TOOL_NAME = 'replace'; +export const EDIT_PARAM_INSTRUCTION = 'instruction'; +export const EDIT_PARAM_OLD_STRING = 'old_string'; +export const EDIT_PARAM_NEW_STRING = 'new_string'; +export const EDIT_PARAM_ALLOW_MULTIPLE = 'allow_multiple'; + +// -- google_web_search -- export const WEB_SEARCH_TOOL_NAME = 'google_web_search'; +export const WEB_SEARCH_PARAM_QUERY = 'query'; +// -- write_todos -- export const WRITE_TODOS_TOOL_NAME = 'write_todos'; -export const WEB_FETCH_TOOL_NAME = 'web_fetch'; -export const READ_MANY_FILES_TOOL_NAME = 'read_many_files'; +export const TODOS_PARAM_TODOS = 'todos'; +export const TODOS_ITEM_PARAM_DESCRIPTION = 'description'; +export const TODOS_ITEM_PARAM_STATUS = 'status'; +// -- web_fetch -- +export const WEB_FETCH_TOOL_NAME = 'web_fetch'; +export const WEB_FETCH_PARAM_PROMPT = 'prompt'; + +// -- read_many_files -- +export const READ_MANY_FILES_TOOL_NAME = 'read_many_files'; +export const READ_MANY_PARAM_INCLUDE = 'include'; +export const READ_MANY_PARAM_EXCLUDE = 'exclude'; +export const READ_MANY_PARAM_RECURSIVE = 'recursive'; +export const READ_MANY_PARAM_USE_DEFAULT_EXCLUDES = 'useDefaultExcludes'; + +// -- save_memory -- export const MEMORY_TOOL_NAME = 'save_memory'; +export const MEMORY_PARAM_FACT = 'fact'; + +// -- get_internal_docs -- export const GET_INTERNAL_DOCS_TOOL_NAME = 'get_internal_docs'; +export const DOCS_PARAM_PATH = 'path'; + +// -- activate_skill -- export const ACTIVATE_SKILL_TOOL_NAME = 'activate_skill'; +export const SKILL_PARAM_NAME = 'name'; + +// -- ask_user -- export const ASK_USER_TOOL_NAME = 'ask_user'; +export const ASK_USER_PARAM_QUESTIONS = 'questions'; +// ask_user question item params +export const ASK_USER_QUESTION_PARAM_QUESTION = 'question'; +export const ASK_USER_QUESTION_PARAM_HEADER = 'header'; +export const ASK_USER_QUESTION_PARAM_TYPE = 'type'; +export const ASK_USER_QUESTION_PARAM_OPTIONS = 'options'; +export const ASK_USER_QUESTION_PARAM_MULTI_SELECT = 'multiSelect'; +export const ASK_USER_QUESTION_PARAM_PLACEHOLDER = 'placeholder'; +// ask_user option item params +export const ASK_USER_OPTION_PARAM_LABEL = 'label'; +export const ASK_USER_OPTION_PARAM_DESCRIPTION = 'description'; + +// -- exit_plan_mode -- export const EXIT_PLAN_MODE_TOOL_NAME = 'exit_plan_mode'; +export const EXIT_PLAN_PARAM_PLAN_PATH = 'plan_path'; + +// -- enter_plan_mode -- export const ENTER_PLAN_MODE_TOOL_NAME = 'enter_plan_mode'; +export const PLAN_MODE_PARAM_REASON = 'reason'; diff --git a/packages/core/src/tools/definitions/coreTools.ts b/packages/core/src/tools/definitions/coreTools.ts index 006597ca33..b5121ca5d2 100644 --- a/packages/core/src/tools/definitions/coreTools.ts +++ b/packages/core/src/tools/definitions/coreTools.ts @@ -38,6 +38,59 @@ export { ASK_USER_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, + // Shared parameter names + PARAM_FILE_PATH, + PARAM_DIR_PATH, + PARAM_PATTERN, + PARAM_CASE_SENSITIVE, + PARAM_RESPECT_GIT_IGNORE, + PARAM_RESPECT_GEMINI_IGNORE, + PARAM_FILE_FILTERING_OPTIONS, + PARAM_DESCRIPTION, + // Tool-specific parameter names + READ_FILE_PARAM_START_LINE, + READ_FILE_PARAM_END_LINE, + WRITE_FILE_PARAM_CONTENT, + GREP_PARAM_INCLUDE_PATTERN, + GREP_PARAM_EXCLUDE_PATTERN, + GREP_PARAM_NAMES_ONLY, + GREP_PARAM_MAX_MATCHES_PER_FILE, + GREP_PARAM_TOTAL_MAX_MATCHES, + GREP_PARAM_FIXED_STRINGS, + GREP_PARAM_CONTEXT, + GREP_PARAM_AFTER, + GREP_PARAM_BEFORE, + GREP_PARAM_NO_IGNORE, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + EDIT_PARAM_ALLOW_MULTIPLE, + LS_PARAM_IGNORE, + SHELL_PARAM_COMMAND, + SHELL_PARAM_IS_BACKGROUND, + WEB_SEARCH_PARAM_QUERY, + WEB_FETCH_PARAM_PROMPT, + READ_MANY_PARAM_INCLUDE, + READ_MANY_PARAM_EXCLUDE, + READ_MANY_PARAM_RECURSIVE, + READ_MANY_PARAM_USE_DEFAULT_EXCLUDES, + MEMORY_PARAM_FACT, + TODOS_PARAM_TODOS, + TODOS_ITEM_PARAM_DESCRIPTION, + TODOS_ITEM_PARAM_STATUS, + DOCS_PARAM_PATH, + ASK_USER_PARAM_QUESTIONS, + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ASK_USER_QUESTION_PARAM_OPTIONS, + ASK_USER_QUESTION_PARAM_MULTI_SELECT, + ASK_USER_QUESTION_PARAM_PLACEHOLDER, + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + PLAN_MODE_PARAM_REASON, + EXIT_PLAN_PARAM_PLAN_PATH, + SKILL_PARAM_NAME, } from './base-declarations.js'; // Re-export sets for compatibility diff --git a/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts b/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts index 83ed680ce7..79c66d81f6 100644 --- a/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts +++ b/packages/core/src/tools/definitions/dynamic-declaration-helpers.ts @@ -17,6 +17,12 @@ import { SHELL_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ACTIVATE_SKILL_TOOL_NAME, + SHELL_PARAM_COMMAND, + PARAM_DESCRIPTION, + PARAM_DIR_PATH, + SHELL_PARAM_IS_BACKGROUND, + EXIT_PLAN_PARAM_PLAN_PATH, + SKILL_PARAM_NAME, } from './base-declarations.js'; /** @@ -47,12 +53,12 @@ export function getShellToolDescription( 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.' + ? `To run a command in the background, set the \`${SHELL_PARAM_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}${efficiencyGuidelines}${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.' + ? `To run a command in the background, set the \`${SHELL_PARAM_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\`.${efficiencyGuidelines}${returnedInfo}`; } @@ -84,27 +90,27 @@ export function getShellDeclaration( parametersJsonSchema: { type: 'object', properties: { - command: { + [SHELL_PARAM_COMMAND]: { type: 'string', description: getCommandDescription(), }, - description: { + [PARAM_DESCRIPTION]: { 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: { + [PARAM_DIR_PATH]: { 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: { + [SHELL_PARAM_IS_BACKGROUND]: { 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: [SHELL_PARAM_COMMAND], }, }; } @@ -121,9 +127,9 @@ export function getExitPlanModeDeclaration( 'Finalizes the planning phase and transitions to implementation by presenting the plan for user approval. This tool MUST be used to exit Plan Mode before any source code edits can be performed. Call this whenever a plan is ready or the user requests implementation.', parametersJsonSchema: { type: 'object', - required: ['plan_path'], + required: [EXIT_PLAN_PARAM_PLAN_PATH], properties: { - plan_path: { + [EXIT_PLAN_PARAM_PLAN_PATH]: { type: 'string', description: `The file path to the finalized plan (e.g., "${plansDir}/feature-x.md"). This path MUST be within the designated plans directory: ${plansDir}/`, }, @@ -146,11 +152,13 @@ export function getActivateSkillDeclaration( let schema: z.ZodTypeAny; if (skillNames.length === 0) { schema = z.object({ - name: z.string().describe('No skills are currently available.'), + [SKILL_PARAM_NAME]: z + .string() + .describe('No skills are currently available.'), }); } else { schema = z.object({ - name: z + [SKILL_PARAM_NAME]: z // eslint-disable-next-line @typescript-eslint/no-unsafe-type-assertion .enum(skillNames as [string, ...string[]]) .describe('The name of the skill to activate.'), diff --git a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts index 23f36fbb24..3309fcc5ba 100644 --- a/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts +++ b/packages/core/src/tools/definitions/model-family-sets/default-legacy.ts @@ -25,6 +25,54 @@ import { GET_INTERNAL_DOCS_TOOL_NAME, ASK_USER_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, + // Shared parameter names + PARAM_FILE_PATH, + PARAM_DIR_PATH, + PARAM_PATTERN, + PARAM_CASE_SENSITIVE, + PARAM_RESPECT_GIT_IGNORE, + PARAM_RESPECT_GEMINI_IGNORE, + PARAM_FILE_FILTERING_OPTIONS, + // Tool-specific parameter names + READ_FILE_PARAM_START_LINE, + READ_FILE_PARAM_END_LINE, + WRITE_FILE_PARAM_CONTENT, + GREP_PARAM_INCLUDE_PATTERN, + GREP_PARAM_EXCLUDE_PATTERN, + GREP_PARAM_NAMES_ONLY, + GREP_PARAM_MAX_MATCHES_PER_FILE, + GREP_PARAM_TOTAL_MAX_MATCHES, + GREP_PARAM_FIXED_STRINGS, + GREP_PARAM_CONTEXT, + GREP_PARAM_AFTER, + GREP_PARAM_BEFORE, + GREP_PARAM_NO_IGNORE, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + EDIT_PARAM_ALLOW_MULTIPLE, + LS_PARAM_IGNORE, + WEB_SEARCH_PARAM_QUERY, + WEB_FETCH_PARAM_PROMPT, + READ_MANY_PARAM_INCLUDE, + READ_MANY_PARAM_EXCLUDE, + READ_MANY_PARAM_RECURSIVE, + READ_MANY_PARAM_USE_DEFAULT_EXCLUDES, + MEMORY_PARAM_FACT, + TODOS_PARAM_TODOS, + TODOS_ITEM_PARAM_DESCRIPTION, + TODOS_ITEM_PARAM_STATUS, + DOCS_PARAM_PATH, + ASK_USER_PARAM_QUESTIONS, + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ASK_USER_QUESTION_PARAM_OPTIONS, + ASK_USER_QUESTION_PARAM_MULTI_SELECT, + ASK_USER_QUESTION_PARAM_PLACEHOLDER, + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + PLAN_MODE_PARAM_REASON, } from '../base-declarations.js'; import { getShellDeclaration, @@ -39,22 +87,22 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - file_path: { + [PARAM_FILE_PATH]: { description: 'The path to the file to read.', type: 'string', }, - start_line: { + [READ_FILE_PARAM_START_LINE]: { description: 'Optional: The 1-based line number to start reading from.', type: 'number', }, - end_line: { + [READ_FILE_PARAM_END_LINE]: { description: 'Optional: The 1-based line number to end reading at (inclusive).', type: 'number', }, }, - required: ['file_path'], + required: [PARAM_FILE_PATH], }, }, @@ -66,17 +114,17 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - file_path: { + [PARAM_FILE_PATH]: { description: 'The path to the file to write to.', type: 'string', }, - content: { + [WRITE_FILE_PARAM_CONTENT]: { description: "The content to write to the file. Do not use omission placeholders like '(rest of methods ...)', '...', or 'unchanged code'; provide complete literal content.", type: 'string', }, }, - required: ['file_path', 'content'], + required: [PARAM_FILE_PATH, WRITE_FILE_PARAM_CONTENT], }, }, @@ -87,43 +135,43 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - pattern: { + [PARAM_PATTERN]: { description: `The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*').`, type: 'string', }, - dir_path: { + [PARAM_DIR_PATH]: { description: 'Optional: The absolute path to the directory to search within. If omitted, searches the current working directory.', type: 'string', }, - include_pattern: { + [GREP_PARAM_INCLUDE_PATTERN]: { description: `Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores).`, type: 'string', }, - exclude_pattern: { + [GREP_PARAM_EXCLUDE_PATTERN]: { description: 'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.', type: 'string', }, - names_only: { + [GREP_PARAM_NAMES_ONLY]: { description: 'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.', type: 'boolean', }, - max_matches_per_file: { + [GREP_PARAM_MAX_MATCHES_PER_FILE]: { description: 'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.', type: 'integer', minimum: 1, }, - total_max_matches: { + [GREP_PARAM_TOTAL_MAX_MATCHES]: { description: 'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.', type: 'integer', minimum: 1, }, }, - required: ['pattern'], + required: [PARAM_PATTERN], }, }, @@ -134,76 +182,76 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - pattern: { + [PARAM_PATTERN]: { description: `The pattern to search for. By default, treated as a Rust-flavored regular expression. Use '\\b' for precise symbol matching (e.g., '\\bMatchMe\\b').`, type: 'string', }, - dir_path: { + [PARAM_DIR_PATH]: { description: "Directory or file to search. Directories are searched recursively. Relative paths are resolved against current working directory. Defaults to current working directory ('.') if omitted.", type: 'string', }, - include_pattern: { + [GREP_PARAM_INCLUDE_PATTERN]: { description: "Glob pattern to filter files (e.g., '*.ts', 'src/**'). Recommended for large repositories to reduce noise. Defaults to all files if omitted.", type: 'string', }, - exclude_pattern: { + [GREP_PARAM_EXCLUDE_PATTERN]: { description: 'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.', type: 'string', }, - names_only: { + [GREP_PARAM_NAMES_ONLY]: { description: 'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.', type: 'boolean', }, - case_sensitive: { + [PARAM_CASE_SENSITIVE]: { description: 'If true, search is case-sensitive. Defaults to false (ignore case) if omitted.', type: 'boolean', }, - fixed_strings: { + [GREP_PARAM_FIXED_STRINGS]: { description: 'If true, treats the `pattern` as a literal string instead of a regular expression. Defaults to false (basic regex) if omitted.', type: 'boolean', }, - context: { + [GREP_PARAM_CONTEXT]: { description: 'Show this many lines of context around each match (equivalent to grep -C). Defaults to 0 if omitted.', type: 'integer', }, - after: { + [GREP_PARAM_AFTER]: { description: 'Show this many lines after each match (equivalent to grep -A). Defaults to 0 if omitted.', type: 'integer', minimum: 0, }, - before: { + [GREP_PARAM_BEFORE]: { description: 'Show this many lines before each match (equivalent to grep -B). Defaults to 0 if omitted.', type: 'integer', minimum: 0, }, - no_ignore: { + [GREP_PARAM_NO_IGNORE]: { description: 'If true, searches all files including those usually ignored (like in .gitignore, build/, dist/, etc). Defaults to false if omitted.', type: 'boolean', }, - max_matches_per_file: { + [GREP_PARAM_MAX_MATCHES_PER_FILE]: { description: 'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.', type: 'integer', minimum: 1, }, - total_max_matches: { + [GREP_PARAM_TOTAL_MAX_MATCHES]: { description: 'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.', type: 'integer', minimum: 1, }, }, - required: ['pattern'], + required: [PARAM_PATTERN], }, }, @@ -214,33 +262,33 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - pattern: { + [PARAM_PATTERN]: { description: "The glob pattern to match against (e.g., '**/*.py', 'docs/*.md').", type: 'string', }, - dir_path: { + [PARAM_DIR_PATH]: { description: 'Optional: The absolute path to the directory to search within. If omitted, searches the root directory.', type: 'string', }, - case_sensitive: { + [PARAM_CASE_SENSITIVE]: { description: 'Optional: Whether the search should be case-sensitive. Defaults to false.', type: 'boolean', }, - respect_git_ignore: { + [PARAM_RESPECT_GIT_IGNORE]: { description: 'Optional: Whether to respect .gitignore patterns when finding files. Only available in git repositories. Defaults to true.', type: 'boolean', }, - respect_gemini_ignore: { + [PARAM_RESPECT_GEMINI_IGNORE]: { description: 'Optional: Whether to respect .geminiignore patterns when finding files. Defaults to true.', type: 'boolean', }, }, - required: ['pattern'], + required: [PARAM_PATTERN], }, }, @@ -251,28 +299,28 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - dir_path: { + [PARAM_DIR_PATH]: { description: 'The path to the directory to list', type: 'string', }, - ignore: { + [LS_PARAM_IGNORE]: { description: 'List of glob patterns to ignore', items: { type: 'string', }, type: 'array', }, - file_filtering_options: { + [PARAM_FILE_FILTERING_OPTIONS]: { description: 'Optional: Whether to respect ignore patterns from .gitignore or .geminiignore', type: 'object', properties: { - respect_git_ignore: { + [PARAM_RESPECT_GIT_IGNORE]: { description: 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', type: 'boolean', }, - respect_gemini_ignore: { + [PARAM_RESPECT_GEMINI_IGNORE]: { description: 'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.', type: 'boolean', @@ -280,7 +328,7 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { }, }, }, - required: ['dir_path'], + required: [PARAM_DIR_PATH], }, }, @@ -304,11 +352,11 @@ export const DEFAULT_LEGACY_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - file_path: { + [PARAM_FILE_PATH]: { description: 'The path to the file to modify.', type: 'string', }, - instruction: { + [EDIT_PARAM_INSTRUCTION]: { description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change. A good instruction should concisely answer: @@ -326,23 +374,28 @@ A good instruction should concisely answer: `, type: 'string', }, - old_string: { + [EDIT_PARAM_OLD_STRING]: { description: 'The exact literal text to replace, preferably unescaped. For single replacements (default), include at least 3 lines of context BEFORE and AFTER the target text, matching whitespace and indentation precisely. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.', type: 'string', }, - new_string: { + [EDIT_PARAM_NEW_STRING]: { description: "The exact literal text to replace `old_string` with, preferably unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic. Do not use omission placeholders like '(rest of methods ...)', '...', or 'unchanged code'; provide exact literal code.", type: 'string', }, - allow_multiple: { + [EDIT_PARAM_ALLOW_MULTIPLE]: { type: 'boolean', description: 'If true, the tool will replace all occurrences of `old_string`. If false (default), it will only succeed if exactly one occurrence is found.', }, }, - required: ['file_path', 'instruction', 'old_string', 'new_string'], + required: [ + PARAM_FILE_PATH, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + ], }, }, @@ -353,12 +406,12 @@ A good instruction should concisely answer: parametersJsonSchema: { type: 'object', properties: { - query: { + [WEB_SEARCH_PARAM_QUERY]: { type: 'string', description: 'The search query to find information on the web.', }, }, - required: ['query'], + required: [WEB_SEARCH_PARAM_QUERY], }, }, @@ -369,13 +422,13 @@ A good instruction should concisely answer: parametersJsonSchema: { type: 'object', properties: { - prompt: { + [WEB_FETCH_PARAM_PROMPT]: { description: 'A comprehensive prompt that includes the URL(s) (up to 20) to fetch and specific instructions on how to process their content (e.g., "Summarize https://example.com/article and extract key points from https://another.com/data"). All URLs to be fetched must be valid and complete, starting with "http://" or "https://", and be fully-formed with a valid hostname (e.g., a domain name like "example.com" or an IP address). For example, "https://example.com" is valid, but "example.com" is not.', type: 'string', }, }, - required: ['prompt'], + required: [WEB_FETCH_PARAM_PROMPT], }, }, @@ -394,7 +447,7 @@ Use this tool when the user's query implies needing the content of several files parametersJsonSchema: { type: 'object', properties: { - include: { + [READ_MANY_PARAM_INCLUDE]: { type: 'array', items: { type: 'string', @@ -404,7 +457,7 @@ Use this tool when the user's query implies needing the content of several files description: 'An array of glob patterns or paths. Examples: ["src/**/*.ts"], ["README.md", "docs/"]', }, - exclude: { + [READ_MANY_PARAM_EXCLUDE]: { type: 'array', items: { type: 'string', @@ -414,30 +467,30 @@ Use this tool when the user's query implies needing the content of several files 'Optional. Glob patterns for files/directories to exclude. Added to default excludes if useDefaultExcludes is true. Example: "**/*.log", "temp/"', default: [], }, - recursive: { + [READ_MANY_PARAM_RECURSIVE]: { type: 'boolean', description: 'Optional. Whether to search recursively (primarily controlled by `**` in glob patterns). Defaults to true.', default: true, }, - useDefaultExcludes: { + [READ_MANY_PARAM_USE_DEFAULT_EXCLUDES]: { type: 'boolean', description: 'Optional. Whether to apply a list of default exclusion patterns (e.g., node_modules, .git, binary files). Defaults to true.', default: true, }, - file_filtering_options: { + [PARAM_FILE_FILTERING_OPTIONS]: { description: 'Whether to respect ignore patterns from .gitignore or .geminiignore', type: 'object', properties: { - respect_git_ignore: { + [PARAM_RESPECT_GIT_IGNORE]: { description: 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', type: 'boolean', }, - respect_gemini_ignore: { + [PARAM_RESPECT_GEMINI_IGNORE]: { description: 'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.', type: 'boolean', @@ -445,7 +498,7 @@ Use this tool when the user's query implies needing the content of several files }, }, }, - required: ['include'], + required: [READ_MANY_PARAM_INCLUDE], }, }, @@ -462,13 +515,13 @@ NEVER save workspace-specific context, local paths, or commands (e.g. "The entry parametersJsonSchema: { type: 'object', properties: { - fact: { + [MEMORY_PARAM_FACT]: { type: 'string', description: 'The specific fact or piece of information to remember. Should be a clear, self-contained statement.', }, }, - required: ['fact'], + required: [MEMORY_PARAM_FACT], additionalProperties: false, }, }, @@ -541,7 +594,7 @@ The agent did not use the todo list because this task could be completed by a ti parametersJsonSchema: { type: 'object', properties: { - todos: { + [TODOS_PARAM_TODOS]: { type: 'array', description: 'The complete list of todo items. This will replace the existing list.', @@ -549,22 +602,22 @@ The agent did not use the todo list because this task could be completed by a ti type: 'object', description: 'A single todo item.', properties: { - description: { + [TODOS_ITEM_PARAM_DESCRIPTION]: { type: 'string', description: 'The description of the task.', }, - status: { + [TODOS_ITEM_PARAM_STATUS]: { type: 'string', description: 'The current status of the task.', enum: ['pending', 'in_progress', 'completed', 'cancelled'], }, }, - required: ['description', 'status'], + required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS], additionalProperties: false, }, }, }, - required: ['todos'], + required: [TODOS_PARAM_TODOS], additionalProperties: false, }, }, @@ -576,7 +629,7 @@ The agent did not use the todo list because this task could be completed by a ti parametersJsonSchema: { type: 'object', properties: { - path: { + [DOCS_PARAM_PATH]: { description: "The relative path to the documentation file (e.g., 'cli/commands.md'). If omitted, lists all available documentation.", type: 'string', @@ -591,47 +644,54 @@ The agent did not use the todo list because this task could be completed by a ti 'Ask the user one or more questions to gather preferences, clarify requirements, or make decisions.', parametersJsonSchema: { type: 'object', - required: ['questions'], + required: [ASK_USER_PARAM_QUESTIONS], properties: { - questions: { + [ASK_USER_PARAM_QUESTIONS]: { type: 'array', minItems: 1, maxItems: 4, items: { type: 'object', - required: ['question', 'header', 'type'], + required: [ + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ], properties: { - question: { + [ASK_USER_QUESTION_PARAM_QUESTION]: { type: 'string', description: 'The complete question to ask the user. Should be clear, specific, and end with a question mark.', }, - header: { + [ASK_USER_QUESTION_PARAM_HEADER]: { type: 'string', description: 'Very short label displayed as a chip/tag. Use abbreviations: "Auth" not "Authentication", "Config" not "Configuration". Examples: "Auth method", "Library", "Approach", "Database".', }, - type: { + [ASK_USER_QUESTION_PARAM_TYPE]: { type: 'string', enum: ['choice', 'text', 'yesno'], default: 'choice', description: "Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.", }, - options: { + [ASK_USER_QUESTION_PARAM_OPTIONS]: { type: 'array', description: "The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.", items: { type: 'object', - required: ['label', 'description'], + required: [ + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + ], properties: { - label: { + [ASK_USER_OPTION_PARAM_LABEL]: { type: 'string', description: 'The display text for this option (1-5 words). Example: "OAuth 2.0"', }, - description: { + [ASK_USER_OPTION_PARAM_DESCRIPTION]: { type: 'string', description: 'Brief explanation of this option. Example: "Industry standard, supports SSO"', @@ -639,12 +699,12 @@ The agent did not use the todo list because this task could be completed by a ti }, }, }, - multiSelect: { + [ASK_USER_QUESTION_PARAM_MULTI_SELECT]: { type: 'boolean', description: "Only applies when type='choice'. Set to true to allow selecting multiple options.", }, - placeholder: { + [ASK_USER_QUESTION_PARAM_PLACEHOLDER]: { type: 'string', description: "Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.", @@ -663,7 +723,7 @@ The agent did not use the todo list because this task could be completed by a ti parametersJsonSchema: { type: 'object', properties: { - reason: { + [PLAN_MODE_PARAM_REASON]: { type: 'string', description: 'Short reason explaining why you are entering plan mode.', diff --git a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts index 7c4fddc9f6..d879e4fd43 100644 --- a/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts +++ b/packages/core/src/tools/definitions/model-family-sets/gemini-3.ts @@ -25,6 +25,54 @@ import { GET_INTERNAL_DOCS_TOOL_NAME, ASK_USER_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, + // Shared parameter names + PARAM_FILE_PATH, + PARAM_DIR_PATH, + PARAM_PATTERN, + PARAM_CASE_SENSITIVE, + PARAM_RESPECT_GIT_IGNORE, + PARAM_RESPECT_GEMINI_IGNORE, + PARAM_FILE_FILTERING_OPTIONS, + // Tool-specific parameter names + READ_FILE_PARAM_START_LINE, + READ_FILE_PARAM_END_LINE, + WRITE_FILE_PARAM_CONTENT, + GREP_PARAM_INCLUDE_PATTERN, + GREP_PARAM_EXCLUDE_PATTERN, + GREP_PARAM_NAMES_ONLY, + GREP_PARAM_MAX_MATCHES_PER_FILE, + GREP_PARAM_TOTAL_MAX_MATCHES, + GREP_PARAM_FIXED_STRINGS, + GREP_PARAM_CONTEXT, + GREP_PARAM_AFTER, + GREP_PARAM_BEFORE, + GREP_PARAM_NO_IGNORE, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + EDIT_PARAM_ALLOW_MULTIPLE, + LS_PARAM_IGNORE, + WEB_SEARCH_PARAM_QUERY, + WEB_FETCH_PARAM_PROMPT, + READ_MANY_PARAM_INCLUDE, + READ_MANY_PARAM_EXCLUDE, + READ_MANY_PARAM_RECURSIVE, + READ_MANY_PARAM_USE_DEFAULT_EXCLUDES, + MEMORY_PARAM_FACT, + TODOS_PARAM_TODOS, + TODOS_ITEM_PARAM_DESCRIPTION, + TODOS_ITEM_PARAM_STATUS, + DOCS_PARAM_PATH, + ASK_USER_PARAM_QUESTIONS, + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ASK_USER_QUESTION_PARAM_OPTIONS, + ASK_USER_QUESTION_PARAM_MULTI_SELECT, + ASK_USER_QUESTION_PARAM_PLACEHOLDER, + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + PLAN_MODE_PARAM_REASON, } from '../base-declarations.js'; import { getShellDeclaration, @@ -42,22 +90,22 @@ export const GEMINI_3_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - file_path: { + [PARAM_FILE_PATH]: { description: 'The path to the file to read.', type: 'string', }, - start_line: { + [READ_FILE_PARAM_START_LINE]: { description: 'Optional: The 1-based line number to start reading from.', type: 'number', }, - end_line: { + [READ_FILE_PARAM_END_LINE]: { description: 'Optional: The 1-based line number to end reading at (inclusive).', type: 'number', }, }, - required: ['file_path'], + required: [PARAM_FILE_PATH], }, }, @@ -67,17 +115,17 @@ export const GEMINI_3_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - file_path: { + [PARAM_FILE_PATH]: { description: 'Path to the file.', type: 'string', }, - content: { + [WRITE_FILE_PARAM_CONTENT]: { description: "The complete content to write. Provide the full file; do not use placeholders like '// ... rest of code'.", type: 'string', }, }, - required: ['file_path', 'content'], + required: [PARAM_FILE_PATH, WRITE_FILE_PARAM_CONTENT], }, }, @@ -88,43 +136,43 @@ export const GEMINI_3_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - pattern: { + [PARAM_PATTERN]: { description: `The regular expression (regex) pattern to search for within file contents (e.g., 'function\\s+myFunction', 'import\\s+\\{.*\\}\\s+from\\s+.*').`, type: 'string', }, - dir_path: { + [PARAM_DIR_PATH]: { description: 'Optional: The absolute path to the directory to search within. If omitted, searches the current working directory.', type: 'string', }, - include_pattern: { + [GREP_PARAM_INCLUDE_PATTERN]: { description: `Optional: A glob pattern to filter which files are searched (e.g., '*.js', '*.{ts,tsx}', 'src/**'). If omitted, searches all files (respecting potential global ignores).`, type: 'string', }, - exclude_pattern: { + [GREP_PARAM_EXCLUDE_PATTERN]: { description: 'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.', type: 'string', }, - names_only: { + [GREP_PARAM_NAMES_ONLY]: { description: 'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.', type: 'boolean', }, - max_matches_per_file: { + [GREP_PARAM_MAX_MATCHES_PER_FILE]: { description: 'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.', type: 'integer', minimum: 1, }, - total_max_matches: { + [GREP_PARAM_TOTAL_MAX_MATCHES]: { description: 'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.', type: 'integer', minimum: 1, }, }, - required: ['pattern'], + required: [PARAM_PATTERN], }, }, @@ -135,76 +183,76 @@ export const GEMINI_3_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - pattern: { + [PARAM_PATTERN]: { description: `The pattern to search for. By default, treated as a Rust-flavored regular expression. Use '\\b' for precise symbol matching (e.g., '\\bMatchMe\\b').`, type: 'string', }, - dir_path: { + [PARAM_DIR_PATH]: { description: "Directory or file to search. Directories are searched recursively. Relative paths are resolved against current working directory. Defaults to current working directory ('.') if omitted.", type: 'string', }, - include_pattern: { + [GREP_PARAM_INCLUDE_PATTERN]: { description: "Glob pattern to filter files (e.g., '*.ts', 'src/**'). Recommended for large repositories to reduce noise. Defaults to all files if omitted.", type: 'string', }, - exclude_pattern: { + [GREP_PARAM_EXCLUDE_PATTERN]: { description: 'Optional: A regular expression pattern to exclude from the search results. If a line matches both the pattern and the exclude_pattern, it will be omitted.', type: 'string', }, - names_only: { + [GREP_PARAM_NAMES_ONLY]: { description: 'Optional: If true, only the file paths of the matches will be returned, without the line content or line numbers. This is useful for gathering a list of files.', type: 'boolean', }, - case_sensitive: { + [PARAM_CASE_SENSITIVE]: { description: 'If true, search is case-sensitive. Defaults to false (ignore case) if omitted.', type: 'boolean', }, - fixed_strings: { + [GREP_PARAM_FIXED_STRINGS]: { description: 'If true, treats the `pattern` as a literal string instead of a regular expression. Defaults to false (basic regex) if omitted.', type: 'boolean', }, - context: { + [GREP_PARAM_CONTEXT]: { description: 'Show this many lines of context around each match (equivalent to grep -C). Defaults to 0 if omitted.', type: 'integer', }, - after: { + [GREP_PARAM_AFTER]: { description: 'Show this many lines after each match (equivalent to grep -A). Defaults to 0 if omitted.', type: 'integer', minimum: 0, }, - before: { + [GREP_PARAM_BEFORE]: { description: 'Show this many lines before each match (equivalent to grep -B). Defaults to 0 if omitted.', type: 'integer', minimum: 0, }, - no_ignore: { + [GREP_PARAM_NO_IGNORE]: { description: 'If true, searches all files including those usually ignored (like in .gitignore, build/, dist/, etc). Defaults to false if omitted.', type: 'boolean', }, - max_matches_per_file: { + [GREP_PARAM_MAX_MATCHES_PER_FILE]: { description: 'Optional: Maximum number of matches to return per file. Use this to prevent being overwhelmed by repetitive matches in large files.', type: 'integer', minimum: 1, }, - total_max_matches: { + [GREP_PARAM_TOTAL_MAX_MATCHES]: { description: 'Optional: Maximum number of total matches to return. Use this to limit the overall size of the response. Defaults to 100 if omitted.', type: 'integer', minimum: 1, }, }, - required: ['pattern'], + required: [PARAM_PATTERN], }, }, @@ -215,33 +263,33 @@ export const GEMINI_3_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - pattern: { + [PARAM_PATTERN]: { description: "The glob pattern to match against (e.g., '**/*.py', 'docs/*.md').", type: 'string', }, - dir_path: { + [PARAM_DIR_PATH]: { description: 'Optional: The absolute path to the directory to search within. If omitted, searches the root directory.', type: 'string', }, - case_sensitive: { + [PARAM_CASE_SENSITIVE]: { description: 'Optional: Whether the search should be case-sensitive. Defaults to false.', type: 'boolean', }, - respect_git_ignore: { + [PARAM_RESPECT_GIT_IGNORE]: { description: 'Optional: Whether to respect .gitignore patterns when finding files. Only available in git repositories. Defaults to true.', type: 'boolean', }, - respect_gemini_ignore: { + [PARAM_RESPECT_GEMINI_IGNORE]: { description: 'Optional: Whether to respect .geminiignore patterns when finding files. Defaults to true.', type: 'boolean', }, }, - required: ['pattern'], + required: [PARAM_PATTERN], }, }, @@ -252,28 +300,28 @@ export const GEMINI_3_SET: CoreToolSet = { parametersJsonSchema: { type: 'object', properties: { - dir_path: { + [PARAM_DIR_PATH]: { description: 'The path to the directory to list', type: 'string', }, - ignore: { + [LS_PARAM_IGNORE]: { description: 'List of glob patterns to ignore', items: { type: 'string', }, type: 'array', }, - file_filtering_options: { + [PARAM_FILE_FILTERING_OPTIONS]: { description: 'Optional: Whether to respect ignore patterns from .gitignore or .geminiignore', type: 'object', properties: { - respect_git_ignore: { + [PARAM_RESPECT_GIT_IGNORE]: { description: 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', type: 'boolean', }, - respect_gemini_ignore: { + [PARAM_RESPECT_GEMINI_IGNORE]: { description: 'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.', type: 'boolean', @@ -281,7 +329,7 @@ export const GEMINI_3_SET: CoreToolSet = { }, }, }, - required: ['dir_path'], + required: [PARAM_DIR_PATH], }, }, @@ -295,31 +343,36 @@ The user has the ability to modify the \`new_string\` content. If modified, this parametersJsonSchema: { type: 'object', properties: { - file_path: { + [PARAM_FILE_PATH]: { description: 'The path to the file to modify.', type: 'string', }, - instruction: { + [EDIT_PARAM_INSTRUCTION]: { description: `A clear, semantic instruction for the code change, acting as a high-quality prompt for an expert LLM assistant. It must be self-contained and explain the goal of the change.`, type: 'string', }, - old_string: { + [EDIT_PARAM_OLD_STRING]: { description: 'The exact literal text to replace, unescaped. If this string is not the exact literal text (i.e. you escaped it) or does not match exactly, the tool will fail.', type: 'string', }, - new_string: { + [EDIT_PARAM_NEW_STRING]: { description: "The exact literal text to replace `old_string` with, unescaped. Provide the EXACT text. Ensure the resulting code is correct and idiomatic. Do not use omission placeholders like '(rest of methods ...)', '...', or 'unchanged code'; provide exact literal code.", type: 'string', }, - allow_multiple: { + [EDIT_PARAM_ALLOW_MULTIPLE]: { type: 'boolean', description: 'If true, the tool will replace all occurrences of `old_string`. If false (default), it will only succeed if exactly one occurrence is found.', }, }, - required: ['file_path', 'instruction', 'old_string', 'new_string'], + required: [ + PARAM_FILE_PATH, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + ], }, }, @@ -329,13 +382,13 @@ The user has the ability to modify the \`new_string\` content. If modified, this parametersJsonSchema: { type: 'object', properties: { - query: { + [WEB_SEARCH_PARAM_QUERY]: { type: 'string', description: "The search query. Supports natural language questions (e.g., 'Latest breaking changes in React 19') or specific technical queries.", }, }, - required: ['query'], + required: [WEB_SEARCH_PARAM_QUERY], }, }, @@ -346,13 +399,13 @@ The user has the ability to modify the \`new_string\` content. If modified, this parametersJsonSchema: { type: 'object', properties: { - prompt: { + [WEB_FETCH_PARAM_PROMPT]: { description: 'A string containing the URL(s) and your specific analysis instructions. Be clear about what information you want to find or summarize. Supports up to 20 URLs.', type: 'string', }, }, - required: ['prompt'], + required: [WEB_FETCH_PARAM_PROMPT], }, }, @@ -371,7 +424,7 @@ Use this tool when the user's query implies needing the content of several files parametersJsonSchema: { type: 'object', properties: { - include: { + [READ_MANY_PARAM_INCLUDE]: { type: 'array', items: { type: 'string', @@ -381,7 +434,7 @@ Use this tool when the user's query implies needing the content of several files description: 'An array of glob patterns or paths. Examples: ["src/**/*.ts"], ["README.md", "docs/"]', }, - exclude: { + [READ_MANY_PARAM_EXCLUDE]: { type: 'array', items: { type: 'string', @@ -391,30 +444,30 @@ Use this tool when the user's query implies needing the content of several files 'Optional. Glob patterns for files/directories to exclude. Added to default excludes if useDefaultExcludes is true. Example: "**/*.log", "temp/"', default: [], }, - recursive: { + [READ_MANY_PARAM_RECURSIVE]: { type: 'boolean', description: 'Optional. Whether to search recursively (primarily controlled by `**` in glob patterns). Defaults to true.', default: true, }, - useDefaultExcludes: { + [READ_MANY_PARAM_USE_DEFAULT_EXCLUDES]: { type: 'boolean', description: 'Optional. Whether to apply a list of default exclusion patterns (e.g., node_modules, .git, binary files). Defaults to true.', default: true, }, - file_filtering_options: { + [PARAM_FILE_FILTERING_OPTIONS]: { description: 'Whether to respect ignore patterns from .gitignore or .geminiignore', type: 'object', properties: { - respect_git_ignore: { + [PARAM_RESPECT_GIT_IGNORE]: { description: 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', type: 'boolean', }, - respect_gemini_ignore: { + [PARAM_RESPECT_GEMINI_IGNORE]: { description: 'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.', type: 'boolean', @@ -422,7 +475,7 @@ Use this tool when the user's query implies needing the content of several files }, }, }, - required: ['include'], + required: [READ_MANY_PARAM_INCLUDE], }, }, @@ -432,13 +485,13 @@ Use this tool when the user's query implies needing the content of several files parametersJsonSchema: { type: 'object', properties: { - fact: { + [MEMORY_PARAM_FACT]: { type: 'string', description: "A concise, global fact or preference (e.g., 'I prefer using tabs'). Do not include local paths or project-specific names.", }, }, - required: ['fact'], + required: [MEMORY_PARAM_FACT], additionalProperties: false, }, }, @@ -511,7 +564,7 @@ The agent did not use the todo list because this task could be completed by a ti parametersJsonSchema: { type: 'object', properties: { - todos: { + [TODOS_PARAM_TODOS]: { type: 'array', description: 'The complete list of todo items. This will replace the existing list.', @@ -519,22 +572,22 @@ The agent did not use the todo list because this task could be completed by a ti type: 'object', description: 'A single todo item.', properties: { - description: { + [TODOS_ITEM_PARAM_DESCRIPTION]: { type: 'string', description: 'The description of the task.', }, - status: { + [TODOS_ITEM_PARAM_STATUS]: { type: 'string', description: 'The current status of the task.', enum: ['pending', 'in_progress', 'completed', 'cancelled'], }, }, - required: ['description', 'status'], + required: [TODOS_ITEM_PARAM_DESCRIPTION, TODOS_ITEM_PARAM_STATUS], additionalProperties: false, }, }, }, - required: ['todos'], + required: [TODOS_PARAM_TODOS], additionalProperties: false, }, }, @@ -546,7 +599,7 @@ The agent did not use the todo list because this task could be completed by a ti parametersJsonSchema: { type: 'object', properties: { - path: { + [DOCS_PARAM_PATH]: { description: "The relative path to the documentation file (e.g., 'cli/commands.md'). If omitted, lists all available documentation.", type: 'string', @@ -561,47 +614,54 @@ The agent did not use the todo list because this task could be completed by a ti 'Ask the user one or more questions to gather preferences, clarify requirements, or make decisions. When using this tool, prefer providing multiple-choice options with detailed descriptions and enable multi-select where appropriate to provide maximum flexibility.', parametersJsonSchema: { type: 'object', - required: ['questions'], + required: [ASK_USER_PARAM_QUESTIONS], properties: { - questions: { + [ASK_USER_PARAM_QUESTIONS]: { type: 'array', minItems: 1, maxItems: 4, items: { type: 'object', - required: ['question', 'header', 'type'], + required: [ + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ], properties: { - question: { + [ASK_USER_QUESTION_PARAM_QUESTION]: { type: 'string', description: 'The complete question to ask the user. Should be clear, specific, and end with a question mark.', }, - header: { + [ASK_USER_QUESTION_PARAM_HEADER]: { type: 'string', description: 'Very short label displayed as a chip/tag. Use abbreviations: "Auth" not "Authentication", "Config" not "Configuration". Examples: "Auth method", "Library", "Approach", "Database".', }, - type: { + [ASK_USER_QUESTION_PARAM_TYPE]: { type: 'string', enum: ['choice', 'text', 'yesno'], default: 'choice', description: "Question type: 'choice' (default) for multiple-choice with options, 'text' for free-form input, 'yesno' for Yes/No confirmation.", }, - options: { + [ASK_USER_QUESTION_PARAM_OPTIONS]: { type: 'array', description: "The selectable choices for 'choice' type questions. Provide 2-4 options. An 'Other' option is automatically added. Not needed for 'text' or 'yesno' types.", items: { type: 'object', - required: ['label', 'description'], + required: [ + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + ], properties: { - label: { + [ASK_USER_OPTION_PARAM_LABEL]: { type: 'string', description: 'The display text for this option (1-5 words). Example: "OAuth 2.0"', }, - description: { + [ASK_USER_OPTION_PARAM_DESCRIPTION]: { type: 'string', description: 'Brief explanation of this option. Example: "Industry standard, supports SSO"', @@ -609,12 +669,12 @@ The agent did not use the todo list because this task could be completed by a ti }, }, }, - multiSelect: { + [ASK_USER_QUESTION_PARAM_MULTI_SELECT]: { type: 'boolean', description: "Only applies when type='choice'. Set to true to allow selecting multiple options.", }, - placeholder: { + [ASK_USER_QUESTION_PARAM_PLACEHOLDER]: { type: 'string', description: "Hint text shown in the input field. For type='text', shown in the main input. For type='choice', shown in the 'Other' custom input.", @@ -633,7 +693,7 @@ The agent did not use the todo list because this task could be completed by a ti parametersJsonSchema: { type: 'object', properties: { - reason: { + [PLAN_MODE_PARAM_REASON]: { type: 'string', description: 'Short reason explaining why you are entering plan mode.', diff --git a/packages/core/src/tools/tool-names.ts b/packages/core/src/tools/tool-names.ts index 9905fb44b3..a2e8061fc6 100644 --- a/packages/core/src/tools/tool-names.ts +++ b/packages/core/src/tools/tool-names.ts @@ -22,6 +22,59 @@ import { ASK_USER_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, + // Shared parameter names + PARAM_FILE_PATH, + PARAM_DIR_PATH, + PARAM_PATTERN, + PARAM_CASE_SENSITIVE, + PARAM_RESPECT_GIT_IGNORE, + PARAM_RESPECT_GEMINI_IGNORE, + PARAM_FILE_FILTERING_OPTIONS, + PARAM_DESCRIPTION, + // Tool-specific parameter names + READ_FILE_PARAM_START_LINE, + READ_FILE_PARAM_END_LINE, + WRITE_FILE_PARAM_CONTENT, + GREP_PARAM_INCLUDE_PATTERN, + GREP_PARAM_EXCLUDE_PATTERN, + GREP_PARAM_NAMES_ONLY, + GREP_PARAM_MAX_MATCHES_PER_FILE, + GREP_PARAM_TOTAL_MAX_MATCHES, + GREP_PARAM_FIXED_STRINGS, + GREP_PARAM_CONTEXT, + GREP_PARAM_AFTER, + GREP_PARAM_BEFORE, + GREP_PARAM_NO_IGNORE, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + EDIT_PARAM_ALLOW_MULTIPLE, + LS_PARAM_IGNORE, + SHELL_PARAM_COMMAND, + SHELL_PARAM_IS_BACKGROUND, + WEB_SEARCH_PARAM_QUERY, + WEB_FETCH_PARAM_PROMPT, + READ_MANY_PARAM_INCLUDE, + READ_MANY_PARAM_EXCLUDE, + READ_MANY_PARAM_RECURSIVE, + READ_MANY_PARAM_USE_DEFAULT_EXCLUDES, + MEMORY_PARAM_FACT, + TODOS_PARAM_TODOS, + TODOS_ITEM_PARAM_DESCRIPTION, + TODOS_ITEM_PARAM_STATUS, + DOCS_PARAM_PATH, + ASK_USER_PARAM_QUESTIONS, + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ASK_USER_QUESTION_PARAM_OPTIONS, + ASK_USER_QUESTION_PARAM_MULTI_SELECT, + ASK_USER_QUESTION_PARAM_PLACEHOLDER, + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + PLAN_MODE_PARAM_REASON, + EXIT_PLAN_PARAM_PLAN_PATH, + SKILL_PARAM_NAME, } from './definitions/coreTools.js'; export { @@ -42,6 +95,59 @@ export { ASK_USER_TOOL_NAME, EXIT_PLAN_MODE_TOOL_NAME, ENTER_PLAN_MODE_TOOL_NAME, + // Shared parameter names + PARAM_FILE_PATH, + PARAM_DIR_PATH, + PARAM_PATTERN, + PARAM_CASE_SENSITIVE, + PARAM_RESPECT_GIT_IGNORE, + PARAM_RESPECT_GEMINI_IGNORE, + PARAM_FILE_FILTERING_OPTIONS, + PARAM_DESCRIPTION, + // Tool-specific parameter names + READ_FILE_PARAM_START_LINE, + READ_FILE_PARAM_END_LINE, + WRITE_FILE_PARAM_CONTENT, + GREP_PARAM_INCLUDE_PATTERN, + GREP_PARAM_EXCLUDE_PATTERN, + GREP_PARAM_NAMES_ONLY, + GREP_PARAM_MAX_MATCHES_PER_FILE, + GREP_PARAM_TOTAL_MAX_MATCHES, + GREP_PARAM_FIXED_STRINGS, + GREP_PARAM_CONTEXT, + GREP_PARAM_AFTER, + GREP_PARAM_BEFORE, + GREP_PARAM_NO_IGNORE, + EDIT_PARAM_INSTRUCTION, + EDIT_PARAM_OLD_STRING, + EDIT_PARAM_NEW_STRING, + EDIT_PARAM_ALLOW_MULTIPLE, + LS_PARAM_IGNORE, + SHELL_PARAM_COMMAND, + SHELL_PARAM_IS_BACKGROUND, + WEB_SEARCH_PARAM_QUERY, + WEB_FETCH_PARAM_PROMPT, + READ_MANY_PARAM_INCLUDE, + READ_MANY_PARAM_EXCLUDE, + READ_MANY_PARAM_RECURSIVE, + READ_MANY_PARAM_USE_DEFAULT_EXCLUDES, + MEMORY_PARAM_FACT, + TODOS_PARAM_TODOS, + TODOS_ITEM_PARAM_DESCRIPTION, + TODOS_ITEM_PARAM_STATUS, + DOCS_PARAM_PATH, + ASK_USER_PARAM_QUESTIONS, + ASK_USER_QUESTION_PARAM_QUESTION, + ASK_USER_QUESTION_PARAM_HEADER, + ASK_USER_QUESTION_PARAM_TYPE, + ASK_USER_QUESTION_PARAM_OPTIONS, + ASK_USER_QUESTION_PARAM_MULTI_SELECT, + ASK_USER_QUESTION_PARAM_PLACEHOLDER, + ASK_USER_OPTION_PARAM_LABEL, + ASK_USER_OPTION_PARAM_DESCRIPTION, + PLAN_MODE_PARAM_REASON, + EXIT_PLAN_PARAM_PLAN_PATH, + SKILL_PARAM_NAME, }; export const LS_TOOL_NAME_LEGACY = 'list_directory'; // Just to be safe if anything used the old exported name directly