mirror of
https://github.com/QwenLM/qwen-code.git
synced 2025-12-20 16:57:46 +00:00
Merge pull request #450 from QwenLM/feature/qwenmd
use sub-command to switch between project and global memory ops
This commit is contained in:
@@ -117,7 +117,7 @@ describe('memoryCommand', () => {
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add <text to remember>',
|
||||
content: 'Usage: /memory add [--global|--project] <text to remember>',
|
||||
});
|
||||
|
||||
expect(mockContext.ui.addItem).not.toHaveBeenCalled();
|
||||
@@ -132,7 +132,7 @@ describe('memoryCommand', () => {
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to memory: "${fact}"`,
|
||||
text: `Attempting to save to memory : "${fact}"`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
@@ -143,6 +143,61 @@ describe('memoryCommand', () => {
|
||||
toolArgs: { fact },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle --global flag and add scope to tool args', () => {
|
||||
if (!addCommand.action) throw new Error('Command has no action');
|
||||
|
||||
const fact = 'remember this globally';
|
||||
const result = addCommand.action(mockContext, `--global ${fact}`);
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to memory (global): "${fact}"`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'tool',
|
||||
toolName: 'save_memory',
|
||||
toolArgs: { fact, scope: 'global' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should handle --project flag and add scope to tool args', () => {
|
||||
if (!addCommand.action) throw new Error('Command has no action');
|
||||
|
||||
const fact = 'remember this for project';
|
||||
const result = addCommand.action(mockContext, `--project ${fact}`);
|
||||
|
||||
expect(mockContext.ui.addItem).toHaveBeenCalledWith(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to memory (project): "${fact}"`,
|
||||
},
|
||||
expect.any(Number),
|
||||
);
|
||||
|
||||
expect(result).toEqual({
|
||||
type: 'tool',
|
||||
toolName: 'save_memory',
|
||||
toolArgs: { fact, scope: 'project' },
|
||||
});
|
||||
});
|
||||
|
||||
it('should return error if flag is provided but no fact follows', () => {
|
||||
if (!addCommand.action) throw new Error('Command has no action');
|
||||
|
||||
const result = addCommand.action(mockContext, '--global ');
|
||||
expect(result).toEqual({
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add [--global|--project] <text to remember>',
|
||||
});
|
||||
|
||||
expect(mockContext.ui.addItem).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('/memory refresh', () => {
|
||||
@@ -173,7 +228,7 @@ describe('memoryCommand', () => {
|
||||
|
||||
mockContext = createMockCommandContext({
|
||||
services: {
|
||||
config: Promise.resolve(mockConfig),
|
||||
config: mockConfig,
|
||||
settings: {
|
||||
merged: {
|
||||
memoryDiscoveryMaxDirs: 1000,
|
||||
|
||||
@@ -7,7 +7,11 @@
|
||||
import {
|
||||
getErrorMessage,
|
||||
loadServerHierarchicalMemory,
|
||||
QWEN_DIR,
|
||||
} from '@qwen-code/qwen-code-core';
|
||||
import path from 'node:path';
|
||||
import os from 'os';
|
||||
import fs from 'fs/promises';
|
||||
import { MessageType } from '../types.js';
|
||||
import {
|
||||
CommandKind,
|
||||
@@ -41,24 +45,136 @@ export const memoryCommand: SlashCommand = {
|
||||
Date.now(),
|
||||
);
|
||||
},
|
||||
subCommands: [
|
||||
{
|
||||
name: '--project',
|
||||
description: 'Show project-level memory contents.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
try {
|
||||
const projectMemoryPath = path.join(process.cwd(), 'QWEN.md');
|
||||
const memoryContent = await fs.readFile(
|
||||
projectMemoryPath,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const messageContent =
|
||||
memoryContent.trim().length > 0
|
||||
? `Project memory content from ${projectMemoryPath}:\n\n---\n${memoryContent}\n---`
|
||||
: 'Project memory is currently empty.';
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: messageContent,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
} catch (_error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Project memory file not found or is currently empty.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '--global',
|
||||
description: 'Show global memory contents.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: async (context) => {
|
||||
try {
|
||||
const globalMemoryPath = path.join(
|
||||
os.homedir(),
|
||||
QWEN_DIR,
|
||||
'QWEN.md',
|
||||
);
|
||||
const globalMemoryContent = await fs.readFile(
|
||||
globalMemoryPath,
|
||||
'utf-8',
|
||||
);
|
||||
|
||||
const messageContent =
|
||||
globalMemoryContent.trim().length > 0
|
||||
? `Global memory content:\n\n---\n${globalMemoryContent}\n---`
|
||||
: 'Global memory is currently empty.';
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: messageContent,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
} catch (_error) {
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: 'Global memory file not found or is currently empty.',
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'add',
|
||||
description: 'Add content to the memory.',
|
||||
description:
|
||||
'Add content to the memory. Use --global for global memory or --project for project memory.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add <text to remember>',
|
||||
content:
|
||||
'Usage: /memory add [--global|--project] <text to remember>',
|
||||
};
|
||||
}
|
||||
|
||||
const trimmedArgs = args.trim();
|
||||
let scope: 'global' | 'project' | undefined;
|
||||
let fact: string;
|
||||
|
||||
// Check for scope flags
|
||||
if (trimmedArgs.startsWith('--global ')) {
|
||||
scope = 'global';
|
||||
fact = trimmedArgs.substring('--global '.length).trim();
|
||||
} else if (trimmedArgs.startsWith('--project ')) {
|
||||
scope = 'project';
|
||||
fact = trimmedArgs.substring('--project '.length).trim();
|
||||
} else if (trimmedArgs === '--global' || trimmedArgs === '--project') {
|
||||
// Flag provided but no text after it
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
'Usage: /memory add [--global|--project] <text to remember>',
|
||||
};
|
||||
} else {
|
||||
// No scope specified, will be handled by the tool
|
||||
fact = trimmedArgs;
|
||||
}
|
||||
|
||||
if (!fact || fact.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content:
|
||||
'Usage: /memory add [--global|--project] <text to remember>',
|
||||
};
|
||||
}
|
||||
|
||||
const scopeText = scope ? `(${scope})` : '';
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to memory: "${args.trim()}"`,
|
||||
text: `Attempting to save to memory ${scopeText}: "${fact}"`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
@@ -66,9 +182,67 @@ export const memoryCommand: SlashCommand = {
|
||||
return {
|
||||
type: 'tool',
|
||||
toolName: 'save_memory',
|
||||
toolArgs: { fact: args.trim() },
|
||||
toolArgs: scope ? { fact, scope } : { fact },
|
||||
};
|
||||
},
|
||||
subCommands: [
|
||||
{
|
||||
name: '--project',
|
||||
description: 'Add content to project-level memory.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add --project <text to remember>',
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to project memory: "${args.trim()}"`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'tool',
|
||||
toolName: 'save_memory',
|
||||
toolArgs: { fact: args.trim(), scope: 'project' },
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
name: '--global',
|
||||
description: 'Add content to global memory.',
|
||||
kind: CommandKind.BUILT_IN,
|
||||
action: (context, args): SlashCommandActionReturn | void => {
|
||||
if (!args || args.trim() === '') {
|
||||
return {
|
||||
type: 'message',
|
||||
messageType: 'error',
|
||||
content: 'Usage: /memory add --global <text to remember>',
|
||||
};
|
||||
}
|
||||
|
||||
context.ui.addItem(
|
||||
{
|
||||
type: MessageType.INFO,
|
||||
text: `Attempting to save to global memory: "${args.trim()}"`,
|
||||
},
|
||||
Date.now(),
|
||||
);
|
||||
|
||||
return {
|
||||
type: 'tool',
|
||||
toolName: 'save_memory',
|
||||
toolArgs: { fact: args.trim(), scope: 'global' },
|
||||
};
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
name: 'refresh',
|
||||
@@ -84,7 +258,7 @@ export const memoryCommand: SlashCommand = {
|
||||
);
|
||||
|
||||
try {
|
||||
const config = await context.services.config;
|
||||
const config = context.services.config;
|
||||
if (config) {
|
||||
const { memoryContent, fileCount } =
|
||||
await loadServerHierarchicalMemory(
|
||||
|
||||
Reference in New Issue
Block a user