diff --git a/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts b/Releases/v2.5/.claude/skills/PAI/Tools/pai.ts index e14bce0d..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" }); @@ -390,7 +424,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,10 +440,28 @@ 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"); - } - - // Change to PAI directory unless --local flag is set - if (!options.local) { + // If resume is a string (session ID), pass it as the next argument + if (typeof options.resume === "string") { + args.push(options.resume); + + // 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); } @@ -572,6 +624,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 +654,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 +678,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 +704,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;