Skip to content
Open
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
75 changes: 68 additions & 7 deletions Releases/v2.5/.claude/skills/PAI/Tools/pai.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,40 @@ function notifyVoice(message: string) {
}).catch(() => {}); // Silently ignore errors
}

function extractSessionCwd(sessionId: string): string | null {
// Find session file in ~/.claude/projects/*/<sessionId>.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" });
Expand Down Expand Up @@ -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"];

Expand All @@ -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);
}

Expand Down Expand Up @@ -572,6 +624,7 @@ USAGE:
k -m <mcp> Launch with specific MCP(s)
k -m bd,ap Launch with multiple MCPs
k -r, --resume Resume last session
k -r <session-id> Resume specific session by ID
k -l, --local Stay in current directory (don't cd to ~/.claude)

COMMANDS:
Expand Down Expand Up @@ -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
Expand All @@ -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;
Expand All @@ -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;
Expand Down