import * as pty from 'node-pty' import { EventEmitter } from 'events' export class PTYManager extends EventEmitter { private ptyProcess: pty.IPty | null = null private resolvedShell: string constructor() { super() this.resolvedShell = this.resolveShell('claude') } start(_shell: string = 'claude', args: string[] = []) { this.ptyProcess = pty.spawn(this.resolvedShell, args, { name: 'xterm-color', cols: 80, rows: 24, cwd: process.env.HOME, env: process.env as { [key: string]: string }, }) this.ptyProcess.onData((data: string) => { this.emit('data', data) }) this.ptyProcess.onExit(({ exitCode }) => { this.emit('exit', exitCode) }) return this.ptyProcess } write(data: string) { if (this.ptyProcess) { this.ptyProcess.write(data) } } resize(cols: number, rows: number) { if (this.ptyProcess) { this.ptyProcess.resize(cols, rows) } } kill() { if (this.ptyProcess) { this.ptyProcess.kill() this.ptyProcess = null } } private resolveShell(shell: string): string { if (shell === 'claude') { const { execSync } = require('child_process') try { return execSync('which claude', { encoding: 'utf8' }).trim() } catch { throw new Error( 'Claude CLI not found. Please install it: https://claude.ai/download' ) } } return shell } }