Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
096d267
feat(core): share file list patterns between glob and grep tools
Han5991 Aug 15, 2025
ed4dade
feat(core): extend binary file patterns with Python-specific exclusions
Han5991 Aug 16, 2025
48bc0b5
Update packages/core/src/utils/ignorePatterns.ts
Han5991 Aug 16, 2025
43bdaf1
feat(core): enhance ignore patterns and extend media file handling
Han5991 Aug 16, 2025
17ae19f
Update packages/core/src/utils/ignorePatterns.ts
Han5991 Aug 16, 2025
aa20ada
refactor(core): simplify ignore pattern handling in grep tool
Han5991 Aug 16, 2025
64f49be
feat(core): enhance binary extensions handling and add tests for brac…
Han5991 Aug 16, 2025
a212b86
refactor(core): replace COMMON_IGNORE_PATTERNS with FileExclusions ut…
Han5991 Aug 16, 2025
1cfa214
feat(core): improve extension extraction logic with path.extname and …
Han5991 Aug 16, 2025
8f38518
refactor(core): update grep exclusion logic and remove unused param doc
Han5991 Aug 16, 2025
214204d
refactor(core): enhance glob exclusion handling in grep tool
Han5991 Aug 16, 2025
c4c50c0
Update packages/core/src/tools/grep.ts
Han5991 Aug 16, 2025
c5eaf9e
refactor(core): adjust grep exclude filtering logic for improved path…
Han5991 Aug 16, 2025
2a098c1
refactor(core): refine grep directory filtering logic
Han5991 Aug 16, 2025
032c7ea
feat(core): expand ignore patterns and refine directory filtering logic
Han5991 Aug 16, 2025
78b0a9e
refactor(core): simplify directory filtering logic in grep tool
Han5991 Aug 16, 2025
3e601fd
refactor(core): clean up redundant directory filtering logic in grep …
Han5991 Aug 16, 2025
0798a79
refactor(core): remove unused import in read-many-files tool
Han5991 Aug 19, 2025
9d3754e
refactor(core): remove unused COMMON_IGNORE_PATTERNS import in grep tool
Han5991 Aug 22, 2025
2c4d1a0
feat(core): integrate file exclusion handling into tools and config
Han5991 Aug 22, 2025
62589cc
Merge branch 'main' into feature/share-file-list-with-glob-grep-tool
bulkypanda Aug 23, 2025
ab108bc
Merge branch 'main' into feature/share-file-list-with-glob-grep-tool
bulkypanda Aug 23, 2025
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
38 changes: 27 additions & 11 deletions packages/cli/src/config/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,11 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { describe, it, expect, vi, beforeEach, afterEach, Mock } from 'vitest';
import * as os from 'os';
import * as path from 'path';
import { ShellTool, EditTool, WriteFileTool } from '@google/gemini-cli-core';
import { loadCliConfig, parseArguments } from './config.js';
import { loadCliConfig, parseArguments, CliArgs } from './config.js';
import { Settings } from './settings.js';
import { Extension } from './extension.js';
import * as ServerConfig from '@google/gemini-cli-core';
Expand Down Expand Up @@ -637,20 +637,23 @@ describe('Hierarchical Memory Loading (config.ts) - Placeholder Suite', () => {
const settings: Settings = {};
const extensions: Extension[] = [
{
path: '/path/to/ext1',
config: {
name: 'ext1',
version: '1.0.0',
},
contextFiles: ['/path/to/ext1/GEMINI.md'],
},
{
path: '/path/to/ext2',
config: {
name: 'ext2',
version: '1.0.0',
},
contextFiles: [],
},
{
path: '/path/to/ext3',
config: {
name: 'ext3',
version: '1.0.0',
Expand Down Expand Up @@ -716,6 +719,7 @@ describe('mergeMcpServers', () => {
};
const extensions: Extension[] = [
{
path: '/path/to/ext1',
config: {
name: 'ext1',
version: '1.0.0',
Expand Down Expand Up @@ -752,6 +756,7 @@ describe('mergeExcludeTools', () => {
const settings: Settings = { excludeTools: ['tool1', 'tool2'] };
const extensions: Extension[] = [
{
path: '/path/to/ext1',
config: {
name: 'ext1',
version: '1.0.0',
Expand All @@ -760,6 +765,7 @@ describe('mergeExcludeTools', () => {
contextFiles: [],
},
{
path: '/path/to/ext2',
config: {
name: 'ext2',
version: '1.0.0',
Expand All @@ -786,6 +792,7 @@ describe('mergeExcludeTools', () => {
const settings: Settings = { excludeTools: ['tool1', 'tool2'] };
const extensions: Extension[] = [
{
path: '/path/to/ext1',
config: {
name: 'ext1',
version: '1.0.0',
Expand All @@ -812,6 +819,7 @@ describe('mergeExcludeTools', () => {
const settings: Settings = { excludeTools: ['tool1'] };
const extensions: Extension[] = [
{
path: '/path/to/ext1',
config: {
name: 'ext1',
version: '1.0.0',
Expand All @@ -820,6 +828,7 @@ describe('mergeExcludeTools', () => {
contextFiles: [],
},
{
path: '/path/to/ext2',
config: {
name: 'ext2',
version: '1.0.0',
Expand Down Expand Up @@ -893,6 +902,7 @@ describe('mergeExcludeTools', () => {
const settings: Settings = {};
const extensions: Extension[] = [
{
path: '/path/to/ext',
config: {
name: 'ext1',
version: '1.0.0',
Expand All @@ -919,6 +929,7 @@ describe('mergeExcludeTools', () => {
const settings: Settings = { excludeTools: ['tool1'] };
const extensions: Extension[] = [
{
path: '/path/to/ext',
config: {
name: 'ext1',
version: '1.0.0',
Expand Down Expand Up @@ -1133,7 +1144,12 @@ describe('Approval mode tool exclusion logic', () => {
const extensions: Extension[] = [];

await expect(
loadCliConfig(settings, extensions, 'test-session', invalidArgv),
loadCliConfig(
settings,
extensions,
'test-session',
invalidArgv as CliArgs,
),
).rejects.toThrow(
'Invalid approval mode: invalid_mode. Valid values are: yolo, auto_edit, default',
);
Expand Down Expand Up @@ -1288,10 +1304,12 @@ describe('loadCliConfig with allowed-mcp-server-names', () => {
describe('loadCliConfig extensions', () => {
const mockExtensions: Extension[] = [
{
path: '/path/to/ext1',
config: { name: 'ext1', version: '1.0.0' },
contextFiles: ['/path/to/ext1.md'],
},
{
path: '/path/to/ext2',
config: { name: 'ext2', version: '1.0.0' },
contextFiles: ['/path/to/ext2.md'],
},
Expand Down Expand Up @@ -1894,14 +1912,12 @@ describe('loadCliConfig trustedFolder', () => {
description,
} of testCases) {
it(`should be correct for: ${description}`, async () => {
(isWorkspaceTrusted as vi.Mock).mockImplementation(
(settings: Settings) => {
const featureIsEnabled =
(settings.folderTrustFeature ?? false) &&
(settings.folderTrust ?? true);
return featureIsEnabled ? mockTrustValue : true;
},
);
(isWorkspaceTrusted as Mock).mockImplementation((settings: Settings) => {
const featureIsEnabled =
(settings.folderTrustFeature ?? false) &&
(settings.folderTrust ?? true);
return featureIsEnabled ? mockTrustValue : true;
});
const argv = await parseArguments();
const settings: Settings = { folderTrustFeature, folderTrust };
const config = await loadCliConfig(settings, [], 'test-session', argv);
Expand Down
9 changes: 9 additions & 0 deletions packages/cli/src/ui/hooks/atCommandProcessor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ import {
ReadManyFilesTool,
StandardFileSystemService,
ToolRegistry,
COMMON_IGNORE_PATTERNS,
DEFAULT_FILE_EXCLUDES,
} from '@google/gemini-cli-core';
import * as os from 'os';
import { ToolCallStatus } from '../types.js';
Expand Down Expand Up @@ -69,6 +71,13 @@ describe('handleAtCommand', () => {
getPromptsByServer: () => [],
}),
getDebugMode: () => false,
getFileExclusions: () => ({
getCoreIgnorePatterns: () => COMMON_IGNORE_PATTERNS,
getDefaultExcludePatterns: () => DEFAULT_FILE_EXCLUDES,
getGlobExcludes: () => COMMON_IGNORE_PATTERNS,
buildExcludePatterns: () => DEFAULT_FILE_EXCLUDES,
getReadManyFilesExcludes: () => DEFAULT_FILE_EXCLUDES,
}),
getUsageStatisticsEnabled: () => false,
} as unknown as Config;

Expand Down
22 changes: 22 additions & 0 deletions packages/core/src/config/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ import { IdeConnectionEvent, IdeConnectionType } from '../telemetry/types.js';
export type { MCPOAuthConfig };
import { WorkspaceContext } from '../utils/workspaceContext.js';
import { Storage } from './storage.js';
import { FileExclusions } from '../utils/ignorePatterns.js';

export enum ApprovalMode {
DEFAULT = 'default',
Expand Down Expand Up @@ -277,6 +278,7 @@ export class Config {
private readonly enablePromptCompletion: boolean = false;
private initialized: boolean = false;
readonly storage: Storage;
private readonly fileExclusions: FileExclusions;

constructor(params: ConfigParameters) {
this.sessionId = params.sessionId;
Expand Down Expand Up @@ -349,6 +351,7 @@ export class Config {
this.skipNextSpeakerCheck = params.skipNextSpeakerCheck ?? false;
this.storage = new Storage(this.targetDir);
this.enablePromptCompletion = params.enablePromptCompletion ?? false;
this.fileExclusions = new FileExclusions(this);

if (params.contextFileName) {
setGeminiMdFilename(params.contextFileName);
Expand Down Expand Up @@ -621,6 +624,21 @@ export class Config {
};
}

/**
* Gets custom file exclusion patterns from configuration.
* TODO: This is a placeholder implementation. In the future, this could
* read from settings files, CLI arguments, or environment variables.
*/
getCustomExcludes(): string[] {
// Placeholder implementation - returns empty array for now
// Future implementation could read from:
// - User settings file
// - Project-specific configuration
// - Environment variables
// - CLI arguments
return [];
}

getCheckpointingEnabled(): boolean {
return this.checkpointing;
}
Expand Down Expand Up @@ -766,6 +784,10 @@ export class Config {
return this.gitService;
}

getFileExclusions(): FileExclusions {
return this.fileExclusions;
}

async createToolRegistry(): Promise<ToolRegistry> {
const registry = new ToolRegistry(this);

Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ export * from './utils/formatters.js';
export * from './utils/generateContentResponseUtilities.js';
export * from './utils/filesearch/fileSearch.js';
export * from './utils/errorParsing.js';
export * from './utils/ignorePatterns.js';

// Export services
export * from './services/fileDiscoveryService.js';
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/tools/glob.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ describe('GlobTool', () => {
getFileFilteringRespectGitIgnore: () => true,
getTargetDir: () => tempRootDir,
getWorkspaceContext: () => createMockWorkspaceContext(tempRootDir),
getFileExclusions: () => ({
getGlobExcludes: () => [],
}),
} as unknown as Config;

beforeEach(async () => {
Expand Down
2 changes: 1 addition & 1 deletion packages/core/src/tools/glob.ts
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ class GlobToolInvocation extends BaseToolInvocation<
stat: true,
nocase: !this.params.case_sensitive,
dot: true,
ignore: ['**/node_modules/**', '**/.git/**'],
ignore: this.config.getFileExclusions().getGlobExcludes(),
follow: false,
signal,
})) as GlobPath[];
Expand Down
12 changes: 12 additions & 0 deletions packages/core/src/tools/grep.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ describe('GrepTool', () => {
const mockConfig = {
getTargetDir: () => tempRootDir,
getWorkspaceContext: () => createMockWorkspaceContext(tempRootDir),
getFileExclusions: () => ({
getGlobExcludes: () => [],
}),
} as unknown as Config;

beforeEach(async () => {
Expand Down Expand Up @@ -258,6 +261,9 @@ describe('GrepTool', () => {
getTargetDir: () => tempRootDir,
getWorkspaceContext: () =>
createMockWorkspaceContext(tempRootDir, [secondDir]),
getFileExclusions: () => ({
getGlobExcludes: () => [],
}),
} as unknown as Config;

const multiDirGrepTool = new GrepTool(multiDirConfig);
Expand Down Expand Up @@ -308,6 +314,9 @@ describe('GrepTool', () => {
getTargetDir: () => tempRootDir,
getWorkspaceContext: () =>
createMockWorkspaceContext(tempRootDir, [secondDir]),
getFileExclusions: () => ({
getGlobExcludes: () => [],
}),
} as unknown as Config;

const multiDirGrepTool = new GrepTool(multiDirConfig);
Expand Down Expand Up @@ -367,6 +376,9 @@ describe('GrepTool', () => {
getTargetDir: () => tempRootDir,
getWorkspaceContext: () =>
createMockWorkspaceContext(tempRootDir, ['/another/dir']),
getFileExclusions: () => ({
getGlobExcludes: () => [],
}),
} as unknown as Config;

const multiDirGrepTool = new GrepTool(multiDirConfig);
Expand Down
35 changes: 26 additions & 9 deletions packages/core/src/tools/grep.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import { makeRelative, shortenPath } from '../utils/paths.js';
import { getErrorMessage, isNodeError } from '../utils/errors.js';
import { isGitRepository } from '../utils/gitUtils.js';
import { Config } from '../config/config.js';
import { FileExclusions } from '../utils/ignorePatterns.js';
import { ToolErrorType } from './tool-error.js';

// --- Interfaces ---
Expand Down Expand Up @@ -58,11 +59,14 @@ class GrepToolInvocation extends BaseToolInvocation<
GrepToolParams,
ToolResult
> {
private readonly fileExclusions: FileExclusions;

constructor(
private readonly config: Config,
params: GrepToolParams,
) {
super(params);
this.fileExclusions = config.getFileExclusions();
}

/**
Expand Down Expand Up @@ -281,7 +285,6 @@ class GrepToolInvocation extends BaseToolInvocation<

/**
* Gets a description of the grep operation
* @param params Parameters for the grep operation
* @returns A string describing the grep
*/
getDescription(): string {
Expand Down Expand Up @@ -391,7 +394,27 @@ class GrepToolInvocation extends BaseToolInvocation<
if (grepAvailable) {
strategyUsed = 'system grep';
const grepArgs = ['-r', '-n', '-H', '-E'];
const commonExcludes = ['.git', 'node_modules', 'bower_components'];
// Extract directory names from exclusion patterns for grep --exclude-dir
const globExcludes = this.fileExclusions.getGlobExcludes();
const commonExcludes = globExcludes
.map((pattern) => {
let dir = pattern;
if (dir.startsWith('**/')) {
dir = dir.substring(3);
}
if (dir.endsWith('/**')) {
dir = dir.slice(0, -3);
} else if (dir.endsWith('/')) {
dir = dir.slice(0, -1);
}

// Only consider patterns that are likely directories. This filters out file patterns.
if (dir && !dir.includes('/') && !dir.includes('*')) {
return dir;
}
return null;
})
.filter((dir): dir is string => !!dir);
Comment thread
Han5991 marked this conversation as resolved.
Outdated
commonExcludes.forEach((dir) => grepArgs.push(`--exclude-dir=${dir}`));
if (include) {
grepArgs.push(`--include=${include}`);
Expand Down Expand Up @@ -474,13 +497,7 @@ class GrepToolInvocation extends BaseToolInvocation<
);
strategyUsed = 'javascript fallback';
const globPattern = include ? include : '**/*';
const ignorePatterns = [
'.git/**',
'node_modules/**',
'bower_components/**',
'.svn/**',
'.hg/**',
]; // Use glob patterns for ignores here
const ignorePatterns = this.fileExclusions.getGlobExcludes();

const filesStream = globStream(globPattern, {
cwd: absolutePath,
Expand Down
Loading
Loading