Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/cli/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ they appear in the UI.
| Show Color | `tools.shell.showColor` | Show color in shell output. | `false` |
| Auto Accept | `tools.autoAccept` | Automatically accept and execute tool calls that are considered safe (e.g., read-only operations). | `false` |
| Use Ripgrep | `tools.useRipgrep` | Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance. | `true` |
| Ripgrep Max Matches | `tools.ripgrep.maxMatches` | Maximum number of matches returned by the ripgrep tool. Increase for large repos or lower to conserve context. | `20000` |
| Enable Tool Output Truncation | `tools.enableToolOutputTruncation` | Enable truncation of large tool outputs. | `true` |
| Tool Output Truncation Threshold | `tools.truncateToolOutputThreshold` | Truncate tool output if it is larger than this many characters. Set to -1 to disable. | `10000` |
| Tool Output Truncation Lines | `tools.truncateToolOutputLines` | The number of lines to keep when truncating tool output. | `100` |
Expand Down
5 changes: 5 additions & 0 deletions docs/get-started/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -617,6 +617,11 @@ their corresponding top-level category object in your `settings.json` file.
implementation. Provides faster search performance.
- **Default:** `true`

- **`tools.ripgrep.maxMatches`** (number):
- **Description:** Maximum number of matches returned by the ripgrep tool.
Increase for larger repositories or decrease to conserve context.
- **Default:** `20000`

