From 305433bc55235a561038690836c1a5a6f0474795 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 2 Feb 2026 15:47:59 -0500 Subject: [PATCH 1/2] feat: Add session ID support to resume command Allow users to resume specific Claude Code sessions by passing a session ID to the -r/--resume flag. This enables direct session navigation without having to resume the most recent session first. Usage: pai -r # Resume last session pai -r 285728b4-8814-40c4-b643-bb7a8e81859e # Resume specific session Changes: - Modified cmdLaunch to accept session ID as string or boolean - Updated argument parsing to detect UUID format after --resume - Enhanced help text with session ID usage examples Co-Authored-By: Claude Sonnet 4.5 --- Releases/v2.5/.claude/skills/PAI/Tools/pai.ts | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts b/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts index e14bce0d..638ecc0b 100755 --- a/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts +++ b/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts @@ -390,7 +390,7 @@ function cmdWallpaper(args: string[]) { // Commands // ============================================================================ -async function cmdLaunch(options: { mcp?: string; resume?: boolean; skipPerms?: boolean; local?: boolean }) { +async function cmdLaunch(options: { mcp?: string; resume?: boolean | string; skipPerms?: boolean; local?: boolean }) { displayBanner(); const args = ["claude"]; @@ -406,6 +406,10 @@ async function cmdLaunch(options: { mcp?: string; resume?: boolean; skipPerms?: // Use --dangerous flag explicitly if you really need to skip all permission checks. if (options.resume) { args.push("--resume"); + // If resume is a string (session ID), pass it as the next argument + if (typeof options.resume === "string") { + args.push(options.resume); + } } // Change to PAI directory unless --local flag is set @@ -572,6 +576,7 @@ USAGE: k -m Launch with specific MCP(s) k -m bd,ap Launch with multiple MCPs k -r, --resume Resume last session + k -r Resume specific session by ID k -l, --local Stay in current directory (don't cd to ~/.claude) COMMANDS: @@ -601,6 +606,7 @@ EXAMPLES: k -m bd Start with Bright Data k -m bd,ap,chrome Start with multiple MCPs k -r Resume last session + k -r 285728b4-8814-40c4-b643-bb7a8e81859e Resume specific session k mcp set research Switch to research profile k update Update Claude Code k prompt "What time is it?" One-shot prompt @@ -624,7 +630,7 @@ async function main() { // Parse arguments let mcp: string | undefined; - let resume = false; + let resume: boolean | string = false; let skipPerms = true; let local = false; let command: string | undefined; @@ -650,7 +656,14 @@ async function main() { break; case "-r": case "--resume": - resume = true; + // Check if next arg is a session ID (UUID format) + const resumeArg = args[i + 1]; + if (resumeArg && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(resumeArg)) { + resume = resumeArg; + i++; // Skip the session ID in next iteration + } else { + resume = true; + } break; case "--safe": skipPerms = false; From 8478d590c026743f68501f521593ba0c5308fbd5 Mon Sep 17 00:00:00 2001 From: Alex Date: Mon, 2 Feb 2026 16:06:03 -0500 Subject: [PATCH 2/2] feat(cli): Auto-detect session cwd for seamless resume When resuming a session by ID, the wrapper now automatically: 1. Finds the session file in ~/.claude/projects/ 2. Extracts the original working directory (cwd) 3. Changes to that directory before launching Claude This eliminates the need for users to remember which directory a session was created in or manually use the -l flag. Before: pai -r # Failed with "No conversation found" After: pai -r # Works! Auto-switches to session's original cwd Implementation: - Added extractSessionCwd() to parse session files - Modified cmdLaunch() to use extracted cwd for session ID resumes - Maintains backward compatibility with boolean resume flag Co-Authored-By: Claude Sonnet 4.5 --- Releases/v2.5/.claude/skills/PAI/Tools/pai.ts | 56 +++++++++++++++++-- 1 file changed, 52 insertions(+), 4 deletions(-) diff --git a/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts b/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts index 638ecc0b..1de65ac7 100755 --- a/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts +++ b/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts @@ -121,6 +121,40 @@ function notifyVoice(message: string) { }).catch(() => {}); // Silently ignore errors } +function extractSessionCwd(sessionId: string): string | null { + // Find session file in ~/.claude/projects/*/.jsonl + const projectsDir = join(CLAUDE_DIR, "projects"); + if (!existsSync(projectsDir)) return null; + + // Search through all project subdirectories + const subdirs = readdirSync(projectsDir); + for (const subdir of subdirs) { + const sessionFile = join(projectsDir, subdir, `${sessionId}.jsonl`); + if (existsSync(sessionFile)) { + try { + // Read the first line that contains sessionId to get the cwd + const content = readFileSync(sessionFile, "utf-8"); + const lines = content.split("\n").filter((l) => l.trim()); + + for (const line of lines) { + try { + const data = JSON.parse(line); + if (data.sessionId === sessionId && data.cwd) { + return data.cwd; + } + } catch { + continue; // Skip malformed lines + } + } + } catch { + continue; // Skip if file can't be read + } + } + } + + return null; +} + function displayBanner() { if (existsSync(BANNER_SCRIPT)) { spawnSync(["bun", BANNER_SCRIPT], { stdin: "inherit", stdout: "inherit", stderr: "inherit" }); @@ -409,11 +443,25 @@ async function cmdLaunch(options: { mcp?: string; resume?: boolean | string; ski // If resume is a string (session ID), pass it as the next argument if (typeof options.resume === "string") { args.push(options.resume); - } - } - // Change to PAI directory unless --local flag is set - if (!options.local) { + // When resuming by session ID, extract the original cwd from the session file + // and change to that directory so Claude Code can find the session + if (!options.local) { + const sessionCwd = extractSessionCwd(options.resume); + if (sessionCwd) { + log(`Resuming session from: ${sessionCwd}`, "📂"); + process.chdir(sessionCwd); + } else { + // Fallback to CLAUDE_DIR if we can't find the session + process.chdir(CLAUDE_DIR); + } + } + } else if (!options.local) { + // Regular resume (no session ID): use CLAUDE_DIR + process.chdir(CLAUDE_DIR); + } + } else if (!options.local) { + // New session: use CLAUDE_DIR process.chdir(CLAUDE_DIR); }