mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-07-15 04:20:57 -07:00
feat(core): add project-level memory scope to save_memory tool (#24161)
This commit is contained in:
@@ -17,6 +17,7 @@ vi.mock('../utils/memoryDiscovery.js', async (importOriginal) => {
|
||||
return {
|
||||
...actual,
|
||||
getGlobalMemoryPaths: vi.fn(),
|
||||
getUserProjectMemoryPaths: vi.fn(),
|
||||
getExtensionMemoryPaths: vi.fn(),
|
||||
getEnvironmentMemoryPaths: vi.fn(),
|
||||
readGeminiMdFiles: vi.fn(),
|
||||
@@ -47,12 +48,18 @@ describe('ContextManager', () => {
|
||||
}),
|
||||
isTrustedFolder: vi.fn().mockReturnValue(true),
|
||||
getMemoryBoundaryMarkers: vi.fn().mockReturnValue(['.git']),
|
||||
storage: {
|
||||
getProjectMemoryDir: vi
|
||||
.fn()
|
||||
.mockReturnValue('/home/user/.gemini/memory/test-project'),
|
||||
},
|
||||
} as unknown as Config;
|
||||
|
||||
contextManager = new ContextManager(mockConfig);
|
||||
vi.clearAllMocks();
|
||||
vi.spyOn(coreEvents, 'emit');
|
||||
vi.mocked(memoryDiscovery.getExtensionMemoryPaths).mockReturnValue([]);
|
||||
vi.mocked(memoryDiscovery.getUserProjectMemoryPaths).mockResolvedValue([]);
|
||||
// default mock: deduplication returns paths as-is (no deduplication)
|
||||
vi.mocked(
|
||||
memoryDiscovery.deduplicatePathsByFileIdentity,
|
||||
|
||||
@@ -8,6 +8,7 @@ import {
|
||||
loadJitSubdirectoryMemory,
|
||||
concatenateInstructions,
|
||||
getGlobalMemoryPaths,
|
||||
getUserProjectMemoryPaths,
|
||||
getExtensionMemoryPaths,
|
||||
getEnvironmentMemoryPaths,
|
||||
readGeminiMdFiles,
|
||||
@@ -25,6 +26,7 @@ export class ContextManager {
|
||||
private globalMemory: string = '';
|
||||
private extensionMemory: string = '';
|
||||
private projectMemory: string = '';
|
||||
private userProjectMemoryContent: string = '';
|
||||
|
||||
constructor(config: Config) {
|
||||
this.config = config;
|
||||
@@ -45,7 +47,7 @@ export class ContextManager {
|
||||
}
|
||||
|
||||
private async discoverMemoryPaths() {
|
||||
const [global, extension, project] = await Promise.all([
|
||||
const [global, extension, project, userProjectMemory] = await Promise.all([
|
||||
getGlobalMemoryPaths(),
|
||||
Promise.resolve(
|
||||
getExtensionMemoryPaths(this.config.getExtensionLoader()),
|
||||
@@ -56,18 +58,25 @@ export class ContextManager {
|
||||
this.config.getMemoryBoundaryMarkers(),
|
||||
)
|
||||
: Promise.resolve([]),
|
||||
getUserProjectMemoryPaths(this.config.storage.getProjectMemoryDir()),
|
||||
]);
|
||||
|
||||
return { global, extension, project };
|
||||
return { global, extension, project, userProjectMemory };
|
||||
}
|
||||
|
||||
private async loadMemoryContents(paths: {
|
||||
global: string[];
|
||||
extension: string[];
|
||||
project: string[];
|
||||
userProjectMemory: string[];
|
||||
}) {
|
||||
const allPathsStringDeduped = Array.from(
|
||||
new Set([...paths.global, ...paths.extension, ...paths.project]),
|
||||
new Set([
|
||||
...paths.global,
|
||||
...paths.extension,
|
||||
...paths.project,
|
||||
...paths.userProjectMemory,
|
||||
]),
|
||||
);
|
||||
|
||||
// deduplicate by file identity to handle case-insensitive filesystems
|
||||
@@ -97,13 +106,19 @@ export class ContextManager {
|
||||
}
|
||||
|
||||
private categorizeMemoryContents(
|
||||
paths: { global: string[]; extension: string[]; project: string[] },
|
||||
paths: {
|
||||
global: string[];
|
||||
extension: string[];
|
||||
project: string[];
|
||||
userProjectMemory: string[];
|
||||
},
|
||||
contentsMap: Map<string, GeminiFileContent>,
|
||||
) {
|
||||
const hierarchicalMemory = categorizeAndConcatenate(paths, contentsMap);
|
||||
|
||||
this.globalMemory = hierarchicalMemory.global || '';
|
||||
this.extensionMemory = hierarchicalMemory.extension || '';
|
||||
this.userProjectMemoryContent = hierarchicalMemory.userProjectMemory || '';
|
||||
|
||||
const mcpInstructions =
|
||||
this.config.getMcpClientManager()?.getMcpInstructions() || '';
|
||||
@@ -174,6 +189,10 @@ export class ContextManager {
|
||||
return this.projectMemory;
|
||||
}
|
||||
|
||||
getUserProjectMemory(): string {
|
||||
return this.userProjectMemoryContent;
|
||||
}
|
||||
|
||||
private markAsLoaded(paths: string[]): void {
|
||||
paths.forEach((p) => this.loadedPaths.add(p));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user