/** * CliEntrypoint - Main air CLI entry * DD §17. Routes argv→command. All side effects through RuntimeApp. * * Usage: air [args...] * * Commands (DD §17 table): * run [project] Start a session (spawns TUI) * init Initialize a new project * doctor [--fix] Run diagnostics * provider Show provider config (read-only) * resume [id] Resume a session * compact [tokens] Trigger context compaction * history Show session history * session Manage sessions * restore --file|--time|--session Restore state * e2e Run validation suite * release Release readiness check * * @module packages/cli */ export { initCommand } from './commands/init.js' import { initCommand } from './commands/init.js' import { doctorCommand } from './commands/doctor.js' import { providerCommand } from './commands/provider.js' import { resumeCommand } from './commands/resume.js' import { compactCommand } from './commands/compact.js' import { historyCommand } from './commands/history.js' import { sessionCommand } from './commands/session.js' import { restoreCommand } from './commands/restore.js' import { e2eCommand } from './commands/e2e.js' import { releaseCommand } from './commands/release.js' import { askCommand } from './commands/ask.js' export async function main(argv: string[]): Promise { const args = argv.slice(2) const command = args[0] const rest = args.slice(1) switch (command) { case 'run': { const { runCommand } = await import('./commands/run.js') await runCommand(rest[0]) break } case 'ask': await askCommand(rest.join(' '), { model: rest.find(a => a.startsWith('--model='))?.split('=')[1], maxTurns: rest.find(a => a.startsWith('--turns='))?.split('=')[1] ? parseInt(rest.find(a => a.startsWith('--turns='))!.split('=')[1]) : undefined }) break case 'init': await initCommand(rest[0]) break case 'doctor': await doctorCommand({ fix: rest.includes('--fix'), bundle: rest.includes('--bundle'), scope: rest.find(a => a.startsWith('--scope='))?.split('=')[1] }) break case 'provider': providerCommand(rest[0] || 'current') break case 'resume': resumeCommand(rest[0]) break case 'compact': await compactCommand(rest[0] ? parseInt(rest[0]) : undefined) break case 'history': historyCommand() break case 'session': sessionCommand(rest[0] as 'list' | 'inspect', rest[1]) break case 'restore': restoreCommand({ file: rest.find(a => a.startsWith('--file='))?.split('=')[1], time: rest.find(a => a.startsWith('--time='))?.split('=')[1], session: rest.find(a => a.startsWith('--session='))?.split('=')[1] }) break case 'e2e': e2eCommand() break case 'release': case 'release:check': releaseCommand() break case 'help': case '--help': case '-h': default: printHelp() break } } function printHelp(): void { console.log(` AirCoding V1.0.0 Alpha Usage: air [args...] Commands: ask "" Ask the AI to implement a task run [project] Start a session (spawns TUI) init Initialize a new AirCoding project doctor [--fix] Run diagnostic checks provider list List configured providers (read-only) provider current Show current provider/model resume [id] Resume a previous session compact [tokens] Trigger context compaction history Show session history session list List active sessions session inspect Inspect a session restore --file= Restore a file from git restore --time= Restore to a point in time e2e Run end-to-end validation release Release readiness check help Show this help Environment: AIRCODING_PROJECT_ROOT Default project path AIRCODING_PROVIDER Default AI provider AIRCODING_MODEL Default AI model `) } // Run CLI if this is the main module if (process.argv[1]?.includes('air') || process.argv[1]?.includes('cli')) { main(process.argv).catch((error) => { console.error('Fatal:', error) process.exit(1) }) }