diff options
Diffstat (limited to 'packages/core/src')
| -rw-r--r-- | packages/core/src/config/config.test.ts | 1 | ||||
| -rw-r--r-- | packages/core/src/config/config.ts | 29 | ||||
| -rw-r--r-- | packages/core/src/tools/ls.ts | 69 | ||||
| -rw-r--r-- | packages/core/src/tools/read-many-files.test.ts | 7 | ||||
| -rw-r--r-- | packages/core/src/tools/read-many-files.ts | 91 | ||||
| -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 |
10 files changed, 281 insertions, 48 deletions
diff --git a/packages/core/src/config/config.test.ts b/packages/core/src/config/config.test.ts index e34880a6..44300a83 100644 --- a/packages/core/src/config/config.test.ts +++ b/packages/core/src/config/config.test.ts @@ -331,6 +331,7 @@ describe('Server Config (config.ts)', () => { config.getDebugMode(), config.getFileService(), config.getExtensionContextFilePaths(), + config.getFileFilteringOptions(), ); expect(config.getUserMemory()).toBe(mockMemoryData.memoryContent); diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index f81b3e32..9528f648 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -76,7 +76,20 @@ export interface GeminiCLIExtension { version: string; isActive: boolean; } - +export interface FileFilteringOptions { + respectGitIgnore: boolean; + respectGeminiIgnore: boolean; +} +// For memory files +export const DEFAULT_MEMORY_FILE_FILTERING_OPTIONS: FileFilteringOptions = { + respectGitIgnore: false, + respectGeminiIgnore: true, +}; +// For all other files +export const DEFAULT_FILE_FILTERING_OPTIONS: FileFilteringOptions = { + respectGitIgnore: true, + respectGeminiIgnore: true, +}; export class MCPServerConfig { constructor( // For stdio transport @@ -137,6 +150,7 @@ export interface ConfigParameters { usageStatisticsEnabled?: boolean; fileFiltering?: { respectGitIgnore?: boolean; + respectGeminiIgnore?: boolean; enableRecursiveFileSearch?: boolean; }; checkpointing?: boolean; @@ -182,6 +196,7 @@ export class Config { private geminiClient!: GeminiClient; private readonly fileFiltering: { respectGitIgnore: boolean; + respectGeminiIgnore: boolean; enableRecursiveFileSearch: boolean; }; private fileDiscoveryService: FileDiscoveryService | null = null; @@ -239,6 +254,7 @@ export class Config { this.fileFiltering = { respectGitIgnore: params.fileFiltering?.respectGitIgnore ?? true, + respectGeminiIgnore: params.fileFiltering?.respectGeminiIgnore ?? true, enableRecursiveFileSearch: params.fileFiltering?.enableRecursiveFileSearch ?? true, }; @@ -473,6 +489,16 @@ export class Config { getFileFilteringRespectGitIgnore(): boolean { return this.fileFiltering.respectGitIgnore; } + getFileFilteringRespectGeminiIgnore(): boolean { + return this.fileFiltering.respectGeminiIgnore; + } + + getFileFilteringOptions(): FileFilteringOptions { + return { + respectGitIgnore: this.fileFiltering.respectGitIgnore, + respectGeminiIgnore: this.fileFiltering.respectGeminiIgnore, + }; + } getCheckpointingEnabled(): boolean { return this.checkpointing; @@ -549,6 +575,7 @@ export class Config { this.getDebugMode(), this.getFileService(), this.getExtensionContextFilePaths(), + this.getFileFilteringOptions(), ); this.setUserMemory(memoryContent); diff --git a/packages/core/src/tools/ls.ts b/packages/core/src/tools/ls.ts index fc4f06dd..68a69101 100644 --- a/packages/core/src/tools/ls.ts +++ b/packages/core/src/tools/ls.ts @@ -10,7 +10,7 @@ import { BaseTool, Icon, ToolResult } from './tools.js'; import { Type } from '@google/genai'; import { SchemaValidator } from '../utils/schemaValidator.js'; import { makeRelative, shortenPath } from '../utils/paths.js'; -import { Config } from '../config/config.js'; +import { Config, DEFAULT_FILE_FILTERING_OPTIONS } from '../config/config.js'; import { isWithinRoot } from '../utils/fileUtils.js'; /** @@ -28,9 +28,12 @@ export interface LSToolParams { ignore?: string[]; /** - * Whether to respect .gitignore patterns (optional, defaults to true) + * Whether to respect .gitignore and .geminiignore patterns (optional, defaults to true) */ - respect_git_ignore?: boolean; + file_filtering_options?: { + respect_git_ignore?: boolean; + respect_gemini_ignore?: boolean; + }; } /** @@ -89,10 +92,22 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> { }, type: Type.ARRAY, }, - respect_git_ignore: { + file_filtering_options: { description: - 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', - type: Type.BOOLEAN, + 'Optional: Whether to respect ignore patterns from .gitignore or .geminiignore', + type: Type.OBJECT, + properties: { + respect_git_ignore: { + description: + 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', + type: Type.BOOLEAN, + }, + respect_gemini_ignore: { + description: + 'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.', + type: Type.BOOLEAN, + }, + }, }, }, required: ['path'], @@ -199,14 +214,25 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> { const files = fs.readdirSync(params.path); + const defaultFileIgnores = + this.config.getFileFilteringOptions() ?? DEFAULT_FILE_FILTERING_OPTIONS; + + const fileFilteringOptions = { + respectGitIgnore: + params.file_filtering_options?.respect_git_ignore ?? + defaultFileIgnores.respectGitIgnore, + respectGeminiIgnore: + params.file_filtering_options?.respect_gemini_ignore ?? + defaultFileIgnores.respectGeminiIgnore, + }; + // Get centralized file discovery service - const respectGitIgnore = - params.respect_git_ignore ?? - this.config.getFileFilteringRespectGitIgnore(); + const fileDiscovery = this.config.getFileService(); const entries: FileEntry[] = []; let gitIgnoredCount = 0; + let geminiIgnoredCount = 0; if (files.length === 0) { // Changed error message to be more neutral for LLM @@ -227,14 +253,21 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> { fullPath, ); - // Check if this file should be git-ignored (only in git repositories) + // Check if this file should be ignored based on git or gemini ignore rules if ( - respectGitIgnore && + fileFilteringOptions.respectGitIgnore && fileDiscovery.shouldGitIgnoreFile(relativePath) ) { gitIgnoredCount++; continue; } + if ( + fileFilteringOptions.respectGeminiIgnore && + fileDiscovery.shouldGeminiIgnoreFile(relativePath) + ) { + geminiIgnoredCount++; + continue; + } try { const stats = fs.statSync(fullPath); @@ -265,13 +298,21 @@ export class LSTool extends BaseTool<LSToolParams, ToolResult> { .join('\n'); let resultMessage = `Directory listing for ${params.path}:\n${directoryContent}`; + const ignoredMessages = []; if (gitIgnoredCount > 0) { - resultMessage += `\n\n(${gitIgnoredCount} items were git-ignored)`; + ignoredMessages.push(`${gitIgnoredCount} git-ignored`); + } + if (geminiIgnoredCount > 0) { + ignoredMessages.push(`${geminiIgnoredCount} gemini-ignored`); + } + + if (ignoredMessages.length > 0) { + resultMessage += `\n\n(${ignoredMessages.join(', ')})`; } let displayMessage = `Listed ${entries.length} item(s).`; - if (gitIgnoredCount > 0) { - displayMessage += ` (${gitIgnoredCount} git-ignored)`; + if (ignoredMessages.length > 0) { + displayMessage += ` (${ignoredMessages.join(', ')})`; } return { diff --git a/packages/core/src/tools/read-many-files.test.ts b/packages/core/src/tools/read-many-files.test.ts index 3bb824cd..adad6efc 100644 --- a/packages/core/src/tools/read-many-files.test.ts +++ b/packages/core/src/tools/read-many-files.test.ts @@ -58,10 +58,13 @@ describe('ReadManyFilesTool', () => { const fileService = new FileDiscoveryService(tempRootDir); const mockConfig = { getFileService: () => fileService, - getFileFilteringRespectGitIgnore: () => true, + + getFileFilteringOptions: () => ({ + respectGitIgnore: true, + respectGeminiIgnore: true, + }), getTargetDir: () => tempRootDir, } as Partial<Config> as Config; - tool = new ReadManyFilesTool(mockConfig); mockReadFileFn = mockControl.mockReadFile; diff --git a/packages/core/src/tools/read-many-files.ts b/packages/core/src/tools/read-many-files.ts index 1c01ee9f..7c3be6e3 100644 --- a/packages/core/src/tools/read-many-files.ts +++ b/packages/core/src/tools/read-many-files.ts @@ -17,7 +17,7 @@ import { getSpecificMimeType, } from '../utils/fileUtils.js'; import { PartListUnion, Schema, Type } from '@google/genai'; -import { Config } from '../config/config.js'; +import { Config, DEFAULT_FILE_FILTERING_OPTIONS } from '../config/config.js'; import { recordFileOperationMetric, FileOperation, @@ -62,9 +62,12 @@ export interface ReadManyFilesParams { useDefaultExcludes?: boolean; /** - * Optional. Whether to respect .gitignore patterns. Defaults to true. + * Whether to respect .gitignore and .geminiignore patterns (optional, defaults to true) */ - respect_git_ignore?: boolean; + file_filtering_options?: { + respect_git_ignore?: boolean; + respect_gemini_ignore?: boolean; + }; } /** @@ -173,11 +176,22 @@ export class ReadManyFilesTool extends BaseTool< 'Optional. Whether to apply a list of default exclusion patterns (e.g., node_modules, .git, binary files). Defaults to true.', default: true, }, - respect_git_ignore: { - type: Type.BOOLEAN, + file_filtering_options: { description: - 'Optional. Whether to respect .gitignore patterns when discovering files. Only available in git repositories. Defaults to true.', - default: true, + 'Whether to respect ignore patterns from .gitignore or .geminiignore', + type: Type.OBJECT, + properties: { + respect_git_ignore: { + description: + 'Optional: Whether to respect .gitignore patterns when listing files. Only available in git repositories. Defaults to true.', + type: Type.BOOLEAN, + }, + respect_gemini_ignore: { + description: + 'Optional: Whether to respect .geminiignore patterns when listing files. Defaults to true.', + type: Type.BOOLEAN, + }, + }, }, }, required: ['paths'], @@ -257,12 +271,19 @@ Use this tool when the user's query implies needing the content of several files include = [], exclude = [], useDefaultExcludes = true, - respect_git_ignore = true, } = params; - const respectGitIgnore = - respect_git_ignore ?? this.config.getFileFilteringRespectGitIgnore(); + const defaultFileIgnores = + this.config.getFileFilteringOptions() ?? DEFAULT_FILE_FILTERING_OPTIONS; + const fileFilteringOptions = { + respectGitIgnore: + params.file_filtering_options?.respect_git_ignore ?? + defaultFileIgnores.respectGitIgnore, // Use the property from the returned object + respectGeminiIgnore: + params.file_filtering_options?.respect_gemini_ignore ?? + defaultFileIgnores.respectGeminiIgnore, // Use the property from the returned object + }; // Get centralized file discovery service const fileDiscovery = this.config.getFileService(); @@ -272,8 +293,8 @@ Use this tool when the user's query implies needing the content of several files const contentParts: PartListUnion = []; const effectiveExcludes = useDefaultExcludes - ? [...DEFAULT_EXCLUDES, ...exclude, ...this.geminiIgnorePatterns] - : [...exclude, ...this.geminiIgnorePatterns]; + ? [...DEFAULT_EXCLUDES, ...exclude] + : [...exclude]; const searchPatterns = [...inputPatterns, ...include]; if (searchPatterns.length === 0) { @@ -294,18 +315,36 @@ Use this tool when the user's query implies needing the content of several files signal, }); - const filteredEntries = respectGitIgnore + const gitFilteredEntries = fileFilteringOptions.respectGitIgnore ? fileDiscovery .filterFiles( entries.map((p) => path.relative(this.config.getTargetDir(), p)), { - respectGitIgnore, + respectGitIgnore: true, + respectGeminiIgnore: false, }, ) .map((p) => path.resolve(this.config.getTargetDir(), p)) : entries; + // Apply gemini ignore filtering if enabled + const finalFilteredEntries = fileFilteringOptions.respectGeminiIgnore + ? fileDiscovery + .filterFiles( + gitFilteredEntries.map((p) => + path.relative(this.config.getTargetDir(), p), + ), + { + respectGitIgnore: false, + respectGeminiIgnore: true, + }, + ) + .map((p) => path.resolve(this.config.getTargetDir(), p)) + : gitFilteredEntries; + let gitIgnoredCount = 0; + let geminiIgnoredCount = 0; + for (const absoluteFilePath of entries) { // Security check: ensure the glob library didn't return something outside targetDir. if (!absoluteFilePath.startsWith(this.config.getTargetDir())) { @@ -317,11 +356,23 @@ Use this tool when the user's query implies needing the content of several files } // Check if this file was filtered out by git ignore - if (respectGitIgnore && !filteredEntries.includes(absoluteFilePath)) { + if ( + fileFilteringOptions.respectGitIgnore && + !gitFilteredEntries.includes(absoluteFilePath) + ) { gitIgnoredCount++; continue; } + // Check if this file was filtered out by gemini ignore + if ( + fileFilteringOptions.respectGeminiIgnore && + !finalFilteredEntries.includes(absoluteFilePath) + ) { + geminiIgnoredCount++; + continue; + } + filesToConsider.add(absoluteFilePath); } @@ -329,7 +380,15 @@ Use this tool when the user's query implies needing the content of several files if (gitIgnoredCount > 0) { skippedFiles.push({ path: `${gitIgnoredCount} file(s)`, - reason: 'ignored', + 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', }); } } catch (error) { 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.'); |
