Adding list sub command to memoryCommand to list the path of GEMINI.md files (#10108)

Co-authored-by: Abhi <43648792+abhipatel12@users.noreply.github.com>
This commit is contained in:
sgnagnarella
2025-10-02 17:46:54 -04:00
committed by GitHub
parent 0713dd4d2d
commit 43bac6a038
8 changed files with 250 additions and 63 deletions
+16 -14
View File
@@ -400,7 +400,7 @@ export async function loadHierarchicalGeminiMemory(
folderTrust: boolean,
memoryImportFormat: 'flat' | 'tree' = 'tree',
fileFilteringOptions?: FileFilteringOptions,
): Promise<{ memoryContent: string; fileCount: number }> {
): Promise<{ memoryContent: string; fileCount: number; filePaths: string[] }> {
// FIX: Use real, canonical paths for a reliable comparison to handle symlinks.
const realCwd = fs.realpathSync(path.resolve(currentWorkingDirectory));
const realHome = fs.realpathSync(path.resolve(homedir()));
@@ -493,19 +493,20 @@ export async function loadCliConfig(
.concat((argv.includeDirectories || []).map(resolvePath));
// Call the (now wrapper) loadHierarchicalGeminiMemory which calls the server's version
const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
cwd,
settings.context?.loadMemoryFromIncludeDirectories
? includeDirectories
: [],
debugMode,
fileService,
settings,
extensionContextFilePaths,
trustedFolder,
memoryImportFormat,
fileFiltering,
);
const { memoryContent, fileCount, filePaths } =
await loadHierarchicalGeminiMemory(
cwd,
settings.context?.loadMemoryFromIncludeDirectories
? includeDirectories
: [],
debugMode,
fileService,
settings,
extensionContextFilePaths,
trustedFolder,
memoryImportFormat,
fileFiltering,
);
let mcpServers = mergeMcpServers(settings, activeExtensions);
const question = argv.promptInteractive || argv.prompt || '';
@@ -657,6 +658,7 @@ export async function loadCliConfig(
mcpServers,
userMemory: memoryContent,
geminiMdFileCount: fileCount,
geminiMdFilePaths: filePaths,
approvalMode,
showMemoryUsage:
argv.showMemoryUsage || settings.ui?.showMemoryUsage || false,
+16 -13
View File
@@ -510,22 +510,25 @@ Logging in with Google... Please restart Gemini CLI to continue.
Date.now(),
);
try {
const { memoryContent, fileCount } = await loadHierarchicalGeminiMemory(
process.cwd(),
settings.merged.context?.loadMemoryFromIncludeDirectories
? config.getWorkspaceContext().getDirectories()
: [],
config.getDebugMode(),
config.getFileService(),
settings.merged,
config.getExtensionContextFilePaths(),
config.isTrustedFolder(),
settings.merged.context?.importFormat || 'tree', // Use setting or default to 'tree'
config.getFileFilteringOptions(),
);
const { memoryContent, fileCount, filePaths } =
await loadHierarchicalGeminiMemory(
process.cwd(),
settings.merged.context?.loadMemoryFromIncludeDirectories
? config.getWorkspaceContext().getDirectories()
: [],
config.getDebugMode(),
config.getFileService(),
settings.merged,
config.getExtensionContextFilePaths(),
config.isTrustedFolder(),
settings.merged.context?.importFormat || 'tree', // Use setting or default to 'tree'
config.getFileFilteringOptions(),
);
config.setUserMemory(memoryContent);
config.setGeminiMdFileCount(fileCount);
config.setGeminiMdFilePaths(filePaths);
setGeminiMdFileCount(fileCount);
historyManager.addItem(
@@ -7,7 +7,7 @@
import type { Mock } from 'vitest';
import { vi, describe, it, expect, beforeEach } from 'vitest';
import { memoryCommand } from './memoryCommand.js';
import type { SlashCommand, type CommandContext } from './types.js';
import type { SlashCommand, CommandContext } from './types.js';
import { createMockCommandContext } from '../../test-utils/mockCommandContext.js';
import { MessageType } from '../types.js';
import type { LoadedSettings } from '../../config/settings.js';
@@ -36,7 +36,9 @@ const mockLoadServerHierarchicalMemory = loadServerHierarchicalMemory as Mock;
describe('memoryCommand', () => {
let mockContext: CommandContext;
const getSubCommand = (name: 'show' | 'add' | 'refresh'): SlashCommand => {
const getSubCommand = (
name: 'show' | 'add' | 'refresh' | 'list',
): SlashCommand => {
const subCommand = memoryCommand.subCommands?.find(
(cmd) => cmd.name === name,
);
@@ -151,14 +153,18 @@ describe('memoryCommand', () => {
let refreshCommand: SlashCommand;
let mockSetUserMemory: Mock;
let mockSetGeminiMdFileCount: Mock;
let mockSetGeminiMdFilePaths: Mock;
beforeEach(() => {
refreshCommand = getSubCommand('refresh');
mockSetUserMemory = vi.fn();
mockSetGeminiMdFileCount = vi.fn();
mockSetGeminiMdFilePaths = vi.fn();
const mockConfig = {
setUserMemory: mockSetUserMemory,
setGeminiMdFileCount: mockSetGeminiMdFileCount,
setGeminiMdFilePaths: mockSetGeminiMdFilePaths,
getWorkingDir: () => '/test/dir',
getDebugMode: () => false,
getFileService: () => ({}) as FileDiscoveryService,
@@ -176,7 +182,7 @@ describe('memoryCommand', () => {
mockContext = createMockCommandContext({
services: {
config: Promise.resolve(mockConfig),
config: mockConfig,
settings: {
merged: {
memoryDiscoveryMaxDirs: 1000,
@@ -193,6 +199,7 @@ describe('memoryCommand', () => {
const refreshResult: LoadServerHierarchicalMemoryResponse = {
memoryContent: 'new memory content',
fileCount: 2,
filePaths: ['/path/one/GEMINI.md', '/path/two/GEMINI.md'],
};
mockLoadServerHierarchicalMemory.mockResolvedValue(refreshResult);
@@ -213,6 +220,9 @@ describe('memoryCommand', () => {
expect(mockSetGeminiMdFileCount).toHaveBeenCalledWith(
refreshResult.fileCount,
);
expect(mockSetGeminiMdFilePaths).toHaveBeenCalledWith(
refreshResult.filePaths,
);
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
@@ -226,7 +236,7 @@ describe('memoryCommand', () => {
it('should display success message when memory is refreshed with no content', async () => {
if (!refreshCommand.action) throw new Error('Command has no action');
const refreshResult = { memoryContent: '', fileCount: 0 };
const refreshResult = { memoryContent: '', fileCount: 0, filePaths: [] };
mockLoadServerHierarchicalMemory.mockResolvedValue(refreshResult);
await refreshCommand.action(mockContext, '');
@@ -234,6 +244,7 @@ describe('memoryCommand', () => {
expect(loadServerHierarchicalMemory).toHaveBeenCalledOnce();
expect(mockSetUserMemory).toHaveBeenCalledWith('');
expect(mockSetGeminiMdFileCount).toHaveBeenCalledWith(0);
expect(mockSetGeminiMdFilePaths).toHaveBeenCalledWith([]);
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
@@ -255,6 +266,7 @@ describe('memoryCommand', () => {
expect(loadServerHierarchicalMemory).toHaveBeenCalledOnce();
expect(mockSetUserMemory).not.toHaveBeenCalled();
expect(mockSetGeminiMdFileCount).not.toHaveBeenCalled();
expect(mockSetGeminiMdFilePaths).not.toHaveBeenCalled();
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
@@ -289,4 +301,54 @@ describe('memoryCommand', () => {
expect(loadServerHierarchicalMemory).not.toHaveBeenCalled();
});
});
describe('/memory list', () => {
let listCommand: SlashCommand;
let mockGetGeminiMdfilePaths: Mock;
beforeEach(() => {
listCommand = getSubCommand('list');
mockGetGeminiMdfilePaths = vi.fn();
mockContext = createMockCommandContext({
services: {
config: {
getGeminiMdFilePaths: mockGetGeminiMdfilePaths,
},
},
});
});
it('should display a message if no GEMINI.md files are found', async () => {
if (!listCommand.action) throw new Error('Command has no action');
mockGetGeminiMdfilePaths.mockReturnValue([]);
await listCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
type: MessageType.INFO,
text: 'No GEMINI.md files in use.',
},
expect.any(Number),
);
});
it('should display the file count and paths if they exist', async () => {
if (!listCommand.action) throw new Error('Command has no action');
const filePaths = ['/path/one/GEMINI.md', '/path/two/GEMINI.md'];
mockGetGeminiMdfilePaths.mockReturnValue(filePaths);
await listCommand.action(mockContext, '');
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
{
type: MessageType.INFO,
text: `There are 2 GEMINI.md file(s) in use:\n\n${filePaths.join('\n')}`,
},
expect.any(Number),
);
});
});
});
+24 -1
View File
@@ -83,7 +83,7 @@ export const memoryCommand: SlashCommand = {
try {
const config = await context.services.config;
if (config) {
const { memoryContent, fileCount } =
const { memoryContent, fileCount, filePaths } =
await loadServerHierarchicalMemory(
config.getWorkingDir(),
config.shouldLoadMemoryFromIncludeDirectories()
@@ -100,6 +100,7 @@ export const memoryCommand: SlashCommand = {
);
config.setUserMemory(memoryContent);
config.setGeminiMdFileCount(fileCount);
config.setGeminiMdFilePaths(filePaths);
const successMessage =
memoryContent.length > 0
@@ -126,5 +127,27 @@ export const memoryCommand: SlashCommand = {
}
},
},
{
name: 'list',
description: 'Lists the paths of the GEMINI.md files in use.',
kind: CommandKind.BUILT_IN,
action: async (context) => {
const filePaths = context.services.config?.getGeminiMdFilePaths() || [];
const fileCount = filePaths.length;
const messageContent =
fileCount > 0
? `There are ${fileCount} GEMINI.md file(s) in use:\n\n${filePaths.join('\n')}`
: 'No GEMINI.md files in use.';
context.ui.addItem(
{
type: MessageType.INFO,
text: messageContent,
},
Date.now(),
);
},
},
],
};