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
22 changes: 19 additions & 3 deletions apps/cli/src/commands.ts
Original file line number Diff line number Diff line change
Expand Up @@ -415,9 +415,25 @@ export const AddDirCommand: SlashCommand = {

export const ResumeCommand: SlashCommand = {
name: '/resume',
description: 'List recent sessions.',
async run(_args, ctx) {
description: 'List recent sessions, or `/resume <id|number>` to switch live.',
async run(args, ctx) {
const sessions = await ctx.sessions.list();
if (args[0]) {
// Accept a full id, or a 1-based index into the recent list below.
let id = args[0].trim();
const n = Number(id);
if (Number.isInteger(n) && n >= 1 && n <= sessions.length) id = sessions[n - 1]!.id;
const loaded = await ctx.sessions.load(id);
if (!loaded) return [`Session ${id} not found. Run /resume to list recent sessions.`];
// Swap the live conversation (REPL applies newHistory) + the append target.
ctx.sessionId = id;
ctx.newHistory = loaded.messages;
const c = loaded.messages.length;
return [
`↻ Switched to session ${id} (${c} message${c === 1 ? '' : 's'}).`,
'New messages now append to this session.',
];
}
if (sessions.length === 0) return ['No previous sessions.'];
const top = sessions.slice(0, 10);
return [
Expand All @@ -426,7 +442,7 @@ export const ResumeCommand: SlashCommand = {
(s, i) => ` ${String(i + 1).padStart(2)}. ${s.id} ${s.title ?? s.cwd} (${s.updatedAt})`,
),
'',
'To resume: deepcode --resume <id> (M2 picker in next iteration).',
'Switch live with `/resume <id-or-number>`, or `deepcode --resume <id>` at launch.',
];
},
};
Expand Down
26 changes: 26 additions & 0 deletions apps/cli/src/parity-commands.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,3 +182,29 @@ describe('/config set', () => {
expect(out.join('\n')).toMatch(/Usage: \/config set/);
});
});

describe('/resume <id> (live switch)', () => {
it('switches the live session: sets sessionId + newHistory', async () => {
const sm = new SessionManager({ root: await tmpHome() });
const s = await sm.create('/proj', { title: 'old chat' });
await sm.append(s.id, { role: 'user', content: [{ type: 'text', text: 'hi' }] });
const c = ctx({ sessions: sm, sessionId: 'current-session' });
const out = await reg.match('/resume')!.cmd.run([s.id], c);
expect(out.join('\n')).toMatch(/Switched to session/);
expect(c.sessionId).toBe(s.id);
expect(c.newHistory).toHaveLength(1);
});

it('errors on an unknown id', async () => {
const sm = new SessionManager({ root: await tmpHome() });
const out = await reg.match('/resume')!.cmd.run(['nope-xyz'], ctx({ sessions: sm }));
expect(out.join('\n')).toMatch(/not found/i);
});

it('lists sessions with no args', async () => {
const sm = new SessionManager({ root: await tmpHome() });
await sm.create('/proj', { title: 'a' });
const out = await reg.match('/resume')!.cmd.run([], ctx({ sessions: sm }));
expect(out.join('\n')).toMatch(/Recent sessions/);
});
});
4 changes: 3 additions & 1 deletion apps/cli/src/repl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -609,7 +609,9 @@ export async function startRepl(opts: ReplOpts): Promise<number> {
temperature,
maxTurns: opts.maxTurns,
cwd: ctx.cwd,
session: { manager: sessions, id: session.id },
// ctx.sessionId (not the launch `session.id`) so a live `/resume <id>`
// switch redirects new messages to the resumed session.
session: { manager: sessions, id: ctx.sessionId },
mode: ctx.mode as Mode,
permissions: settings.permissions,
hooks,
Expand Down
2 changes: 1 addition & 1 deletion docs/BEHAVIOR_PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ Legend: `βœ…` matches Β· `🟑` matches with caveats Β· `πŸ”„` deferred Β· `⚠
| `/cost` / `/usage` | βœ“ | βœ“ | βœ… |
| `/context` | βœ“ | βœ“ | βœ… |
| `/config` | βœ“ | βœ“ | 🟑 β€” dumps merged settings + `/config set <key> <value>` (dotted keys, JSON values) writes user settings; no full arrow-key editor |
| `/resume` | βœ“ | βœ“ (list only) | 🟑 β€” Claude Code has fuzzy picker; ours lists; pick via `--resume <id>` |
| `/resume` | βœ“ | βœ“ | βœ… β€” lists recent sessions; `/resume <id\|number>` switches the live session in-REPL; `--resume <id>` / `-r` at launch |
| `/init` | βœ“ | βœ“ | βœ… β€” interactive 3-phase REPL flow (scan β†’ draft β†’ approve-write `AGENTS.md`) |
| `/mcp` | βœ“ | βœ“ | βœ… |
| `/add-dir` | βœ“ | βœ“ (records intent) | 🟑 β€” M3 will enforce |
Expand Down
Loading