Merge pull request #450 from QwenLM/feature/qwenmd

use sub-command to switch between project and global memory ops
This commit is contained in:
pomelo
2025-08-27 09:52:23 +08:00
committed by GitHub
9 changed files with 396 additions and 56 deletions

View File

@@ -117,7 +117,7 @@ describe('memoryCommand', () => {
expect(result).toEqual({ expect(result).toEqual({
type: 'message', type: 'message',
messageType: 'error', messageType: 'error',
content: 'Usage: /memory add <text to remember>', content: 'Usage: /memory add [--global|--project] <text to remember>',
}); });
expect(mockContext.ui.addItem).not.toHaveBeenCalled(); expect(mockContext.ui.addItem).not.toHaveBeenCalled();
@@ -143,6 +143,61 @@ describe('memoryCommand', () => {
toolArgs: { fact }, 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', () => { describe('/memory refresh', () => {
@@ -173,7 +228,7 @@ describe('memoryCommand', () => {
mockContext = createMockCommandContext({ mockContext = createMockCommandContext({
services: { services: {
config: Promise.resolve(mockConfig), config: mockConfig,
settings: { settings: {
merged: { merged: {
memoryDiscoveryMaxDirs: 1000, memoryDiscoveryMaxDirs: 1000,

View File

@@ -7,7 +7,11 @@
import { import {
getErrorMessage, getErrorMessage,
loadServerHierarchicalMemory, loadServerHierarchicalMemory,
QWEN_DIR,
} from '@qwen-code/qwen-code-core'; } 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 { MessageType } from '../types.js';
import { import {
CommandKind, CommandKind,
@@ -41,24 +45,136 @@ export const memoryCommand: SlashCommand = {
Date.now(), 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', 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, kind: CommandKind.BUILT_IN,
action: (context, args): SlashCommandActionReturn | void => { action: (context, args): SlashCommandActionReturn | void => {
if (!args || args.trim() === '') { if (!args || args.trim() === '') {
return { return {
type: 'message', type: 'message',
messageType: 'error', 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( context.ui.addItem(
{ {
type: MessageType.INFO, type: MessageType.INFO,
text: `Attempting to save to memory: "${args.trim()}"`, text: `Attempting to save to memory ${scopeText}: "${fact}"`,
}, },
Date.now(), Date.now(),
); );
@@ -66,9 +182,67 @@ export const memoryCommand: SlashCommand = {
return { return {
type: 'tool', type: 'tool',
toolName: 'save_memory', 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', name: 'refresh',
@@ -84,7 +258,7 @@ export const memoryCommand: SlashCommand = {
); );
try { try {
const config = await context.services.config; const config = context.services.config;
if (config) { if (config) {
const { memoryContent, fileCount } = const { memoryContent, fileCount } =
await loadServerHierarchicalMemory( await loadServerHierarchicalMemory(

View File

@@ -10,7 +10,7 @@ import * as path from 'path';
import * as fs from 'fs/promises'; import * as fs from 'fs/promises';
import * as os from 'os'; import * as os from 'os';
import type { ChildProcess } from 'node:child_process'; import type { ChildProcess } from 'node:child_process';
import { getProjectHash, GEMINI_DIR } from '../utils/paths.js'; import { getProjectHash, QWEN_DIR } from '../utils/paths.js';
const hoistedMockExec = vi.hoisted(() => vi.fn()); const hoistedMockExec = vi.hoisted(() => vi.fn());
vi.mock('node:child_process', () => ({ vi.mock('node:child_process', () => ({
@@ -157,7 +157,7 @@ describe('GitService', () => {
let gitConfigPath: string; let gitConfigPath: string;
beforeEach(() => { beforeEach(() => {
repoDir = path.join(homedir, GEMINI_DIR, 'history', hash); repoDir = path.join(homedir, QWEN_DIR, 'history', hash);
gitConfigPath = path.join(repoDir, '.gitconfig'); gitConfigPath = path.join(repoDir, '.gitconfig');
}); });

View File

@@ -10,7 +10,7 @@ import * as os from 'os';
import { isNodeError } from '../utils/errors.js'; import { isNodeError } from '../utils/errors.js';
import { exec } from 'node:child_process'; import { exec } from 'node:child_process';
import { simpleGit, SimpleGit, CheckRepoActions } from 'simple-git'; import { simpleGit, SimpleGit, CheckRepoActions } from 'simple-git';
import { getProjectHash, GEMINI_DIR } from '../utils/paths.js'; import { getProjectHash, QWEN_DIR } from '../utils/paths.js';
export class GitService { export class GitService {
private projectRoot: string; private projectRoot: string;
@@ -21,7 +21,7 @@ export class GitService {
private getHistoryDir(): string { private getHistoryDir(): string {
const hash = getProjectHash(this.projectRoot); const hash = getProjectHash(this.projectRoot);
return path.join(os.homedir(), GEMINI_DIR, 'history', hash); return path.join(os.homedir(), QWEN_DIR, 'history', hash);
} }
async initialize(): Promise<void> { async initialize(): Promise<void> {

View File

@@ -522,13 +522,16 @@ describe('MemoryTool', () => {
expect(result).not.toBe(false); expect(result).not.toBe(false);
if (result && result.type === 'edit') { if (result && result.type === 'edit') {
expect(result.title).toBe('Choose Memory Storage Location'); expect(result.title).toContain('Choose Memory Location');
expect(result.fileName).toBe('Memory Storage Options'); expect(result.title).toContain('GLOBAL');
expect(result.fileDiff).toContain('Choose where to save this memory'); expect(result.title).toContain('PROJECT');
expect(result.fileName).toBe('QWEN.md');
expect(result.fileDiff).toContain('Test fact'); expect(result.fileDiff).toContain('Test fact');
expect(result.fileDiff).toContain('Global:'); expect(result.fileDiff).toContain('--- QWEN.md');
expect(result.fileDiff).toContain('Project:'); expect(result.fileDiff).toContain('+++ QWEN.md');
expect(result.originalContent).toBe(''); expect(result.fileDiff).toContain('+- Test fact');
expect(result.originalContent).toContain('scope: global');
expect(result.originalContent).toContain('INSTRUCTIONS:');
} }
}); });
@@ -577,13 +580,16 @@ describe('MemoryTool', () => {
expect(description).toBe(`${expectedPath} (project)`); expect(description).toBe(`${expectedPath} (project)`);
}); });
it('should default to global scope when scope is not specified', () => { it('should show choice prompt when scope is not specified', () => {
const params = { fact: 'Test fact' }; const params = { fact: 'Test fact' };
const invocation = memoryTool.build(params); const invocation = memoryTool.build(params);
const description = invocation.getDescription(); const description = invocation.getDescription();
const expectedPath = path.join('~', '.qwen', 'QWEN.md'); const globalPath = path.join('~', '.qwen', 'QWEN.md');
expect(description).toBe(`${expectedPath} (global)`); const projectPath = path.join(process.cwd(), 'QWEN.md');
expect(description).toBe(
`CHOOSE: ${globalPath} (global) OR ${projectPath} (project)`,
);
}); });
}); });
}); });

View File

@@ -199,7 +199,12 @@ class MemoryToolInvocation extends BaseToolInvocation<
private static readonly allowlist: Set<string> = new Set(); private static readonly allowlist: Set<string> = new Set();
getDescription(): string { getDescription(): string {
const scope = this.params.scope || 'global'; if (!this.params.scope) {
const globalPath = tildeifyPath(getMemoryFilePath('global'));
const projectPath = tildeifyPath(getMemoryFilePath('project'));
return `CHOOSE: ${globalPath} (global) OR ${projectPath} (project)`;
}
const scope = this.params.scope;
const memoryFilePath = getMemoryFilePath(scope); const memoryFilePath = getMemoryFilePath(scope);
return `${tildeifyPath(memoryFilePath)} (${scope})`; return `${tildeifyPath(memoryFilePath)} (${scope})`;
} }
@@ -207,26 +212,54 @@ class MemoryToolInvocation extends BaseToolInvocation<
override async shouldConfirmExecute( override async shouldConfirmExecute(
_abortSignal: AbortSignal, _abortSignal: AbortSignal,
): Promise<ToolEditConfirmationDetails | false> { ): Promise<ToolEditConfirmationDetails | false> {
// If scope is not specified, prompt the user to choose // When scope is not specified, show a choice dialog defaulting to global
if (!this.params.scope) { if (!this.params.scope) {
// Show preview of what would be added to global by default
const defaultScope = 'global';
const currentContent = await readMemoryFileContent(defaultScope);
const newContent = computeNewContent(currentContent, this.params.fact);
const globalPath = tildeifyPath(getMemoryFilePath('global')); const globalPath = tildeifyPath(getMemoryFilePath('global'));
const projectPath = tildeifyPath(getMemoryFilePath('project')); const projectPath = tildeifyPath(getMemoryFilePath('project'));
const fileName = path.basename(getMemoryFilePath(defaultScope));
const choiceText = `Choose where to save this memory:
"${this.params.fact}"
Options:
- Global: ${globalPath} (shared across all projects)
- Project: ${projectPath} (current project only)
Preview of changes to be made to GLOBAL memory:
`;
const fileDiff =
choiceText +
Diff.createPatch(
fileName,
currentContent,
newContent,
'Current',
'Proposed (Global)',
DEFAULT_DIFF_OPTIONS,
);
const confirmationDetails: ToolEditConfirmationDetails = { const confirmationDetails: ToolEditConfirmationDetails = {
type: 'edit', type: 'edit',
title: `Choose Memory Storage Location`, title: `Choose Memory Location: GLOBAL (${globalPath}) or PROJECT (${projectPath})`,
fileName: 'Memory Storage Options', fileName,
filePath: '', filePath: getMemoryFilePath(defaultScope),
fileDiff: `Choose where to save this memory:\n\n"${this.params.fact}"\n\nOptions:\n- Global: ${globalPath} (shared across all projects)\n- Project: ${projectPath} (current project only)\n\nPlease specify the scope parameter: "global" or "project"`, fileDiff,
originalContent: '', originalContent: `scope: global\n\n# INSTRUCTIONS:\n# - Click "Yes" to save to GLOBAL memory: ${globalPath}\n# - Click "Modify with external editor" and change "global" to "project" to save to PROJECT memory: ${projectPath}\n\n${currentContent}`,
newContent: `Memory to save: ${this.params.fact}\n\nScope options:\n- global: ${globalPath}\n- project: ${projectPath}`, newContent: `scope: global\n\n# INSTRUCTIONS:\n# - Click "Yes" to save to GLOBAL memory: ${globalPath}\n# - Click "Modify with external editor" and change "global" to "project" to save to PROJECT memory: ${projectPath}\n\n${newContent}`,
onConfirm: async (_outcome: ToolConfirmationOutcome) => { onConfirm: async (_outcome: ToolConfirmationOutcome) => {
// This will be handled by the execution flow // Will be handled in createUpdatedParams
}, },
}; };
return confirmationDetails; return confirmationDetails;
} }
// Only check allowlist when scope is specified
const scope = this.params.scope; const scope = this.params.scope;
const memoryFilePath = getMemoryFilePath(scope); const memoryFilePath = getMemoryFilePath(scope);
const allowlistKey = `${memoryFilePath}_${scope}`; const allowlistKey = `${memoryFilePath}_${scope}`;
@@ -279,17 +312,25 @@ class MemoryToolInvocation extends BaseToolInvocation<
}; };
} }
// If scope is not specified, prompt the user to choose // If scope is not specified and user didn't modify content, return error prompting for choice
if (!this.params.scope) { if (!this.params.scope && !modified_by_user) {
const errorMessage = const globalPath = tildeifyPath(getMemoryFilePath('global'));
'Please specify where to save this memory. Use scope parameter: "global" for user-level (~/.qwen/QWEN.md) or "project" for current project (./QWEN.md).'; const projectPath = tildeifyPath(getMemoryFilePath('project'));
const errorMessage = `Please specify where to save this memory:
Global: ${globalPath} (shared across all projects)
Project: ${projectPath} (current project only)`;
return { return {
llmContent: JSON.stringify({ success: false, error: errorMessage }), llmContent: JSON.stringify({
returnDisplay: `${errorMessage}\n\nGlobal: ${tildeifyPath(getMemoryFilePath('global'))}\nProject: ${tildeifyPath(getMemoryFilePath('project'))}`, success: false,
error: 'Please specify where to save this memory',
}),
returnDisplay: errorMessage,
}; };
} }
const scope = this.params.scope; const scope = this.params.scope || 'global';
const memoryFilePath = getMemoryFilePath(scope); const memoryFilePath = getMemoryFilePath(scope);
try { try {
@@ -447,24 +488,88 @@ export class MemoryTool
getModifyContext(_abortSignal: AbortSignal): ModifyContext<SaveMemoryParams> { getModifyContext(_abortSignal: AbortSignal): ModifyContext<SaveMemoryParams> {
return { return {
getFilePath: (params: SaveMemoryParams) => getFilePath: (params: SaveMemoryParams) => {
getMemoryFilePath(params.scope || 'global'), // Determine scope from modified content or default
getCurrentContent: async (params: SaveMemoryParams): Promise<string> => let scope = params.scope || 'global';
readMemoryFileContent(params.scope || 'global'), if (params.modified_content) {
getProposedContent: async (params: SaveMemoryParams): Promise<string> => { const scopeMatch = params.modified_content.match(
/^scope:\s*(global|project)\s*\n/i,
);
if (scopeMatch) {
scope = scopeMatch[1].toLowerCase() as 'global' | 'project';
}
}
return getMemoryFilePath(scope);
},
getCurrentContent: async (params: SaveMemoryParams): Promise<string> => {
// Check if content starts with scope directive
if (params.modified_content) {
const scopeMatch = params.modified_content.match(
/^scope:\s*(global|project)\s*\n/i,
);
if (scopeMatch) {
const scope = scopeMatch[1].toLowerCase() as 'global' | 'project';
const content = await readMemoryFileContent(scope);
const globalPath = tildeifyPath(getMemoryFilePath('global'));
const projectPath = tildeifyPath(getMemoryFilePath('project'));
return `scope: ${scope}\n\n# INSTRUCTIONS:\n# - Save as "global" for GLOBAL memory: ${globalPath}\n# - Save as "project" for PROJECT memory: ${projectPath}\n\n${content}`;
}
}
const scope = params.scope || 'global'; const scope = params.scope || 'global';
const content = await readMemoryFileContent(scope);
const globalPath = tildeifyPath(getMemoryFilePath('global'));
const projectPath = tildeifyPath(getMemoryFilePath('project'));
return `scope: ${scope}\n\n# INSTRUCTIONS:\n# - Save as "global" for GLOBAL memory: ${globalPath}\n# - Save as "project" for PROJECT memory: ${projectPath}\n\n${content}`;
},
getProposedContent: async (params: SaveMemoryParams): Promise<string> => {
let scope = params.scope || 'global';
// Check if modified content has scope directive
if (params.modified_content) {
const scopeMatch = params.modified_content.match(
/^scope:\s*(global|project)\s*\n/i,
);
if (scopeMatch) {
scope = scopeMatch[1].toLowerCase() as 'global' | 'project';
}
}
const currentContent = await readMemoryFileContent(scope); const currentContent = await readMemoryFileContent(scope);
return computeNewContent(currentContent, params.fact); const newContent = computeNewContent(currentContent, params.fact);
const globalPath = tildeifyPath(getMemoryFilePath('global'));
const projectPath = tildeifyPath(getMemoryFilePath('project'));
return `scope: ${scope}\n\n# INSTRUCTIONS:\n# - Save as "global" for GLOBAL memory: ${globalPath}\n# - Save as "project" for PROJECT memory: ${projectPath}\n\n${newContent}`;
}, },
createUpdatedParams: ( createUpdatedParams: (
_oldContent: string, _oldContent: string,
modifiedProposedContent: string, modifiedProposedContent: string,
originalParams: SaveMemoryParams, originalParams: SaveMemoryParams,
): SaveMemoryParams => ({ ): SaveMemoryParams => {
// Parse user's scope choice from modified content
const scopeMatch = modifiedProposedContent.match(
/^scope:\s*(global|project)/i,
);
const scope = scopeMatch
? (scopeMatch[1].toLowerCase() as 'global' | 'project')
: 'global';
// Strip out the scope directive and instruction lines, keep only the actual memory content
const contentWithoutScope = modifiedProposedContent.replace(
/^scope:\s*(global|project)\s*\n/,
'',
);
const actualContent = contentWithoutScope
.replace(/^#[^\n]*\n/gm, '')
.replace(/^\s*\n/gm, '')
.trim();
return {
...originalParams, ...originalParams,
scope,
modified_by_user: true, modified_by_user: true,
modified_content: modifiedProposedContent, modified_content: actualContent,
}), };
},
}; };
} }
} }

View File

@@ -8,7 +8,7 @@ import path from 'node:path';
import os from 'os'; import os from 'os';
import * as crypto from 'crypto'; import * as crypto from 'crypto';
export const GEMINI_DIR = '.qwen'; export const QWEN_DIR = '.qwen';
export const GOOGLE_ACCOUNTS_FILENAME = 'google_accounts.json'; export const GOOGLE_ACCOUNTS_FILENAME = 'google_accounts.json';
const TMP_DIR_NAME = 'tmp'; const TMP_DIR_NAME = 'tmp';
const COMMANDS_DIR_NAME = 'commands'; const COMMANDS_DIR_NAME = 'commands';
@@ -181,7 +181,7 @@ export function getProjectHash(projectRoot: string): string {
*/ */
export function getProjectTempDir(projectRoot: string): string { export function getProjectTempDir(projectRoot: string): string {
const hash = getProjectHash(projectRoot); const hash = getProjectHash(projectRoot);
return path.join(os.homedir(), GEMINI_DIR, TMP_DIR_NAME, hash); return path.join(os.homedir(), QWEN_DIR, TMP_DIR_NAME, hash);
} }
/** /**
@@ -189,7 +189,7 @@ export function getProjectTempDir(projectRoot: string): string {
* @returns The path to the user's commands directory. * @returns The path to the user's commands directory.
*/ */
export function getUserCommandsDir(): string { export function getUserCommandsDir(): string {
return path.join(os.homedir(), GEMINI_DIR, COMMANDS_DIR_NAME); return path.join(os.homedir(), QWEN_DIR, COMMANDS_DIR_NAME);
} }
/** /**
@@ -198,5 +198,5 @@ export function getUserCommandsDir(): string {
* @returns The path to the project's commands directory. * @returns The path to the project's commands directory.
*/ */
export function getProjectCommandsDir(projectRoot: string): string { export function getProjectCommandsDir(projectRoot: string): string {
return path.join(projectRoot, GEMINI_DIR, COMMANDS_DIR_NAME); return path.join(projectRoot, QWEN_DIR, COMMANDS_DIR_NAME);
} }

View File

@@ -7,7 +7,7 @@
import path from 'node:path'; import path from 'node:path';
import { promises as fsp, existsSync, readFileSync } from 'node:fs'; import { promises as fsp, existsSync, readFileSync } from 'node:fs';
import * as os from 'os'; import * as os from 'os';
import { GEMINI_DIR, GOOGLE_ACCOUNTS_FILENAME } from './paths.js'; import { QWEN_DIR, GOOGLE_ACCOUNTS_FILENAME } from './paths.js';
interface UserAccounts { interface UserAccounts {
active: string | null; active: string | null;
@@ -15,7 +15,7 @@ interface UserAccounts {
} }
function getGoogleAccountsCachePath(): string { function getGoogleAccountsCachePath(): string {
return path.join(os.homedir(), GEMINI_DIR, GOOGLE_ACCOUNTS_FILENAME); return path.join(os.homedir(), QWEN_DIR, GOOGLE_ACCOUNTS_FILENAME);
} }
async function readAccounts(filePath: string): Promise<UserAccounts> { async function readAccounts(filePath: string): Promise<UserAccounts> {

View File

@@ -8,10 +8,10 @@ import * as os from 'os';
import * as fs from 'fs'; import * as fs from 'fs';
import * as path from 'path'; import * as path from 'path';
import { randomUUID } from 'crypto'; import { randomUUID } from 'crypto';
import { GEMINI_DIR } from './paths.js'; import { QWEN_DIR } from './paths.js';
const homeDir = os.homedir() ?? ''; const homeDir = os.homedir() ?? '';
const geminiDir = path.join(homeDir, GEMINI_DIR); const geminiDir = path.join(homeDir, QWEN_DIR);
const installationIdFile = path.join(geminiDir, 'installation_id'); const installationIdFile = path.join(geminiDir, 'installation_id');
function ensureGeminiDirExists() { function ensureGeminiDirExists() {