diff options
Diffstat (limited to 'packages/core/src')
| -rw-r--r-- | packages/core/src/config/config.ts | 2 | ||||
| -rw-r--r-- | packages/core/src/tools/memoryTool.test.ts | 8 | ||||
| -rw-r--r-- | packages/core/src/tools/memoryTool.ts | 20 | ||||
| -rw-r--r-- | packages/core/src/utils/memoryDiscovery.ts | 190 |
4 files changed, 123 insertions, 97 deletions
diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index 2576080b..4962d2a7 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -74,7 +74,7 @@ export interface ConfigParameters { geminiMdFileCount?: number; approvalMode?: ApprovalMode; showMemoryUsage?: boolean; - contextFileName?: string; + contextFileName?: string | string[]; geminiIgnorePatterns?: string[]; accessibility?: AccessibilitySettings; telemetry?: boolean; diff --git a/packages/core/src/tools/memoryTool.test.ts b/packages/core/src/tools/memoryTool.test.ts index 612a08dc..aff0cc2e 100644 --- a/packages/core/src/tools/memoryTool.test.ts +++ b/packages/core/src/tools/memoryTool.test.ts @@ -9,6 +9,7 @@ import { MemoryTool, setGeminiMdFilename, getCurrentGeminiMdFilename, + getAllGeminiMdFilenames, DEFAULT_CONTEXT_FILENAME, } from './memoryTool.js'; import * as fs from 'fs/promises'; @@ -74,6 +75,13 @@ describe('MemoryTool', () => { setGeminiMdFilename(''); expect(getCurrentGeminiMdFilename()).toBe(initialName); }); + + it('should handle an array of filenames', () => { + const newNames = ['CUSTOM_CONTEXT.md', 'ANOTHER_CONTEXT.md']; + setGeminiMdFilename(newNames); + expect(getCurrentGeminiMdFilename()).toBe('CUSTOM_CONTEXT.md'); + expect(getAllGeminiMdFilenames()).toEqual(newNames); + }); }); describe('performAddMemoryEntry (static method)', () => { diff --git a/packages/core/src/tools/memoryTool.ts b/packages/core/src/tools/memoryTool.ts index a0c62eae..2c6f41c8 100644 --- a/packages/core/src/tools/memoryTool.ts +++ b/packages/core/src/tools/memoryTool.ts @@ -51,18 +51,32 @@ export const MEMORY_SECTION_HEADER = '## Gemini Added Memories'; // This variable will hold the currently configured filename for GEMINI.md context files. // It defaults to DEFAULT_CONTEXT_FILENAME but can be overridden by setGeminiMdFilename. -let currentGeminiMdFilename = DEFAULT_CONTEXT_FILENAME; +let currentGeminiMdFilename: string | string[] = DEFAULT_CONTEXT_FILENAME; -export function setGeminiMdFilename(newFilename: string): void { - if (newFilename && newFilename.trim() !== '') { +export function setGeminiMdFilename(newFilename: string | string[]): void { + if (Array.isArray(newFilename)) { + if (newFilename.length > 0) { + currentGeminiMdFilename = newFilename.map((name) => name.trim()); + } + } else if (newFilename && newFilename.trim() !== '') { currentGeminiMdFilename = newFilename.trim(); } } export function getCurrentGeminiMdFilename(): string { + if (Array.isArray(currentGeminiMdFilename)) { + return currentGeminiMdFilename[0]; + } return currentGeminiMdFilename; } +export function getAllGeminiMdFilenames(): string[] { + if (Array.isArray(currentGeminiMdFilename)) { + return currentGeminiMdFilename; + } + return [currentGeminiMdFilename]; +} + interface SaveMemoryParams { fact: string; } diff --git a/packages/core/src/utils/memoryDiscovery.ts b/packages/core/src/utils/memoryDiscovery.ts index 07649415..2180b7a3 100644 --- a/packages/core/src/utils/memoryDiscovery.ts +++ b/packages/core/src/utils/memoryDiscovery.ts @@ -11,7 +11,7 @@ import { homedir } from 'os'; import { bfsFileSearch } from './bfsFileSearch.js'; import { GEMINI_CONFIG_DIR, - getCurrentGeminiMdFilename, + getAllGeminiMdFilenames, } from '../tools/memoryTool.js'; // Simple console logger, similar to the one previously in CLI's config.ts @@ -83,131 +83,135 @@ async function getGeminiMdFilePathsInternal( debugMode: boolean, extensionContextFilePaths: string[] = [], ): Promise<string[]> { - const resolvedCwd = path.resolve(currentWorkingDirectory); - const resolvedHome = path.resolve(userHomePath); - const globalMemoryPath = path.join( - resolvedHome, - GEMINI_CONFIG_DIR, - getCurrentGeminiMdFilename(), - ); - const paths: string[] = []; + const allPaths = new Set<string>(); + const geminiMdFilenames = getAllGeminiMdFilenames(); - if (debugMode) - logger.debug( - `Searching for ${getCurrentGeminiMdFilename()} starting from CWD: ${resolvedCwd}`, + for (const geminiMdFilename of geminiMdFilenames) { + const resolvedCwd = path.resolve(currentWorkingDirectory); + const resolvedHome = path.resolve(userHomePath); + const globalMemoryPath = path.join( + resolvedHome, + GEMINI_CONFIG_DIR, + geminiMdFilename, ); - if (debugMode) logger.debug(`User home directory: ${resolvedHome}`); - try { - await fs.access(globalMemoryPath, fsSync.constants.R_OK); - paths.push(globalMemoryPath); if (debugMode) logger.debug( - `Found readable global ${getCurrentGeminiMdFilename()}: ${globalMemoryPath}`, + `Searching for ${geminiMdFilename} starting from CWD: ${resolvedCwd}`, ); - } catch { - if (debugMode) - logger.debug( - `Global ${getCurrentGeminiMdFilename()} not found or not readable: ${globalMemoryPath}`, - ); - } + if (debugMode) logger.debug(`User home directory: ${resolvedHome}`); - const projectRoot = await findProjectRoot(resolvedCwd); - if (debugMode) - logger.debug(`Determined project root: ${projectRoot ?? 'None'}`); + try { + await fs.access(globalMemoryPath, fsSync.constants.R_OK); + allPaths.add(globalMemoryPath); + if (debugMode) + logger.debug( + `Found readable global ${geminiMdFilename}: ${globalMemoryPath}`, + ); + } catch { + if (debugMode) + logger.debug( + `Global ${geminiMdFilename} not found or not readable: ${globalMemoryPath}`, + ); + } - const upwardPaths: string[] = []; - let currentDir = resolvedCwd; - // Determine the directory that signifies the top of the project or user-specific space. - const ultimateStopDir = projectRoot - ? path.dirname(projectRoot) - : path.dirname(resolvedHome); + const projectRoot = await findProjectRoot(resolvedCwd); + if (debugMode) + logger.debug(`Determined project root: ${projectRoot ?? 'None'}`); - while (currentDir && currentDir !== path.dirname(currentDir)) { - // Loop until filesystem root or currentDir is empty - if (debugMode) { - logger.debug( - `Checking for ${getCurrentGeminiMdFilename()} in (upward scan): ${currentDir}`, - ); - } + const upwardPaths: string[] = []; + let currentDir = resolvedCwd; + // Determine the directory that signifies the top of the project or user-specific space. + const ultimateStopDir = projectRoot + ? path.dirname(projectRoot) + : path.dirname(resolvedHome); - // Skip the global .gemini directory itself during upward scan from CWD, - // as global is handled separately and explicitly first. - if (currentDir === path.join(resolvedHome, GEMINI_CONFIG_DIR)) { + while (currentDir && currentDir !== path.dirname(currentDir)) { + // Loop until filesystem root or currentDir is empty if (debugMode) { logger.debug( - `Upward scan reached global config dir path, stopping upward search here: ${currentDir}`, + `Checking for ${geminiMdFilename} in (upward scan): ${currentDir}`, ); } - break; - } - const potentialPath = path.join(currentDir, getCurrentGeminiMdFilename()); - try { - await fs.access(potentialPath, fsSync.constants.R_OK); - // Add to upwardPaths only if it's not the already added globalMemoryPath - if (potentialPath !== globalMemoryPath) { - upwardPaths.unshift(potentialPath); + // Skip the global .gemini directory itself during upward scan from CWD, + // as global is handled separately and explicitly first. + if (currentDir === path.join(resolvedHome, GEMINI_CONFIG_DIR)) { if (debugMode) { logger.debug( - `Found readable upward ${getCurrentGeminiMdFilename()}: ${potentialPath}`, + `Upward scan reached global config dir path, stopping upward search here: ${currentDir}`, ); } + break; } - } catch { - if (debugMode) { - logger.debug( - `Upward ${getCurrentGeminiMdFilename()} not found or not readable in: ${currentDir}`, - ); + + const potentialPath = path.join(currentDir, geminiMdFilename); + try { + await fs.access(potentialPath, fsSync.constants.R_OK); + // Add to upwardPaths only if it's not the already added globalMemoryPath + if (potentialPath !== globalMemoryPath) { + upwardPaths.unshift(potentialPath); + if (debugMode) { + logger.debug( + `Found readable upward ${geminiMdFilename}: ${potentialPath}`, + ); + } + } + } catch { + if (debugMode) { + logger.debug( + `Upward ${geminiMdFilename} not found or not readable in: ${currentDir}`, + ); + } } - } - // Stop condition: if currentDir is the ultimateStopDir, break after this iteration. - if (currentDir === ultimateStopDir) { - if (debugMode) - logger.debug( - `Reached ultimate stop directory for upward scan: ${currentDir}`, - ); - break; - } + // Stop condition: if currentDir is the ultimateStopDir, break after this iteration. + if (currentDir === ultimateStopDir) { + if (debugMode) + logger.debug( + `Reached ultimate stop directory for upward scan: ${currentDir}`, + ); + break; + } - currentDir = path.dirname(currentDir); - } - paths.push(...upwardPaths); + currentDir = path.dirname(currentDir); + } + upwardPaths.forEach((p) => allPaths.add(p)); - const downwardPaths = await bfsFileSearch(resolvedCwd, { - fileName: getCurrentGeminiMdFilename(), - maxDirs: MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY, - debug: debugMode, - respectGitIgnore: true, - projectRoot: projectRoot || resolvedCwd, - }); - downwardPaths.sort(); // Sort for consistent ordering, though hierarchy might be more complex - if (debugMode && downwardPaths.length > 0) - logger.debug( - `Found downward ${getCurrentGeminiMdFilename()} files (sorted): ${JSON.stringify( - downwardPaths, - )}`, - ); - // Add downward paths only if they haven't been included already (e.g. from upward scan) - for (const dPath of downwardPaths) { - if (!paths.includes(dPath)) { - paths.push(dPath); + const downwardPaths = await bfsFileSearch(resolvedCwd, { + fileName: geminiMdFilename, + maxDirs: MAX_DIRECTORIES_TO_SCAN_FOR_MEMORY, + debug: debugMode, + respectGitIgnore: true, + projectRoot: projectRoot || resolvedCwd, + }); + downwardPaths.sort(); // Sort for consistent ordering, though hierarchy might be more complex + if (debugMode && downwardPaths.length > 0) + logger.debug( + `Found downward ${geminiMdFilename} files (sorted): ${JSON.stringify( + downwardPaths, + )}`, + ); + // Add downward paths only if they haven't been included already (e.g. from upward scan) + for (const dPath of downwardPaths) { + allPaths.add(dPath); } } // Add extension context file paths for (const extensionPath of extensionContextFilePaths) { - if (!paths.includes(extensionPath)) { - paths.push(extensionPath); - } + allPaths.add(extensionPath); } + const finalPaths = Array.from(allPaths); + if (debugMode) logger.debug( - `Final ordered ${getCurrentGeminiMdFilename()} paths to read: ${JSON.stringify(paths)}`, + `Final ordered ${getAllGeminiMdFilenames()} paths to read: ${JSON.stringify( + finalPaths, + )}`, ); - return paths; + return finalPaths; } async function readGeminiMdFiles( @@ -228,7 +232,7 @@ async function readGeminiMdFiles( if (!isTestEnv) { const message = error instanceof Error ? error.message : String(error); logger.warn( - `Warning: Could not read ${getCurrentGeminiMdFilename()} file at ${filePath}. Error: ${message}`, + `Warning: Could not read ${getAllGeminiMdFilenames()} file at ${filePath}. Error: ${message}`, ); } results.push({ filePath, content: null }); // Still include it with null content |
