import { execa, ExecaError } from 'execa'; import { ExecResult } from '../models/types'; export class ShellUtils { async exec(command: string, options?: { cwd?: string; timeout?: number }): Promise { try { const result = await execa(command, { shell: '/bin/bash', cwd: options?.cwd, timeout: options?.timeout || 30000, reject: false, }); return { success: result.exitCode === 0, stdout: result.stdout || '', stderr: result.stderr || '', exitCode: result.exitCode || 0, }; } catch (error) { const execaError = error as ExecaError; return { success: false, stdout: execaError.stdout || '', stderr: execaError.stderr || '', exitCode: execaError.exitCode || 1, }; } } async execWithOutput(command: string, options?: { cwd?: string; timeout?: number }): Promise { const result = await this.exec(command, options); if (!result.success) { throw new Error(`Command failed: ${command}\n${result.stderr}`); } return result.stdout; } async execSilent(command: string, options?: { cwd?: string; timeout?: number }): Promise { const result = await this.exec(command, options); return result.success; } async getExitCode(command: string, options?: { cwd?: string }): Promise { const result = await this.exec(command, options); return result.exitCode; } isCommandAvailable(command: string): Promise { return this.execSilent(`which ${command}`).catch(() => false); } async getTmuxVersion(): Promise { const result = await this.exec('tmux -V'); if (result.success) { return result.stdout; } return null; } async getScreenVersion(): Promise { const result = await this.exec('screen -v'); if (result.success) { return result.stdout; } return null; } } export const shellUtils = new ShellUtils();