diff --git a/packages/cli/src/ui/commands/dreamCommand.test.ts b/packages/cli/src/ui/commands/dreamCommand.test.ts index c04c0adcfde..3bd91baa094 100644 --- a/packages/cli/src/ui/commands/dreamCommand.test.ts +++ b/packages/cli/src/ui/commands/dreamCommand.test.ts @@ -11,21 +11,7 @@ import { dreamCommand } from './dreamCommand.js'; import { createMockCommandContext } from '../../test-utils/mockCommandContext.js'; describe('dreamCommand', () => { - it('declares acp in supportedModes', () => { - expect(dreamCommand.supportedModes).toEqual(['interactive', 'acp']); - }); - - it('returns error when config is not loaded', async () => { - const context = createMockCommandContext({ services: { config: null } }); - const result = await dreamCommand.action?.(context, ''); - expect(result).toEqual({ - type: 'message', - messageType: 'error', - content: expect.stringContaining('Config'), - }); - }); - - it('submits a consolidation prompt in interactive mode without eager metadata write', async () => { + it('submits a consolidation prompt with the project-scoped transcript directory', async () => { const projectRoot = path.join('tmp', 'dream-project'); const buildConsolidationPrompt = vi.fn().mockReturnValue('dream prompt'); const writeDreamManualRun = vi.fn(); @@ -57,29 +43,8 @@ describe('dreamCommand', () => { expect.any(String), expectedTranscriptDir, ); - // In interactive mode, writeDreamManualRun is deferred to onComplete - expect(writeDreamManualRun).not.toHaveBeenCalled(); - }); - - it('calls writeDreamManualRun eagerly in ACP mode', async () => { - const projectRoot = path.join('tmp', 'dream-project'); - const buildConsolidationPrompt = vi.fn().mockReturnValue('dream prompt'); - const writeDreamManualRun = vi.fn(); - const context = createMockCommandContext({ - executionMode: 'acp', - services: { - config: { - getProjectRoot: vi.fn().mockReturnValue(projectRoot), - getMemoryManager: vi.fn().mockReturnValue({ - buildConsolidationPrompt, - writeDreamManualRun, - }), - getSessionId: vi.fn().mockReturnValue('session-1'), - }, - }, - }); - - await dreamCommand.action?.(context, ''); - expect(writeDreamManualRun).toHaveBeenCalledWith(projectRoot, 'session-1'); + expect(expectedTranscriptDir).not.toContain( + `${path.sep}.qwen${path.sep}tmp${path.sep}`, + ); }); }); diff --git a/packages/cli/src/ui/commands/dreamCommand.ts b/packages/cli/src/ui/commands/dreamCommand.ts index 9aac3314818..fa7c520f0b8 100644 --- a/packages/cli/src/ui/commands/dreamCommand.ts +++ b/packages/cli/src/ui/commands/dreamCommand.ts @@ -16,7 +16,6 @@ export const dreamCommand: SlashCommand = { return t('Consolidate managed auto-memory topic files.'); }, kind: CommandKind.BUILT_IN, - supportedModes: ['interactive', 'acp'] as const, action: async (context) => { const config = context.services.config; if (!config) { @@ -27,45 +26,25 @@ export const dreamCommand: SlashCommand = { }; } - try { - const projectRoot = config.getProjectRoot(); - const memoryRoot = getAutoMemoryRoot(projectRoot); - const transcriptDir = path.join( - new Storage(projectRoot).getProjectDir(), - 'chats', - ); + const projectRoot = config.getProjectRoot(); + const memoryRoot = getAutoMemoryRoot(projectRoot); + const transcriptDir = path.join( + new Storage(projectRoot).getProjectDir(), + 'chats', + ); - const prompt = config - .getMemoryManager() - .buildConsolidationPrompt(memoryRoot, transcriptDir); + const prompt = config + .getMemoryManager() + .buildConsolidationPrompt(memoryRoot, transcriptDir); - const recordDream = async () => - config + return { + type: 'submit_prompt', + content: prompt, + onComplete: async () => { + await config .getMemoryManager() .writeDreamManualRun(projectRoot, config.getSessionId()); - - // In ACP mode, onComplete is never invoked — record eagerly. - if (context.executionMode === 'acp') { - try { - await recordDream(); - } catch { - // Best-effort: dream dedup recording must not block prompt submission. - } - } - - return { - type: 'submit_prompt', - content: prompt, - onComplete: recordDream, - }; - } catch (error) { - return { - type: 'message', - messageType: 'error', - content: t('Failed to process /dream: {{message}}', { - message: error instanceof Error ? error.message : String(error), - }), - }; - } + }, + }; }, }; diff --git a/packages/cli/src/ui/commands/forgetCommand.test.ts b/packages/cli/src/ui/commands/forgetCommand.test.ts deleted file mode 100644 index 6885af1b95d..00000000000 --- a/packages/cli/src/ui/commands/forgetCommand.test.ts +++ /dev/null @@ -1,100 +0,0 @@ -/** - * @license - * Copyright 2026 Qwen Team - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, expect, it, vi } from 'vitest'; -import { forgetCommand } from './forgetCommand.js'; -import { createMockCommandContext } from '../../test-utils/mockCommandContext.js'; - -describe('forgetCommand', () => { - it('returns error when no argument is given', async () => { - const context = createMockCommandContext(); - const result = await forgetCommand.action?.(context, ''); - expect(result).toEqual({ - type: 'message', - messageType: 'error', - content: expect.stringContaining('/forget'), - }); - }); - - it('returns error when config is not loaded', async () => { - const context = createMockCommandContext({ services: { config: null } }); - const result = await forgetCommand.action?.(context, 'something'); - expect(result).toEqual({ - type: 'message', - messageType: 'error', - content: expect.stringContaining('Config'), - }); - }); - - it('returns info message on successful forget', async () => { - const context = createMockCommandContext({ - services: { - config: { - getProjectRoot: vi.fn().mockReturnValue('/tmp/test-project'), - getMemoryManager: vi.fn().mockReturnValue({ - selectForgetCandidates: vi - .fn() - .mockResolvedValue({ matches: [{ id: '1' }] }), - forgetMatches: vi - .fn() - .mockResolvedValue({ systemMessage: 'Forgot 1 entry.' }), - }), - }, - }, - }); - const result = await forgetCommand.action?.(context, 'old preference'); - expect(result).toEqual({ - type: 'message', - messageType: 'info', - content: 'Forgot 1 entry.', - }); - }); - - it('returns fallback message when no entries match', async () => { - const context = createMockCommandContext({ - services: { - config: { - getProjectRoot: vi.fn().mockReturnValue('/tmp/test-project'), - getMemoryManager: vi.fn().mockReturnValue({ - selectForgetCandidates: vi.fn().mockResolvedValue({ matches: [] }), - forgetMatches: vi.fn().mockResolvedValue({ systemMessage: null }), - }), - }, - }, - }); - const result = await forgetCommand.action?.(context, 'nonexistent'); - expect(result).toEqual({ - type: 'message', - messageType: 'info', - content: expect.stringContaining('nonexistent'), - }); - }); - - it('returns error message when memory manager throws', async () => { - const context = createMockCommandContext({ - services: { - config: { - getProjectRoot: vi.fn().mockReturnValue('/tmp/test-project'), - getMemoryManager: vi.fn().mockReturnValue({ - selectForgetCandidates: vi - .fn() - .mockRejectedValue(new Error('EACCES: permission denied')), - }), - }, - }, - }); - const result = await forgetCommand.action?.(context, 'something'); - expect(result).toEqual({ - type: 'message', - messageType: 'error', - content: expect.stringContaining('EACCES: permission denied'), - }); - }); - - it('declares acp in supportedModes', () => { - expect(forgetCommand.supportedModes).toEqual(['interactive', 'acp']); - }); -}); diff --git a/packages/cli/src/ui/commands/forgetCommand.ts b/packages/cli/src/ui/commands/forgetCommand.ts index e827b9c635f..185d7abcf7b 100644 --- a/packages/cli/src/ui/commands/forgetCommand.ts +++ b/packages/cli/src/ui/commands/forgetCommand.ts @@ -14,7 +14,6 @@ export const forgetCommand: SlashCommand = { return t('Remove matching entries from managed auto-memory.'); }, kind: CommandKind.BUILT_IN, - supportedModes: ['interactive', 'acp'] as const, action: async (context, args) => { const query = args.trim(); @@ -35,29 +34,19 @@ export const forgetCommand: SlashCommand = { }; } - try { - const selection = await config - .getMemoryManager() - .selectForgetCandidates(config.getProjectRoot(), query, { config }); + const selection = await config + .getMemoryManager() + .selectForgetCandidates(config.getProjectRoot(), query, { config }); - const result = await config - .getMemoryManager() - .forgetMatches(config.getProjectRoot(), selection.matches); - return { - type: 'message', - messageType: 'info', - content: - result.systemMessage ?? - t('No managed auto-memory entries matched: {{query}}', { query }), - }; - } catch (error) { - return { - type: 'message', - messageType: 'error', - content: t('Failed to process /forget: {{message}}', { - message: error instanceof Error ? error.message : String(error), - }), - }; - } + const result = await config + .getMemoryManager() + .forgetMatches(config.getProjectRoot(), selection.matches); + return { + type: 'message', + messageType: 'info', + content: + result.systemMessage ?? + t('No managed auto-memory entries matched: {{query}}', { query }), + }; }, }; diff --git a/packages/cli/src/ui/commands/rememberCommand.test.ts b/packages/cli/src/ui/commands/rememberCommand.test.ts deleted file mode 100644 index b3d95c8669a..00000000000 --- a/packages/cli/src/ui/commands/rememberCommand.test.ts +++ /dev/null @@ -1,67 +0,0 @@ -/** - * @license - * Copyright 2026 Qwen Team - * SPDX-License-Identifier: Apache-2.0 - */ - -import { describe, expect, it, vi } from 'vitest'; -import { rememberCommand } from './rememberCommand.js'; -import { createMockCommandContext } from '../../test-utils/mockCommandContext.js'; - -describe('rememberCommand', () => { - it('returns error when no argument is given', () => { - const context = createMockCommandContext(); - const result = rememberCommand.action?.(context, ''); - expect(result).toEqual({ - type: 'message', - messageType: 'error', - content: expect.stringContaining('/remember'), - }); - }); - - it('returns error when config is not loaded', () => { - const context = createMockCommandContext({ services: { config: null } }); - const result = rememberCommand.action?.(context, 'something'); - expect(result).toEqual({ - type: 'message', - messageType: 'error', - content: expect.stringContaining('Config'), - }); - }); - - it('returns submit_prompt for managed auto-memory', () => { - const context = createMockCommandContext({ - services: { - config: { - getManagedAutoMemoryEnabled: vi.fn().mockReturnValue(true), - getProjectRoot: vi.fn().mockReturnValue('/tmp/test-project'), - }, - }, - }); - const result = rememberCommand.action?.(context, 'user prefers dark mode'); - expect(result).toMatchObject({ - type: 'submit_prompt', - content: expect.stringContaining('user prefers dark mode'), - }); - }); - - it('returns submit_prompt for non-managed memory (QWEN.md fallback)', () => { - const context = createMockCommandContext({ - services: { - config: { - getManagedAutoMemoryEnabled: vi.fn().mockReturnValue(false), - getProjectRoot: vi.fn().mockReturnValue('/tmp/test-project'), - }, - }, - }); - const result = rememberCommand.action?.(context, 'some fact'); - expect(result).toMatchObject({ - type: 'submit_prompt', - content: expect.stringContaining('some fact'), - }); - }); - - it('declares acp in supportedModes', () => { - expect(rememberCommand.supportedModes).toEqual(['interactive', 'acp']); - }); -}); diff --git a/packages/cli/src/ui/commands/rememberCommand.ts b/packages/cli/src/ui/commands/rememberCommand.ts index 2c9b21bf5ea..b727671fbf8 100644 --- a/packages/cli/src/ui/commands/rememberCommand.ts +++ b/packages/cli/src/ui/commands/rememberCommand.ts @@ -19,7 +19,6 @@ export const rememberCommand: SlashCommand = { return t('Save a durable memory to the memory system.'); }, kind: CommandKind.BUILT_IN, - supportedModes: ['interactive', 'acp'] as const, action: (context: CommandContext, args): SlashCommandActionReturn | void => { const fact = args.trim(); if (!fact) { @@ -31,17 +30,17 @@ export const rememberCommand: SlashCommand = { } const config = context.services.config; - if (!config) { - return { - type: 'message', - messageType: 'error', - content: t('Config not loaded.'), - }; - } + const useManagedMemory = config?.getManagedAutoMemoryEnabled() ?? false; - if (config.getManagedAutoMemoryEnabled()) { - const memoryDir = getAutoMemoryRoot(config.getProjectRoot()); - const dirHint = ` Save it to \`${memoryDir}\`.`; + if (useManagedMemory) { + // In managed auto-memory mode the save_memory tool is not registered. + // Submit a prompt so the main agent writes the per-entry file directly, + // choosing the appropriate type (user / feedback / project / reference) + // based on the content, following the instructions in buildManagedAutoMemoryPrompt. + const memoryDir = config + ? getAutoMemoryRoot(config.getProjectRoot()) + : undefined; + const dirHint = memoryDir ? ` Save it to \`${memoryDir}\`.` : ''; return { type: 'submit_prompt', content: `Please save the following to your memory system.${dirHint} Choose the most appropriate memory type (user, feedback, project, or reference) based on the content:\n\n${fact}`,