import { spawn } from 'child_process'; import type { WorktreeInfo, Config } from '../types/index.js'; export interface GroveAppOptions { worktree: WorktreeInfo; config: Config; } export class GroveApp { private worktree: WorktreeInfo; private config: Config; constructor(options: GroveAppOptions) { this.worktree = options.worktree; this.config = options.config; } public async start(): Promise { const relativePath = this.worktree.path.replace(process.cwd().replace(`/${this.worktree.name}`, ''), '..'); console.log(`Grove is starting Claude in ${relativePath} with the branch ${this.worktree.branch}. Tip: When you're done with this worktree run: git worktree remove ${this.worktree.path}\n`); // Change to worktree directory process.chdir(this.worktree.path); if (this.config.autoStart.claudeCode) { // Launch Claude Code directly const claude = spawn('claude', [], { stdio: 'inherit', // Pass through stdin/stdout/stderr cwd: this.worktree.path, env: process.env }); return new Promise((resolve, reject) => { claude.on('close', (code) => { this.cleanup(); if (code === 0) { resolve(); } else { reject(new Error(`Claude Code exited with code ${code}`)); } }); claude.on('error', (error) => { this.cleanup(); reject(error); }); // Handle Ctrl+C to cleanup properly process.on('SIGINT', () => { claude.kill('SIGINT'); }); }); } else { // Just show instructions and wait for user to exit console.log(`📂 Working directory: ${this.worktree.path}`); console.log(`🔧 Run 'claude' to start Claude Code when ready`); return new Promise((resolve) => { process.on('SIGINT', () => { this.cleanup(); resolve(); }); }); } } private cleanup(): void { // Change back to original directory const originalDir = process.env['INIT_CWD'] ?? process.cwd().replace(`/${this.worktree.name}`, ''); process.chdir(originalDir); } }