diff --git a/apps/cli/src/commands.test.ts b/apps/cli/src/commands.test.ts index 20d6e9b..deced6c 100644 --- a/apps/cli/src/commands.test.ts +++ b/apps/cli/src/commands.test.ts @@ -419,4 +419,19 @@ describe('inspector + export commands', () => { const out = await reg.match('/export')!.cmd.run([], makeContext({ history: [] })); expect(out.join('\n')).toMatch(/Nothing to export/); }); + + it('/compact needs a provider', async () => { + const ctx = makeContext({ + history: [{ role: 'user', content: [{ type: 'text', text: 'x' }] }], + }); + const out = await reg.match('/compact')!.cmd.run([], ctx); + expect(out.join('\n')).toMatch(/needs a provider/); + }); + + it('/compact reports nothing with empty history', async () => { + const out = await reg + .match('/compact')! + .cmd.run([], makeContext({ provider: { name: 'm', runTurn: async () => ({}) } as never })); + expect(out.join('\n')).toMatch(/Nothing to compact/); + }); }); diff --git a/apps/cli/src/commands.ts b/apps/cli/src/commands.ts index b2d6d91..b6eb0fb 100644 --- a/apps/cli/src/commands.ts +++ b/apps/cli/src/commands.ts @@ -678,6 +678,27 @@ export const ExportCommand: SlashCommand = { }, }; +export const CompactCommand: SlashCommand = { + name: '/compact', + description: 'Summarize the conversation so far to free up context.', + async run(_args, ctx) { + if (!ctx.provider) return ['(/compact needs a provider — none configured.)']; + const history = ctx.history ?? []; + if (history.length === 0) return ['Nothing to compact yet.']; + try { + const { compact } = await import('@deepcode/core'); + const result = await compact(history, { provider: ctx.provider }); + if (result.messagesRemoved === 0) { + return ['Conversation is already short enough — nothing to compact.']; + } + ctx.newHistory = result.history; + return [`✓ Compacted ${result.messagesRemoved} messages → ${result.history.length} kept.`]; + } catch (err) { + return [`(Compaction failed: ${(err as Error).message})`]; + } + }, +}; + /** Render a conversation as readable markdown (text + tool calls). */ function historyToMarkdown(history: StoredMessage[]): string { const out: string[] = ['# DeepCode conversation export', '']; @@ -732,6 +753,7 @@ export const BUILTIN_COMMANDS: SlashCommand[] = [ AgentsCommand, SkillsCommand, ExportCommand, + CompactCommand, ]; // ────────────────────────────────────────────────────────────────────────── diff --git a/docs/BEHAVIOR_PARITY.md b/docs/BEHAVIOR_PARITY.md index e7b9220..c31b0d7 100644 --- a/docs/BEHAVIOR_PARITY.md +++ b/docs/BEHAVIOR_PARITY.md @@ -26,7 +26,7 @@ Legend: `✅` matches · `🟡` matches with caveats · `🔄` deferred · `⚠ | `/add-dir` | ✓ | ✓ (records intent) | 🟡 — M3 will enforce | | `/todos` | ✓ | ✓ | ✅ — reads `/todos.json` written by TodoWrite tool | | `/plugins` | ✓ | ✓ | ✅ — lists wired plugins + contributed hook events + warnings (M5.2) | -| `/compact` | ✓ | ✓ auto-trigger | 🟡 — manual `/compact` slash command not exposed yet (auto works via agent loop) | +| `/compact` | ✓ | ✓ | ✅ — manual `/compact` + automatic threshold trigger in the agent loop | | `/btw` | ✓ | ✗ | 🔄 | | `/recap` | ✓ | ✗ | 🔄 | | `/rewind` | ✓ | ✓ | ✅ — 5 ops (code/conversation/both/summarize-from/up-to); `Esc Esc` bound |