Skip to content

Commit a078701

Browse files
committed
feat(sessions): add claude resume command copy
1 parent 5d0d701 commit a078701

4 files changed

Lines changed: 39 additions & 4 deletions

File tree

tests/e2e/test-web-ui-session-browser.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,7 @@ async function flushScheduledFrames(vm) {
160160
}
161161

162162
async function waitForCondition(check, label) {
163-
const deadline = Date.now() + 6000;
163+
const deadline = Date.now() + 20000;
164164
while (Date.now() < deadline) {
165165
if (check()) {
166166
return;

tests/unit/session-resume-command.test.mjs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,11 @@ test('isResumeCommandAvailable supports codex and codebuddy with sessionId', ()
1515
assert.strictEqual(methods.isResumeCommandAvailable({ source: 'codebuddy', sessionId: 'abc123' }), true);
1616
assert.strictEqual(methods.isResumeCommandAvailable({ source: 'gemini', sessionId: 'gm-123' }), true);
1717
assert.strictEqual(methods.isResumeCommandAvailable({ source: 'codebuddy', sessionId: '' }), false);
18-
assert.strictEqual(methods.isResumeCommandAvailable({ source: 'claude', sessionId: 'sess-2' }), false);
18+
assert.strictEqual(methods.isResumeCommandAvailable({ source: 'claude', sessionId: 'sess-2' }), true);
19+
assert.strictEqual(methods.isResumeCommandAvailable({ source: 'claude', sessionId: '', filePath: '/home/user/.claude/projects/demo/sess-3.jsonl' }), true);
1920
});
2021

21-
test('buildResumeCommand generates codex resume with optional --yolo, codebuddy -r, and gemini -r', () => {
22+
test('buildResumeCommand generates codex resume with optional --yolo, codebuddy -r, gemini -r, and claude -r', () => {
2223
const methods = createSessionActionMethods();
2324
const contextBase = {
2425
...methods,
@@ -44,4 +45,14 @@ test('buildResumeCommand generates codex resume with optional --yolo, codebuddy
4445
methods.buildResumeCommand.call({ ...contextBase, sessionResumeWithYolo: true }, { source: 'gemini', sessionId: 'gm-123' }),
4546
'gemini -r gm-123'
4647
);
48+
49+
assert.strictEqual(
50+
methods.buildResumeCommand.call({ ...contextBase, sessionResumeWithYolo: true }, { source: 'claude', sessionId: 'sess-2' }),
51+
'claude -r sess-2'
52+
);
53+
54+
assert.strictEqual(
55+
methods.buildResumeCommand.call({ ...contextBase, sessionResumeWithYolo: true }, { source: 'claude', sessionId: '', filePath: '/home/user/.claude/projects/demo/sess-3.jsonl' }),
56+
'claude -r sess-3'
57+
);
4758
});

tests/unit/web-ui-behavior-parity.test.mjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -494,6 +494,7 @@ test('captured bundled app skeleton only exposes expected data key drift versus
494494
'hasConfigTemplateDiffChanges',
495495
'normalizeConfigTemplateDiffConfirmEnabled',
496496
'setConfigTemplateDiffConfirmEnabled',
497+
'extractClaudeResumeKeyFromFilePath',
497498
'loadPluginsOverview',
498499
'selectPlugin',
499500
'createPromptTemplate',

web-ui/modules/app.methods.session-actions.mjs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,10 @@ export function createSessionActionMethods(options = {}) {
117117
if (!session) return false;
118118
const source = String(session.source || '').trim().toLowerCase();
119119
const sessionId = typeof session.sessionId === 'string' ? session.sessionId.trim() : '';
120+
if (source === 'claude') {
121+
const filePath = typeof session.filePath === 'string' ? session.filePath.trim() : '';
122+
return !!sessionId || !!this.extractClaudeResumeKeyFromFilePath(filePath);
123+
}
120124
return (source === 'codex' || source === 'codebuddy' || source === 'gemini') && !!sessionId;
121125
},
122126

@@ -140,19 +144,38 @@ export function createSessionActionMethods(options = {}) {
140144
buildResumeCommand(session) {
141145
const source = session && session.source ? String(session.source).trim().toLowerCase() : '';
142146
const sessionId = session && session.sessionId ? String(session.sessionId).trim() : '';
143-
const arg = this.quoteResumeArg(sessionId);
147+
const filePath = session && session.filePath ? String(session.filePath).trim() : '';
148+
const resumeKey = source === 'claude'
149+
? (sessionId || this.extractClaudeResumeKeyFromFilePath(filePath))
150+
: sessionId;
151+
const arg = this.quoteResumeArg(resumeKey);
144152
if (source === 'codebuddy') {
145153
return `codebuddy -r ${arg}`;
146154
}
147155
if (source === 'gemini') {
148156
return `gemini -r ${arg}`;
149157
}
158+
if (source === 'claude') {
159+
return `claude -r ${arg}`;
160+
}
150161
if (this.sessionResumeWithYolo) {
151162
return `codex --yolo resume ${arg}`;
152163
}
153164
return `codex resume ${arg}`;
154165
},
155166

167+
extractClaudeResumeKeyFromFilePath(filePath) {
168+
const value = typeof filePath === 'string' ? filePath.trim() : '';
169+
if (!value) return '';
170+
const normalized = value.replace(/\\/g, '/');
171+
const base = normalized.split('/').pop() || '';
172+
if (!base) return '';
173+
if (base.toLowerCase().endsWith('.jsonl')) {
174+
return base.slice(0, -5);
175+
}
176+
return base;
177+
},
178+
156179
quoteShellArg(value) {
157180
const text = typeof value === 'string' ? value : String(value || '');
158181
if (!text) return "''";

0 commit comments

Comments
 (0)