- **`tools.enableToolOutputTruncation`** (boolean):
- **Description:** Enable truncation of large tool outputs.
- **Default:** `true`
Expand Down
1 change: 1 addition & 0 deletions packages/cli/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -651,6 +651,7 @@ export async function loadCliConfig(
truncateToolOutputThreshold: settings.tools?.truncateToolOutputThreshold,
truncateToolOutputLines: settings.tools?.truncateToolOutputLines,
enableToolOutputTruncation: settings.tools?.enableToolOutputTruncation,
ripgrepMaxMatches: settings.tools?.ripgrep?.maxMatches,
eventEmitter: appEvents,
useSmartEdit: argv.useSmartEdit ?? settings.useSmartEdit,
useWriteTodos: argv.useWriteTodos ?? settings.useWriteTodos,
Expand Down
22 changes: 22 additions & 0 deletions packages/cli/src/config/settingsSchema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import type {
import {
DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES,
DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD,
DEFAULT_RIPGREP_MAX_MATCHES,
DEFAULT_GEMINI_MODEL,
DEFAULT_MODEL_CONFIGS,
} from '@google/gemini-cli-core';
Expand Down Expand Up @@ -1012,6 +1013,27 @@ const SETTINGS_SCHEMA = {
'Use ripgrep for file content search instead of the fallback implementation. Provides faster search performance.',
showInDialog: true,
},
ripgrep: {
type: 'object',
label: 'Ripgrep',
category: 'Tools',
requiresRestart: false,
default: {},
description: 'Ripgrep-specific settings.',
showInDialog: false,
properties: {
maxMatches: {
type: 'number',
label: 'Ripgrep Max Matches',
category: 'Tools',
requiresRestart: false,
default: DEFAULT_RIPGREP_MAX_MATCHES,
description:
'Maximum number of matches returned by the ripgrep tool. Increase for larger repositories or decrease to conserve context.',
showInDialog: true,
},
},
},
enableToolOutputTruncation: {
type: 'boolean',
label: 'Enable Tool Output Truncation',
Expand Down
23 changes: 22 additions & 1 deletion packages/core/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import type { Mock } from 'vitest';
import type { ConfigParameters, SandboxConfig } from './config.js';
import { Config, DEFAULT_FILE_FILTERING_OPTIONS } from './config.js';
import {
Config,
DEFAULT_FILE_FILTERING_OPTIONS,
DEFAULT_RIPGREP_MAX_MATCHES,
} from './config.js';
import { ExperimentFlags } from '../code_assist/experiments/flagNames.js';
import { debugLogger } from '../utils/debugLogger.js';
import { ApprovalMode } from '../policy/types.js';
Expand Down Expand Up @@ -70,6 +74,7 @@ vi.mock('../tools/grep.js');
vi.mock('../tools/ripGrep.js', () => ({
canUseRipgrep: vi.fn(),
RipGrepTool: class MockRipGrepTool {},
RIPGREP_DEFAULT_MAX_MATCHES: 20000,
}));
vi.mock('../tools/glob');
vi.mock('../tools/edit');
Expand Down Expand Up @@ -987,6 +992,22 @@ describe('Server Config (config.ts)', () => {
});
});

describe('getRipgrepMaxMatches', () => {
it('returns the default value when not configured', () => {
const config = new Config(baseParams);
expect(config.getRipgrepMaxMatches()).toBe(DEFAULT_RIPGREP_MAX_MATCHES);
});

it('returns the configured value when provided', () => {
const customValue = 1234;
const config = new Config({
...baseParams,
ripgrepMaxMatches: customValue,
});
expect(config.getRipgrepMaxMatches()).toBe(customValue);
});
});

describe('Proxy Configuration Error Handling', () => {
beforeEach(() => {
vi.clearAllMocks();
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ export {

export const DEFAULT_TRUNCATE_TOOL_OUTPUT_THRESHOLD = 4_000_000;
export const DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES = 1000;
export const DEFAULT_RIPGREP_MAX_MATCHES = 20_000;

export class MCPServerConfig {
constructor(
Expand Down Expand Up @@ -282,6 +283,7 @@ export interface ConfigParameters {
truncateToolOutputThreshold?: number;
truncateToolOutputLines?: number;
enableToolOutputTruncation?: boolean;
ripgrepMaxMatches?: number;
eventEmitter?: EventEmitter;
useSmartEdit?: boolean;
useWriteTodos?: boolean;
Expand Down Expand Up @@ -391,6 +393,7 @@ export class Config {
private readonly truncateToolOutputThreshold: number;
private readonly truncateToolOutputLines: number;
private readonly enableToolOutputTruncation: boolean;
private readonly ripgrepMaxMatches: number;
private initialized: boolean = false;
readonly storage: Storage;
private readonly fileExclusions: FileExclusions;
Expand Down Expand Up @@ -512,6 +515,8 @@ export class Config {
this.truncateToolOutputLines =
params.truncateToolOutputLines ?? DEFAULT_TRUNCATE_TOOL_OUTPUT_LINES;
this.enableToolOutputTruncation = params.enableToolOutputTruncation ?? true;
this.ripgrepMaxMatches =
params.ripgrepMaxMatches ?? DEFAULT_RIPGREP_MAX_MATCHES;
this.useSmartEdit = params.useSmartEdit ?? true;
this.useWriteTodos = params.useWriteTodos ?? true;
this.enableHooks = params.enableHooks ?? false;
Expand Down Expand Up @@ -1263,6 +1268,10 @@ export class Config {
return this.useRipgrep;
}

getRipgrepMaxMatches(): number {
return this.ripgrepMaxMatches;
}

getEnableInteractiveShell(): boolean {
return this.enableInteractiveShell;
}
Expand Down
36 changes: 36 additions & 0 deletions packages/core/src/tools/ripGrep.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -530,6 +530,42 @@ describe('RipGrepTool', () => {
expect(result.returnDisplay).toBe('Found 1 match');
});

it('should respect the configured ripgrep max matches', async () => {
const limitedConfig = {
...mockConfig,
getRipgrepMaxMatches: () => 5,
} as unknown as Config;
const limitedTool = new RipGrepTool(limitedConfig);

const outputData = Array.from({ length: 6 }, (_, index) =>
JSON.stringify({
type: 'match',
data: {
path: { text: `file${index}.txt` },
line_number: index + 1,
lines: { text: `match ${index}\n` },
},
}),
).join('\n');

mockSpawn.mockImplementationOnce(
createMockSpawn({
outputData,
exitCode: 0,
}),
);

const params: RipGrepToolParams = { pattern: 'match' };
const invocation = limitedTool.build(params);
const result = await invocation.execute(abortSignal);

expect(result.returnDisplay).toBe('Found 5 matches (limited)');
expect(result.llmContent).toContain(
'(results limited to 5 matches for performance)',
);
expect(result.llmContent).not.toContain('File: file5.txt');
});

it('should return "No matches found" when pattern does not exist', async () => {
// Setup specific mock for no matches
mockSpawn.mockImplementationOnce(
Expand Down
7 changes: 3 additions & 4 deletions packages/core/src/tools/ripGrep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { BaseDeclarativeTool, BaseToolInvocation, Kind } from './tools.js';
import { SchemaValidator } from '../utils/schemaValidator.js';
import { makeRelative, shortenPath } from '../utils/paths.js';
import { getErrorMessage, isNodeError } from '../utils/errors.js';
import type { Config } from '../config/config.js';
import { DEFAULT_RIPGREP_MAX_MATCHES, 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';
Expand All @@ -24,8 +24,6 @@ import {
COMMON_DIRECTORY_EXCLUDES,
} from '../utils/ignorePatterns.js';

const DEFAULT_TOTAL_MAX_MATCHES = 20000;

function getRgCandidateFilenames(): readonly string[] {
return process.platform === 'win32' ? ['rg.exe', 'rg'] : ['rg'];
}
Expand Down Expand Up @@ -206,7 +204,8 @@ class GrepToolInvocation extends BaseToolInvocation<
const searchDirAbs = resolveAndValidatePath(this.config, pathParam);
const searchDirDisplay = pathParam;

const totalMaxMatches = DEFAULT_TOTAL_MAX_MATCHES;
const totalMaxMatches =
this.config.getRipgrepMaxMatches() ?? DEFAULT_RIPGREP_MAX_MATCHES;
if (this.config.getDebugMode()) {
debugLogger.log(`[GrepTool] Total result limit: ${totalMaxMatches}`);
}
Expand Down
17 changes: 17 additions & 0 deletions schemas/settings.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -994,6 +994,23 @@
"default": true,
"type": "boolean"
},
"ripgrep": {
"title": "Ripgrep",
"description": "Ripgrep-specific settings.",
"markdownDescription": "Ripgrep-specific settings.\n\n- Category: `Tools`\n- Requires restart: `no`\n- Default: `{}`",
"default": {},
"type": "object",
"properties": {
"maxMatches": {
"title": "Ripgrep Max Matches",
"description": "Maximum number of matches returned by the ripgrep tool. Increase for larger repositories or decrease to conserve context.",
"markdownDescription": "Maximum number of matches returned by the ripgrep tool. Increase for larger repositories or decrease to conserve context.\n\n- Category: `Tools`\n- Requires restart: `no`\n- Default: `20000`",
"default": 20000,
"type": "number"
}
},
"additionalProperties": false
},
"enableToolOutputTruncation": {
"title": "Enable Tool Output Truncation",
"description": "Enable truncation of large tool outputs.",
Expand Down