diff --git a/packages/core/src/services/fileDiscoveryService.test.ts b/packages/core/src/services/fileDiscoveryService.test.ts index 9a56435bdc..de7c561e4d 100644 --- a/packages/core/src/services/fileDiscoveryService.test.ts +++ b/packages/core/src/services/fileDiscoveryService.test.ts @@ -136,6 +136,43 @@ describe('FileDiscoveryService', () => { }); }); + describe('filterFilesWithReport', () => { + beforeEach(async () => { + await fs.mkdir(path.join(projectRoot, '.git')); + await createTestFile('.gitignore', 'node_modules/'); + await createTestFile('.geminiignore', '*.log'); + }); + + it('should return filtered paths and correct ignored count', () => { + const files = [ + 'src/index.ts', + 'node_modules/package/index.js', + 'debug.log', + 'README.md', + ].map((f) => path.join(projectRoot, f)); + + const service = new FileDiscoveryService(projectRoot); + const report = service.filterFilesWithReport(files); + + expect(report.filteredPaths).toEqual( + ['src/index.ts', 'README.md'].map((f) => path.join(projectRoot, f)), + ); + expect(report.ignoredCount).toBe(2); + }); + + it('should handle no ignored files', () => { + const files = ['src/index.ts', 'README.md'].map((f) => + path.join(projectRoot, f), + ); + + const service = new FileDiscoveryService(projectRoot); + const report = service.filterFilesWithReport(files); + + expect(report.filteredPaths).toEqual(files); + expect(report.ignoredCount).toBe(0); + }); + }); + describe('shouldGitIgnoreFile & shouldGeminiIgnoreFile', () => { beforeEach(async () => { await fs.mkdir(path.join(projectRoot, '.git')); diff --git a/packages/core/src/services/fileDiscoveryService.ts b/packages/core/src/services/fileDiscoveryService.ts index 4620362685..dbec5f1ffa 100644 --- a/packages/core/src/services/fileDiscoveryService.ts +++ b/packages/core/src/services/fileDiscoveryService.ts @@ -18,8 +18,7 @@ export interface FilterFilesOptions { export interface FilterReport { filteredPaths: string[]; - gitIgnoredCount: number; - geminiIgnoredCount: number; + ignoredCount: number; } export class FileDiscoveryService { @@ -70,28 +69,12 @@ export class FileDiscoveryService { respectGeminiIgnore: true, }, ): FilterReport { - const filteredPaths: string[] = []; - let gitIgnoredCount = 0; - let geminiIgnoredCount = 0; - - for (const filePath of filePaths) { - if (opts.respectGitIgnore && this.shouldGitIgnoreFile(filePath)) { - gitIgnoredCount++; - continue; - } - - if (opts.respectGeminiIgnore && this.shouldGeminiIgnoreFile(filePath)) { - geminiIgnoredCount++; - continue; - } - - filteredPaths.push(filePath); - } + const filteredPaths = this.filterFiles(filePaths, opts); + const ignoredCount = filePaths.length - filteredPaths.length; return { filteredPaths, - gitIgnoredCount, - geminiIgnoredCount, + ignoredCount, }; } diff --git a/packages/core/src/tools/glob.ts b/packages/core/src/tools/glob.ts index 2ac2fd89c2..0dbd71e479 100644 --- a/packages/core/src/tools/glob.ts +++ b/packages/core/src/tools/glob.ts @@ -169,7 +169,7 @@ class GlobToolInvocation extends BaseToolInvocation< path.relative(this.config.getTargetDir(), p.fullpath()), ); - const { filteredPaths, gitIgnoredCount, geminiIgnoredCount } = + const { filteredPaths, ignoredCount } = fileDiscovery.filterFilesWithReport(relativePaths, { respectGitIgnore: this.params?.respect_git_ignore ?? @@ -196,11 +196,8 @@ class GlobToolInvocation extends BaseToolInvocation< } else { message += ` within ${searchDirectories.length} workspace directories`; } - if (gitIgnoredCount > 0) { - message += ` (${gitIgnoredCount} files were git-ignored)`; - } - if (geminiIgnoredCount > 0) { - message += ` (${geminiIgnoredCount} files were gemini-ignored)`; + if (ignoredCount > 0) { + message += ` (${ignoredCount} files were ignored)`; } return { llmContent: message, @@ -231,11 +228,8 @@ class GlobToolInvocation extends BaseToolInvocation< } else { resultMessage += ` across ${searchDirectories.length} workspace directories`; } - if (gitIgnoredCount > 0) { - resultMessage += ` (${gitIgnoredCount} additional files were git-ignored)`; - } - if (geminiIgnoredCount > 0) { - resultMessage += ` (${geminiIgnoredCount} additional files were gemini-ignored)`; + if (ignoredCount > 0) { + resultMessage += ` (${ignoredCount} additional files were ignored)`; } resultMessage += `, sorted by modification time (newest first):\n${fileListDescription}`; diff --git a/packages/core/src/tools/ls.test.ts b/packages/core/src/tools/ls.test.ts index f48f8cde23..1cda0c9e7e 100644 --- a/packages/core/src/tools/ls.test.ts +++ b/packages/core/src/tools/ls.test.ts @@ -149,7 +149,7 @@ describe('LSTool', () => { expect(result.llmContent).toContain('file1.txt'); expect(result.llmContent).not.toContain('file2.log'); // .git is always ignored by default. - expect(result.returnDisplay).toBe('Listed 2 item(s). (2 git-ignored)'); + expect(result.returnDisplay).toBe('Listed 2 item(s). (2 ignored)'); }); it('should respect geminiignore patterns', async () => { @@ -161,7 +161,7 @@ describe('LSTool', () => { expect(result.llmContent).toContain('file1.txt'); expect(result.llmContent).not.toContain('file2.log'); - expect(result.returnDisplay).toBe('Listed 2 item(s). (1 gemini-ignored)'); + expect(result.returnDisplay).toBe('Listed 2 item(s). (1 ignored)'); }); it('should handle non-directory paths', async () => { diff --git a/packages/core/src/tools/ls.ts b/packages/core/src/tools/ls.ts index 9699be5d60..7aac367e50 100644 --- a/packages/core/src/tools/ls.ts +++ b/packages/core/src/tools/ls.ts @@ -173,7 +173,7 @@ class LSToolInvocation extends BaseToolInvocation { ); const fileDiscovery = this.config.getFileService(); - const { filteredPaths, gitIgnoredCount, geminiIgnoredCount } = + const { filteredPaths, ignoredCount } = fileDiscovery.filterFilesWithReport(relativePaths, { respectGitIgnore: this.params.file_filtering_options?.respect_git_ignore ?? @@ -222,20 +222,13 @@ class LSToolInvocation extends BaseToolInvocation { .join('\n'); let resultMessage = `Directory listing for ${this.params.path}:\n${directoryContent}`; - const ignoredMessages = []; - if (gitIgnoredCount > 0) { - ignoredMessages.push(`${gitIgnoredCount} git-ignored`); - } - if (geminiIgnoredCount > 0) { - ignoredMessages.push(`${geminiIgnoredCount} gemini-ignored`); - } - if (ignoredMessages.length > 0) { - resultMessage += `\n\n(${ignoredMessages.join(', ')})`; + if (ignoredCount > 0) { + resultMessage += `\n\n(${ignoredCount} ignored)`; } let displayMessage = `Listed ${entries.length} item(s).`; - if (ignoredMessages.length > 0) { - displayMessage += ` (${ignoredMessages.join(', ')})`; + if (ignoredCount > 0) { + displayMessage += ` (${ignoredCount} ignored)`; } return { diff --git a/packages/core/src/tools/read-many-files.ts b/packages/core/src/tools/read-many-files.ts index 88d2660c1b..5681647cb5 100644 --- a/packages/core/src/tools/read-many-files.ts +++ b/packages/core/src/tools/read-many-files.ts @@ -225,7 +225,7 @@ ${finalExclusionPatternsForDescription ); const fileDiscovery = this.config.getFileService(); - const { filteredPaths, gitIgnoredCount, geminiIgnoredCount } = + const { filteredPaths, ignoredCount } = fileDiscovery.filterFilesWithReport(relativeEntries, { respectGitIgnore: this.params.file_filtering_options?.respect_git_ignore ?? @@ -253,19 +253,11 @@ ${finalExclusionPatternsForDescription filesToConsider.add(fullPath); } - // Add info about git-ignored files if any were filtered - if (gitIgnoredCount > 0) { + // Add info about ignored files if any were filtered + if (ignoredCount > 0) { skippedFiles.push({ - path: `${gitIgnoredCount} file(s)`, - reason: 'git ignored', - }); - } - - // Add info about gemini-ignored files if any were filtered - if (geminiIgnoredCount > 0) { - skippedFiles.push({ - path: `${geminiIgnoredCount} file(s)`, - reason: 'gemini ignored', + path: `${ignoredCount} file(s)`, + reason: 'ignored by project ignore files', }); } } catch (error) {