import { execFileSync } from 'node:child_process'; export interface SpawnSpec { file: string; args: string[]; env: NodeJS.ProcessEnv; } export interface SpawnOptions { command: 'claude' | 'bash'; /** reprise d'une session existante (P2) : `--resume `, `--fork-session` si fork. */ resume?: { claudeSessionId: string; fork?: boolean }; /** répertoires supplémentaires à relier dans une seule session (P6) : `--add-dir ` répété. */ addDirs?: string[]; } let cachedClaudeBin: string | null = null; export function resolveClaudeBin(): string { if (cachedClaudeBin) return cachedClaudeBin; try { cachedClaudeBin = execFileSync('which', ['claude'], { encoding: 'utf8' }).trim(); } catch { throw new Error( 'Claude Code CLI not found in PATH. Install it first: https://code.claude.com/docs/en/quickstart', ); } return cachedClaudeBin; } /** Module volontairement abstrait : le plan B « BYO API key / Agent SDK » se brancherait ici. */ export function buildSpawnSpec(opts: SpawnOptions): SpawnSpec { const env: NodeJS.ProcessEnv = { ...process.env, TERM: 'xterm-256color', COLORTERM: 'truecolor', }; if (opts.command === 'bash') { return { file: 'bash', args: ['--norc'], env }; } const args: string[] = []; if (opts.resume) { // `--resume` doit toujours s'exécuter dans le cwd d'origine (garanti par l'appelant — spike S1). args.push('--resume', opts.resume.claudeSessionId); if (opts.resume.fork) args.push('--fork-session'); } // Session de groupe : relie plusieurs repos/worktrees dans une seule session (P6). for (const dir of opts.addDirs ?? []) args.push('--add-dir', dir); return { file: resolveClaudeBin(), args, env }; }