mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-18 09:11:55 -07:00
refactor(tools): Move all tool names into tool-names.ts (#11493)
This commit is contained in:
@@ -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 () => {
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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
|
||||
});
|
||||
|
||||
@@ -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: {
|
||||
|
||||
@@ -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 = <TOutput extends z.ZodTypeAny>(
|
||||
tools: Array<string | MockTool> = [LSTool.Name],
|
||||
tools: Array<string | MockTool> = [LS_TOOL_NAME],
|
||||
runConfigOverrides: Partial<AgentDefinition<TOutput>['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) => {
|
||||
|
||||
@@ -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()) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -409,7 +409,7 @@ class EditToolInvocation implements ToolInvocation<EditToolParams, ToolResult> {
|
||||
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<EditToolParams, ToolResult>
|
||||
implements ModifiableDeclarativeTool<EditToolParams>
|
||||
{
|
||||
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.
|
||||
|
||||
|
||||
@@ -260,11 +260,9 @@ class GlobToolInvocation extends BaseToolInvocation<
|
||||
* Implementation of the Glob tool logic
|
||||
*/
|
||||
export class GlobTool extends BaseDeclarativeTool<GlobToolParams, ToolResult> {
|
||||
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,
|
||||
|
||||
@@ -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<LSToolParams, ToolResult> {
|
||||
* Implementation of the LS tool logic
|
||||
*/
|
||||
export class LSTool extends BaseDeclarativeTool<LSToolParams, ToolResult> {
|
||||
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,
|
||||
|
||||
@@ -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<SaveMemoryParams, ToolResult>
|
||||
implements ModifiableDeclarativeTool<SaveMemoryParams>
|
||||
{
|
||||
static readonly Name: string = memoryToolSchemaData.name!;
|
||||
constructor() {
|
||||
super(
|
||||
MemoryTool.Name,
|
||||
MEMORY_TOOL_NAME,
|
||||
'Save Memory',
|
||||
memoryToolDescription,
|
||||
Kind.Think,
|
||||
|
||||
@@ -815,11 +815,9 @@ export class SmartEditTool
|
||||
extends BaseDeclarativeTool<EditToolParams, ToolResult>
|
||||
implements ModifiableDeclarativeTool<EditToolParams>
|
||||
{
|
||||
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.
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<WriteFileToolParams, ToolResult>
|
||||
implements ModifiableDeclarativeTool<WriteFileToolParams>
|
||||
{
|
||||
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.
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user