Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 4 additions & 39 deletions packages/cli/src/ui/commands/dreamCommand.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down Expand Up @@ -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}`,
);
});
});
53 changes: 16 additions & 37 deletions packages/cli/src/ui/commands/dreamCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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),
}),
};
}
},
};
},
};
100 changes: 0 additions & 100 deletions packages/cli/src/ui/commands/forgetCommand.test.ts

This file was deleted.

37 changes: 13 additions & 24 deletions packages/cli/src/ui/commands/forgetCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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();

Expand All @@ -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 }),
};
},
};
67 changes: 0 additions & 67 deletions packages/cli/src/ui/commands/rememberCommand.test.ts

This file was deleted.

21 changes: 10 additions & 11 deletions packages/cli/src/ui/commands/rememberCommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand All @@ -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) {
Comment on lines 32 to +35
// 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}`,
Expand Down
Loading