mirror of
https://github.com/QwenLM/qwen-code.git
synced 2026-05-05 07:10:55 +00:00
347 lines
10 KiB
TypeScript
347 lines
10 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import {
|
|
getErrorMessage,
|
|
getCurrentGeminiMdFilename,
|
|
loadServerHierarchicalMemory,
|
|
QWEN_DIR,
|
|
} from '@qwen-code/qwen-code-core';
|
|
import path from 'node:path';
|
|
import os from 'node:os';
|
|
import fs from 'node:fs/promises';
|
|
import { MessageType } from '../types.js';
|
|
import type { SlashCommand, SlashCommandActionReturn } from './types.js';
|
|
import { CommandKind } from './types.js';
|
|
import { t } from '../../i18n/index.js';
|
|
|
|
export const memoryCommand: SlashCommand = {
|
|
name: 'memory',
|
|
get description() {
|
|
return t('Commands for interacting with memory.');
|
|
},
|
|
kind: CommandKind.BUILT_IN,
|
|
subCommands: [
|
|
{
|
|
name: 'show',
|
|
get description() {
|
|
return t('Show the current memory contents.');
|
|
},
|
|
kind: CommandKind.BUILT_IN,
|
|
action: async (context) => {
|
|
const memoryContent = context.services.config?.getUserMemory() || '';
|
|
const fileCount = context.services.config?.getGeminiMdFileCount() || 0;
|
|
|
|
const messageContent =
|
|
memoryContent.length > 0
|
|
? `${t('Current memory content from {{count}} file(s):', { count: String(fileCount) })}\n\n---\n${memoryContent}\n---`
|
|
: t('Memory is currently empty.');
|
|
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: messageContent,
|
|
},
|
|
Date.now(),
|
|
);
|
|
},
|
|
subCommands: [
|
|
{
|
|
name: '--project',
|
|
get description() {
|
|
return t('Show project-level memory contents.');
|
|
},
|
|
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.');
|
|
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: messageContent,
|
|
},
|
|
Date.now(),
|
|
);
|
|
} catch (_error) {
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: t(
|
|
'Project memory file not found or is currently empty.',
|
|
),
|
|
},
|
|
Date.now(),
|
|
);
|
|
}
|
|
},
|
|
},
|
|
{
|
|
name: '--global',
|
|
get description() {
|
|
return t('Show global memory contents.');
|
|
},
|
|
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.');
|
|
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: messageContent,
|
|
},
|
|
Date.now(),
|
|
);
|
|
} catch (_error) {
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: t(
|
|
'Global memory file not found or is currently empty.',
|
|
),
|
|
},
|
|
Date.now(),
|
|
);
|
|
}
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'add',
|
|
get description() {
|
|
return t(
|
|
'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: t(
|
|
'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: t(
|
|
'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: t(
|
|
'Usage: /memory add [--global|--project] <text to remember>',
|
|
),
|
|
};
|
|
}
|
|
|
|
const scopeText = scope ? `(${scope})` : '';
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: t('Attempting to save to memory {{scope}}: "{{fact}}"', {
|
|
scope: scopeText,
|
|
fact,
|
|
}),
|
|
},
|
|
Date.now(),
|
|
);
|
|
|
|
return {
|
|
type: 'tool',
|
|
toolName: 'save_memory',
|
|
toolArgs: scope ? { fact, scope } : { fact },
|
|
};
|
|
},
|
|
subCommands: [
|
|
{
|
|
name: '--project',
|
|
get description() {
|
|
return t('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: t('Usage: /memory add --project <text to remember>'),
|
|
};
|
|
}
|
|
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: t('Attempting to save to project memory: "{{text}}"', {
|
|
text: args.trim(),
|
|
}),
|
|
},
|
|
Date.now(),
|
|
);
|
|
|
|
return {
|
|
type: 'tool',
|
|
toolName: 'save_memory',
|
|
toolArgs: { fact: args.trim(), scope: 'project' },
|
|
};
|
|
},
|
|
},
|
|
{
|
|
name: '--global',
|
|
get description() {
|
|
return t('Add content to global memory.');
|
|
},
|
|
kind: CommandKind.BUILT_IN,
|
|
action: (context, args): SlashCommandActionReturn | void => {
|
|
if (!args || args.trim() === '') {
|
|
return {
|
|
type: 'message',
|
|
messageType: 'error',
|
|
content: t('Usage: /memory add --global <text to remember>'),
|
|
};
|
|
}
|
|
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: t('Attempting to save to global memory: "{{text}}"', {
|
|
text: args.trim(),
|
|
}),
|
|
},
|
|
Date.now(),
|
|
);
|
|
|
|
return {
|
|
type: 'tool',
|
|
toolName: 'save_memory',
|
|
toolArgs: { fact: args.trim(), scope: 'global' },
|
|
};
|
|
},
|
|
},
|
|
],
|
|
},
|
|
{
|
|
name: 'refresh',
|
|
get description() {
|
|
return t('Refresh the memory from the source.');
|
|
},
|
|
kind: CommandKind.BUILT_IN,
|
|
action: async (context) => {
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: t('Refreshing memory from source files...'),
|
|
},
|
|
Date.now(),
|
|
);
|
|
|
|
try {
|
|
const config = context.services.config;
|
|
if (config) {
|
|
const { memoryContent, fileCount } =
|
|
await loadServerHierarchicalMemory(
|
|
config.getWorkingDir(),
|
|
config.shouldLoadMemoryFromIncludeDirectories()
|
|
? config.getWorkspaceContext().getDirectories()
|
|
: [],
|
|
config.getFileService(),
|
|
config.getExtensionContextFilePaths(),
|
|
config.getFolderTrust(),
|
|
context.services.settings.merged.context?.importFormat ||
|
|
'tree', // Use setting or default to 'tree'
|
|
);
|
|
config.setUserMemory(memoryContent);
|
|
config.setGeminiMdFileCount(fileCount);
|
|
|
|
const successMessage =
|
|
memoryContent.length > 0
|
|
? `Memory refreshed successfully. Loaded ${memoryContent.length} characters from ${fileCount} file(s).`
|
|
: 'Memory refreshed successfully. No memory content found.';
|
|
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.INFO,
|
|
text: successMessage,
|
|
},
|
|
Date.now(),
|
|
);
|
|
}
|
|
} catch (error) {
|
|
const errorMessage = getErrorMessage(error);
|
|
context.ui.addItem(
|
|
{
|
|
type: MessageType.ERROR,
|
|
text: `Error refreshing memory: ${errorMessage}`,
|
|
},
|
|
Date.now(),
|
|
);
|
|
}
|
|
},
|
|
},
|
|
],
|
|
};
|