mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 07:10:55 +00:00
Merge pull request #2368 from huww98/fix/memory-show-multi-file-support
fix(cli): `/memory show --project` and `--global` now display all configured context files
This commit is contained in:
commit
b57b8ed5fe
2 changed files with 159 additions and 44 deletions
|
|
@ -168,6 +168,116 @@ describe('memoryCommand', () => {
|
|||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fall back to AGENTS.md when QWEN.md does not exist for --project', async () => {
|
||||
const projectCommand = showCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === '--project',
|
||||
);
|
||||
if (!projectCommand?.action) throw new Error('Command has no action');
|
||||
|
||||
setGeminiMdFilename(['QWEN.md', 'AGENTS.md']);
|
||||
vi.spyOn(process, 'cwd').mockReturnValue('/test/project');
|
||||
mockReadFile.mockImplementation(async (filePath: string) => {
|
||||
if (filePath.endsWith('AGENTS.md')) return 'agents memory content';
|
||||
throw new Error('ENOENT');
|
||||
});
|
||||
|
||||
await projectCommand.action(mockContext, '');
|
||||
|
||||
const expectedPath = path.join('/test/project', 'AGENTS.md');
|
||||
expect(mockReadFile).toHaveBeenCalledWith(expectedPath, 'utf-8');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: expect.stringContaining('agents memory content'),
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should fall back to AGENTS.md when QWEN.md does not exist for --global', async () => {
|
||||
const globalCommand = showCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === '--global',
|
||||
);
|
||||
if (!globalCommand?.action) throw new Error('Command has no action');
|
||||
|
||||
setGeminiMdFilename(['QWEN.md', 'AGENTS.md']);
|
||||
vi.spyOn(os, 'homedir').mockReturnValue('/home/user');
|
||||
mockReadFile.mockImplementation(async (filePath: string) => {
|
||||
if (filePath.endsWith('AGENTS.md')) return 'global agents memory';
|
||||
throw new Error('ENOENT');
|
||||
});
|
||||
|
||||
await globalCommand.action(mockContext, '');
|
||||
|
||||
const expectedPath = path.join('/home/user', QWEN_DIR, 'AGENTS.md');
|
||||
expect(mockReadFile).toHaveBeenCalledWith(expectedPath, 'utf-8');
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: expect.stringContaining('global agents memory'),
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
});
|
||||
|
||||
it('should show content from both QWEN.md and AGENTS.md for --project when both exist', async () => {
|
||||
const projectCommand = showCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === '--project',
|
||||
);
|
||||
if (!projectCommand?.action) throw new Error('Command has no action');
|
||||
|
||||
setGeminiMdFilename(['QWEN.md', 'AGENTS.md']);
|
||||
vi.spyOn(process, 'cwd').mockReturnValue('/test/project');
|
||||
mockReadFile.mockImplementation(async (filePath: string) => {
|
||||
if (filePath.endsWith('QWEN.md')) return 'qwen memory';
|
||||
if (filePath.endsWith('AGENTS.md')) return 'agents memory';
|
||||
throw new Error('ENOENT');
|
||||
});
|
||||
|
||||
await projectCommand.action(mockContext, '');
|
||||
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
path.join('/test/project', 'QWEN.md'),
|
||||
'utf-8',
|
||||
);
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
path.join('/test/project', 'AGENTS.md'),
|
||||
'utf-8',
|
||||
);
|
||||
const addItemCall = (mockContext.ui.addItem as Mock).mock.calls[0][0];
|
||||
expect(addItemCall.text).toContain('qwen memory');
|
||||
expect(addItemCall.text).toContain('agents memory');
|
||||
});
|
||||
|
||||
it('should show content from both files for --global when both exist', async () => {
|
||||
const globalCommand = showCommand.subCommands?.find(
|
||||
(cmd) => cmd.name === '--global',
|
||||
);
|
||||
if (!globalCommand?.action) throw new Error('Command has no action');
|
||||
|
||||
setGeminiMdFilename(['QWEN.md', 'AGENTS.md']);
|
||||
vi.spyOn(os, 'homedir').mockReturnValue('/home/user');
|
||||
mockReadFile.mockImplementation(async (filePath: string) => {
|
||||
if (filePath.endsWith('QWEN.md')) return 'global qwen memory';
|
||||
if (filePath.endsWith('AGENTS.md')) return 'global agents memory';
|
||||
throw new Error('ENOENT');
|
||||
});
|
||||
|
||||
await globalCommand.action(mockContext, '');
|
||||
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
path.join('/home/user', QWEN_DIR, 'QWEN.md'),
|
||||
'utf-8',
|
||||
);
|
||||
expect(mockReadFile).toHaveBeenCalledWith(
|
||||
path.join('/home/user', QWEN_DIR, 'AGENTS.md'),
|
||||
'utf-8',
|
||||
);
|
||||
const addItemCall = (mockContext.ui.addItem as Mock).mock.calls[0][0];
|
||||
expect(addItemCall.text).toContain('global qwen memory');
|
||||
expect(addItemCall.text).toContain('global agents memory');
|
||||
});
|
||||
});
|
||||
|
||||
describe('/memory add', () => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
import {
|
||||
getErrorMessage,
|
||||
getCurrentGeminiMdFilename,
|
||||
getAllGeminiMdFilenames,
|
||||
loadServerHierarchicalMemory,
|
||||
QWEN_DIR,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
|
|
@ -18,6 +18,28 @@ import type { SlashCommand, SlashCommandActionReturn } from './types.js';
|
|||
import { CommandKind } from './types.js';
|
||||
import { t } from '../../i18n/index.js';
|
||||
|
||||
/**
|
||||
* Read all existing memory files from the configured filenames in a directory.
|
||||
* Returns an array of found files with their paths and contents.
|
||||
*/
|
||||
async function findAllExistingMemoryFiles(
|
||||
dir: string,
|
||||
): Promise<Array<{ filePath: string; content: string }>> {
|
||||
const results: Array<{ filePath: string; content: string }> = [];
|
||||
for (const filename of getAllGeminiMdFilenames()) {
|
||||
const filePath = path.join(dir, filename);
|
||||
try {
|
||||
const content = await fs.readFile(filePath, 'utf-8');
|
||||
if (content.trim().length > 0) {
|
||||
results.push({ filePath, content });
|
||||
}
|
||||
} catch {
|
||||
// File doesn't exist, try next
|
||||
}
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
export const memoryCommand: SlashCommand = {
|
||||
name: 'memory',
|
||||
get description() {
|
||||
|
|
@ -56,37 +78,27 @@ export const memoryCommand: SlashCommand = {
|
|||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
try {
|
||||
const workingDir =
|
||||
context.services.config?.getWorkingDir?.() ?? process.cwd();
|
||||
const projectMemoryPath = path.join(
|
||||
workingDir,
|
||||
getCurrentGeminiMdFilename(),
|
||||
);
|
||||
const memoryContent = await fs.readFile(
|
||||
projectMemoryPath,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const messageContent =
|
||||
memoryContent.trim().length > 0
|
||||
? t(
|
||||
'Project memory content from {{path}}:\n\n---\n{{content}}\n---',
|
||||
{
|
||||
path: projectMemoryPath,
|
||||
content: memoryContent,
|
||||
},
|
||||
)
|
||||
: t('Project memory is currently empty.');
|
||||
const workingDir =
|
||||
context.services.config?.getWorkingDir?.() ?? process.cwd();
|
||||
const results = await findAllExistingMemoryFiles(workingDir);
|
||||
|
||||
if (results.length > 0) {
|
||||
const combined = results
|
||||
.map((r) =>
|
||||
t(
|
||||
'Project memory content from {{path}}:\n\n---\n{{content}}\n---',
|
||||
{ path: r.filePath, content: r.content },
|
||||
),
|
||||
)
|
||||
.join('\n\n');
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: messageContent,
|
||||
text: combined,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
} catch (_error) {
|
||||
} else {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
|
|
@ -106,32 +118,25 @@ export const memoryCommand: SlashCommand = {
|
|||
},
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
try {
|
||||
const globalMemoryPath = path.join(
|
||||
os.homedir(),
|
||||
QWEN_DIR,
|
||||
getCurrentGeminiMdFilename(),
|
||||
);
|
||||
const globalMemoryContent = await fs.readFile(
|
||||
globalMemoryPath,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const messageContent =
|
||||
globalMemoryContent.trim().length > 0
|
||||
? t('Global memory content:\n\n---\n{{content}}\n---', {
|
||||
content: globalMemoryContent,
|
||||
})
|
||||
: t('Global memory is currently empty.');
|
||||
const globalDir = path.join(os.homedir(), QWEN_DIR);
|
||||
const results = await findAllExistingMemoryFiles(globalDir);
|
||||
|
||||
if (results.length > 0) {
|
||||
const combined = results
|
||||
.map((r) =>
|
||||
t('Global memory content:\n\n---\n{{content}}\n---', {
|
||||
content: r.content,
|
||||
}),
|
||||
)
|
||||
.join('\n\n');
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: messageContent,
|
||||
text: combined,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
} catch (_error) {
|
||||
} else {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue