diff options
Diffstat (limited to 'packages/core/src/utils')
| -rw-r--r-- | packages/core/src/utils/bfsFileSearch.test.ts | 39 | ||||
| -rw-r--r-- | packages/core/src/utils/bfsFileSearch.ts | 11 | ||||
| -rw-r--r-- | packages/core/src/utils/getFolderStructure.test.ts | 33 | ||||
| -rw-r--r-- | packages/core/src/utils/getFolderStructure.ts | 34 | ||||
| -rw-r--r-- | packages/core/src/utils/memoryDiscovery.ts | 15 |
5 files changed, 117 insertions, 15 deletions
diff --git a/packages/core/src/utils/bfsFileSearch.test.ts b/packages/core/src/utils/bfsFileSearch.test.ts index 83e9b0b9..3ce452de 100644 --- a/packages/core/src/utils/bfsFileSearch.test.ts +++ b/packages/core/src/utils/bfsFileSearch.test.ts @@ -145,4 +145,43 @@ describe('bfsFileSearch', () => { }); expect(result).toEqual(['/test/subdir1/file1.txt']); }); + + it('should respect .geminiignore files', async () => { + const mockFs = vi.mocked(fsPromises); + const mockGitUtils = vi.mocked(gitUtils); + + mockGitUtils.isGitRepository.mockReturnValue(false); + + const mockReaddir = mockFs.readdir as unknown as ReaddirWithFileTypes; + vi.mocked(mockReaddir).mockImplementation(async (dir) => { + if (dir === '/test') { + return [ + createMockDirent('.geminiignore', true), + createMockDirent('subdir1', false), + createMockDirent('subdir2', false), + ]; + } + if (dir === '/test/subdir1') { + return [createMockDirent('file1.txt', true)]; + } + if (dir === '/test/subdir2') { + return [createMockDirent('file1.txt', true)]; + } + return []; + }); + + vi.mocked(fs).readFileSync.mockReturnValue('subdir2'); + + const fileService = new FileDiscoveryService('/test'); + const result = await bfsFileSearch('/test', { + fileName: 'file1.txt', + fileService, + fileFilteringOptions: { + respectGitIgnore: true, + respectGeminiIgnore: true, + }, + }); + + expect(result).toEqual(['/test/subdir1/file1.txt']); + }); }); diff --git a/packages/core/src/utils/bfsFileSearch.ts b/packages/core/src/utils/bfsFileSearch.ts index e552f520..790521e0 100644 --- a/packages/core/src/utils/bfsFileSearch.ts +++ b/packages/core/src/utils/bfsFileSearch.ts @@ -8,7 +8,7 @@ import * as fs from 'fs/promises'; import * as path from 'path'; import { Dirent } from 'fs'; import { FileDiscoveryService } from '../services/fileDiscoveryService.js'; - +import { FileFilteringOptions } from '../config/config.js'; // Simple console logger for now. // TODO: Integrate with a more robust server-side logger. const logger = { @@ -22,6 +22,7 @@ interface BfsFileSearchOptions { maxDirs?: number; debug?: boolean; fileService?: FileDiscoveryService; + fileFilteringOptions?: FileFilteringOptions; } /** @@ -69,7 +70,13 @@ export async function bfsFileSearch( for (const entry of entries) { const fullPath = path.join(currentDir, entry.name); - if (fileService?.shouldGitIgnoreFile(fullPath)) { + if ( + fileService?.shouldIgnoreFile(fullPath, { + respectGitIgnore: options.fileFilteringOptions?.respectGitIgnore, + respectGeminiIgnore: + options.fileFilteringOptions?.respectGeminiIgnore, + }) + ) { continue; } diff --git a/packages/core/src/utils/getFolderStructure.test.ts b/packages/core/src/utils/getFolderStructure.test.ts index 3d7c125e..b6354745 100644 --- a/packages/core/src/utils/getFolderStructure.test.ts +++ b/packages/core/src/utils/getFolderStructure.test.ts @@ -307,6 +307,7 @@ describe('getFolderStructure gitignore', () => { createDirent('file1.txt', 'file'), createDirent('node_modules', 'dir'), createDirent('ignored.txt', 'file'), + createDirent('gem_ignored.txt', 'file'), createDirent('.gemini', 'dir'), ] as any; } @@ -327,6 +328,9 @@ describe('getFolderStructure gitignore', () => { if (path === '/test/project/.gitignore') { return 'ignored.txt\nnode_modules/\n.gemini/\n!/.gemini/config.yaml'; } + if (path === '/test/project/.geminiignore') { + return 'gem_ignored.txt\nnode_modules/\n.gemini/\n!/.gemini/config.yaml'; + } return ''; }); @@ -347,10 +351,37 @@ describe('getFolderStructure gitignore', () => { const fileService = new FileDiscoveryService('/test/project'); const structure = await getFolderStructure('/test/project', { fileService, - respectGitIgnore: false, + fileFilteringOptions: { + respectGeminiIgnore: false, + respectGitIgnore: false, + }, }); expect(structure).toContain('ignored.txt'); // node_modules is still ignored by default expect(structure).toContain('node_modules/...'); }); + + it('should ignore files and folders specified in .geminiignore', async () => { + const fileService = new FileDiscoveryService('/test/project'); + const structure = await getFolderStructure('/test/project', { + fileService, + }); + expect(structure).not.toContain('gem_ignored.txt'); + expect(structure).toContain('node_modules/...'); + expect(structure).not.toContain('logs.json'); + }); + + it('should not ignore files if respectGeminiIgnore is false', async () => { + const fileService = new FileDiscoveryService('/test/project'); + const structure = await getFolderStructure('/test/project', { + fileService, + fileFilteringOptions: { + respectGeminiIgnore: false, + respectGitIgnore: true, // Explicitly disable gemini ignore only + }, + }); + expect(structure).toContain('gem_ignored.txt'); + // node_modules is still ignored by default + expect(structure).toContain('node_modules/...'); + }); }); diff --git a/packages/core/src/utils/getFolderStructure.ts b/packages/core/src/utils/getFolderStructure.ts index 6798a147..15588a4b 100644 --- a/packages/core/src/utils/getFolderStructure.ts +++ b/packages/core/src/utils/getFolderStructure.ts @@ -9,6 +9,8 @@ import { Dirent } from 'fs'; import * as path from 'path'; import { getErrorMessage, isNodeError } from './errors.js'; import { FileDiscoveryService } from '../services/fileDiscoveryService.js'; +import { FileFilteringOptions } from '../config/config.js'; +import { DEFAULT_FILE_FILTERING_OPTIONS } from '../config/config.js'; const MAX_ITEMS = 200; const TRUNCATION_INDICATOR = '...'; @@ -26,16 +28,16 @@ interface FolderStructureOptions { fileIncludePattern?: RegExp; /** For filtering files. */ fileService?: FileDiscoveryService; - /** Whether to use .gitignore patterns. */ - respectGitIgnore?: boolean; + /** File filtering ignore options. */ + fileFilteringOptions?: FileFilteringOptions; } - // Define a type for the merged options where fileIncludePattern remains optional type MergedFolderStructureOptions = Required< Omit<FolderStructureOptions, 'fileIncludePattern' | 'fileService'> > & { fileIncludePattern?: RegExp; fileService?: FileDiscoveryService; + fileFilteringOptions?: FileFilteringOptions; }; /** Represents the full, unfiltered information about a folder and its contents. */ @@ -126,8 +128,13 @@ async function readFullStructure( } const fileName = entry.name; const filePath = path.join(currentPath, fileName); - if (options.respectGitIgnore && options.fileService) { - if (options.fileService.shouldGitIgnoreFile(filePath)) { + if (options.fileService) { + const shouldIgnore = + (options.fileFilteringOptions.respectGitIgnore && + options.fileService.shouldGitIgnoreFile(filePath)) || + (options.fileFilteringOptions.respectGeminiIgnore && + options.fileService.shouldGeminiIgnoreFile(filePath)); + if (shouldIgnore) { continue; } } @@ -160,14 +167,16 @@ async function readFullStructure( const subFolderName = entry.name; const subFolderPath = path.join(currentPath, subFolderName); - let isIgnoredByGit = false; - if (options.respectGitIgnore && options.fileService) { - if (options.fileService.shouldGitIgnoreFile(subFolderPath)) { - isIgnoredByGit = true; - } + let isIgnored = false; + if (options.fileService) { + isIgnored = + (options.fileFilteringOptions.respectGitIgnore && + options.fileService.shouldGitIgnoreFile(subFolderPath)) || + (options.fileFilteringOptions.respectGeminiIgnore && + options.fileService.shouldGeminiIgnoreFile(subFolderPath)); } - if (options.ignoredFolders.has(subFolderName) || isIgnoredByGit) { + if (options.ignoredFolders.has(subFolderName) || isIgnored) { const ignoredSubFolder: FullFolderInfo = { name: subFolderName, path: subFolderPath, @@ -295,7 +304,8 @@ export async function getFolderStructure( ignoredFolders: options?.ignoredFolders ?? DEFAULT_IGNORED_FOLDERS, fileIncludePattern: options?.fileIncludePattern, fileService: options?.fileService, - respectGitIgnore: options?.respectGitIgnore ?? true, + fileFilteringOptions: + options?.fileFilteringOptions ?? DEFAULT_FILE_FILTERING_OPTIONS, }; try { diff --git a/packages/core/src/utils/memoryDiscovery.ts b/packages/core/src/utils/memoryDiscovery.ts index ab240ea8..33231823 100644 --- a/packages/core/src/utils/memoryDiscovery.ts +++ b/packages/core/src/utils/memoryDiscovery.ts @@ -15,6 +15,10 @@ import { } from '../tools/memoryTool.js'; import { FileDiscoveryService } from '../services/fileDiscoveryService.js'; import { processImports } from './memoryImportProcessor.js'; +import { + DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, + FileFilteringOptions, +} from '../config/config.js'; // Simple console logger, similar to the one previously in CLI's config.ts // TODO: Integrate with a more robust server-side logger if available/appropriate. @@ -85,6 +89,7 @@ async function getGeminiMdFilePathsInternal( debugMode: boolean, fileService: FileDiscoveryService, extensionContextFilePaths: string[] = [], + fileFilteringOptions: FileFilteringOptions, ): Promise<string[]> { const allPaths = new Set<string>(); const geminiMdFilenames = getAllGeminiMdFilenames(); @@ -181,11 +186,18 @@ async function getGeminiMdFilePathsInternal( } upwardPaths.forEach((p) => allPaths.add(p)); + // Merge options with memory defaults, with options taking precedence + const mergedOptions = { + ...DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, + ...fileFilteringOptions, + }; + const downwardPaths = await bfsFileSearch(resolvedCwd, { fileName: geminiMdFilename, maxDirs: MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY, debug: debugMode, fileService, + fileFilteringOptions: mergedOptions, // Pass merged options as fileFilter }); downwardPaths.sort(); // Sort for consistent ordering, though hierarchy might be more complex if (debugMode && downwardPaths.length > 0) @@ -282,11 +294,13 @@ export async function loadServerHierarchicalMemory( debugMode: boolean, fileService: FileDiscoveryService, extensionContextFilePaths: string[] = [], + fileFilteringOptions?: FileFilteringOptions, ): Promise<{ memoryContent: string; fileCount: number }> { if (debugMode) logger.debug( `Loading server hierarchical memory for CWD: ${currentWorkingDirectory}`, ); + // For the server, homedir() refers to the server process's home. // This is consistent with how MemoryTool already finds the global path. const userHomePath = homedir(); @@ -296,6 +310,7 @@ export async function loadServerHierarchicalMemory( debugMode, fileService, extensionContextFilePaths, + fileFilteringOptions || DEFAULT_MEMORY_FILE_FILTERING_OPTIONS, ); if (filePaths.length === 0) { if (debugMode) logger.debug('No GEMINI.md files found in hierarchy.'); |
