Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -437,6 +437,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 @@ -668,6 +668,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 @@ -1002,6 +1003,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 @@ -1080,6 +1085,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 @@ -172,6 +172,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 @@ -283,6 +284,7 @@ export interface ConfigParameters {
truncateToolOutputThreshold?: number;
truncateToolOutputLines?: number;
enableToolOutputTruncation?: boolean;
ripgrepMaxMatches?: number;
eventEmitter?: EventEmitter;
useSmartEdit?: boolean;
useWriteTodos?: boolean;
Expand Down Expand Up @@ -393,6 +395,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 @@ -517,6 +520,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.initialUseModelRouter = params.useModelRouter ?? false;
Expand Down Expand Up @@ -1268,6 +1273,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 @@ -986,6 +986,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