From d3c7c90b48e3bfc8f52f7081ef4d3d913331ee95 Mon Sep 17 00:00:00 2001 From: Andrew Hundt Date: Wed, 10 Dec 2025 22:07:45 -0500 Subject: [PATCH] fix(claudeLocal): convert --continue to --resume to avoid flag conflict Claude Code 2.0.64 rejects --session-id with --continue. Instead of complex flag filtering, simply convert --continue to --resume by finding the last session (which is what --continue does anyway). This maintains happy-cli's session control while achieving the same behavior as --continue. - Added claudeFindLastSession() utility to find most recent session - claudeLocal.ts: Convert --continue to --resume before processing fix(ClaudeLocal): resolve --continue/--session-id flag conflict in Claude 2.0.64 - Remove buggy validation that checked opts.sessionId instead of startFrom - Enhance claudeFindLastSession to find most recent VALID session using claudeCheckSession - If no valid sessions exist, --continue creates a new session instead of failing Resolves critical bug where "acd --continue" failed with: "Error: --session-id cannot be used with --continue or --resume" Files modified: - src/claude/claudeLocal.ts: Remove incorrect validation - src/claude/utils/claudeFindLastSession.ts: Find most recent valid session --- src/claude/claudeLocal.ts | 14 +++++++-- src/claude/utils/claudeFindLastSession.ts | 35 +++++++++++++++++++++++ 2 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 src/claude/utils/claudeFindLastSession.ts diff --git a/src/claude/claudeLocal.ts b/src/claude/claudeLocal.ts index a0b9d861..cb005e98 100644 --- a/src/claude/claudeLocal.ts +++ b/src/claude/claudeLocal.ts @@ -5,6 +5,7 @@ import { mkdirSync, existsSync } from "node:fs"; import { randomUUID } from "node:crypto"; import { logger } from "@/ui/logger"; import { claudeCheckSession } from "./utils/claudeCheckSession"; +import { claudeFindLastSession } from "./utils/claudeFindLastSession"; import { getProjectPath } from "./utils/path"; import { projectPath } from "@/projectPath"; import { systemPrompt } from "./utils/systemPrompt"; @@ -33,10 +34,19 @@ export async function claudeLocal(opts: { // - If resuming an existing session: use --resume (Claude keeps the same session ID) // - If starting fresh: generate UUID and pass via --session-id let startFrom = opts.sessionId; - if (opts.sessionId && !claudeCheckSession(opts.sessionId, opts.path)) { - startFrom = null; + + // Convert --continue to --resume: find last session and use existing resume logic + if (!startFrom && opts.claudeArgs?.includes('--continue')) { + const lastSession = claudeFindLastSession(opts.path); + if (lastSession) { + startFrom = lastSession; + logger.debug(`[ClaudeLocal] Converting --continue to --resume ${lastSession}`); + } + // Remove --continue from claudeArgs since we're handling it + opts.claudeArgs = opts.claudeArgs?.filter(arg => arg !== '--continue'); } + // Generate new session ID if not resuming const newSessionId = startFrom ? null : randomUUID(); const effectiveSessionId = startFrom || newSessionId!; diff --git a/src/claude/utils/claudeFindLastSession.ts b/src/claude/utils/claudeFindLastSession.ts new file mode 100644 index 00000000..ffc34d7d --- /dev/null +++ b/src/claude/utils/claudeFindLastSession.ts @@ -0,0 +1,35 @@ +import { readdirSync, statSync, readFileSync } from 'node:fs'; +import { join } from 'node:path'; +import { getProjectPath } from './path'; +import { claudeCheckSession } from './claudeCheckSession'; + +/** + * Finds the most recently modified VALID session in the project directory. + * A valid session must contain at least one message with a uuid field. + * Returns the session ID (filename without .jsonl extension) or null if no valid sessions found. + */ +export function claudeFindLastSession(workingDirectory: string): string | null { + try { + const projectDir = getProjectPath(workingDirectory); + const files = readdirSync(projectDir) + .filter(f => f.endsWith('.jsonl')) + .map(f => { + const sessionId = f.replace('.jsonl', ''); + // Check if this is a valid session + if (claudeCheckSession(sessionId, workingDirectory)) { + return { + name: f, + sessionId: sessionId, + mtime: statSync(join(projectDir, f)).mtime.getTime() + }; + } + return null; + }) + .filter(f => f !== null) + .sort((a, b) => b.mtime - a.mtime); // Most recent valid session first + + return files.length > 0 ? files[0].sessionId : null; + } catch { + return null; + } +} \ No newline at end of file