#!/usr/bin/env bun import * as p from "@clack/prompts"; import { getCommandDef, getVersion, parseArgs, printCommandHelp, printUsage } from "./cli-core"; import { CONSOLE_LOG, PROCESS_EXIT } from "./cli-io"; import { runApply } from "./commands/apply"; import { runConfig } from "./commands/config"; import { runDoctor } from "./commands/doctor"; import { runFix, runFixForeground } from "./commands/fix"; import { runInit } from "./commands/init"; import { runList } from "./commands/list"; import { runLog } from "./commands/log"; import { runPrune } from "./commands/prune"; import { runForeground, startReview } from "./commands/run"; import { runStatus } from "./commands/status"; import { runStop } from "./commands/stop"; import { runUpdate } from "./commands/update"; import { CliError, type CommandDef, parseCommand } from "./lib/cli-parser"; export { COMMANDS, getCommandDef, getVersion, type ParsedArgs, parseArgs, printCommandHelp, printUsage, } from "./cli-core"; export { type RrrDeps, runRrr, runRrrEntrypoint } from "./cli-rrr"; export interface CliDeps { parseArgs: typeof parseArgs; getVersion: typeof getVersion; printUsage: typeof printUsage; printCommandHelp: typeof printCommandHelp; getCommandDef: (name: string) => CommandDef | undefined; parseCommand: typeof parseCommand; runInit: typeof runInit; runConfig: typeof runConfig; startReview: typeof startReview; runFix: typeof runFix; runFixForeground: typeof runFixForeground; runApply: typeof runApply; runForeground: typeof runForeground; runStatus: typeof runStatus; runStop: typeof runStop; runLog: typeof runLog; runPrune: typeof runPrune; runDoctor: typeof runDoctor; runList: typeof runList; runUpdate: typeof runUpdate; isInteractiveTerminal: () => boolean; log: (message: string) => void; logError: (message: string) => void; logMessage: (message: string) => void; exit: (code: number) => void; } const CLACK_ERROR = p.log.error.bind(p.log) as (message: string) => void; const CLACK_MESSAGE = p.log.message.bind(p.log) as (message: string) => void; const IS_INTERACTIVE_TERMINAL = (): boolean => process.stdin.isTTY === true && process.stdout.isTTY === true; const DEFAULT_CLI_DEPS: CliDeps = { parseArgs, getVersion, printUsage, printCommandHelp, getCommandDef, parseCommand, runInit, runConfig, startReview, runFix, runFixForeground, runApply, runForeground, runStatus, runStop, runLog, runPrune, runDoctor, runList, runUpdate, isInteractiveTerminal: IS_INTERACTIVE_TERMINAL, log: CONSOLE_LOG, logError: CLACK_ERROR, logMessage: CLACK_MESSAGE, exit: PROCESS_EXIT, }; function buildCliDeps(overrides: Partial): CliDeps { return { ...DEFAULT_CLI_DEPS, ...overrides }; } function reportCliError(cliDeps: CliDeps, error: unknown): void { cliDeps.logError(`Error: ${error}`); cliDeps.exit(1); } export async function runCli( args: string[] = process.argv.slice(2), deps: Partial = {} ): Promise { const cliDeps = buildCliDeps(deps); const { command, args: commandArgs, showHelp, showVersion } = cliDeps.parseArgs(args); if (showVersion) { cliDeps.log(`ralph-review v${cliDeps.getVersion()}`); return; } if (showHelp && !command) { cliDeps.log(cliDeps.printUsage()); return; } if (!command) { if (commandArgs.length > 0 || !cliDeps.isInteractiveTerminal()) { cliDeps.log(cliDeps.printUsage()); return; } try { await cliDeps.runStatus(); } catch (error) { reportCliError(cliDeps, error); } return; } if (showHelp) { const commandHelp = cliDeps.printCommandHelp(command); if (commandHelp) { cliDeps.log(commandHelp); } else { cliDeps.log(cliDeps.printUsage()); } return; } const resolvedCommand = cliDeps.getCommandDef(command)?.name ?? command; const commandDef = cliDeps.getCommandDef(resolvedCommand); if (commandDef && resolvedCommand !== "config") { try { cliDeps.parseCommand(commandDef, commandArgs); } catch (error) { if (error instanceof CliError) { const [firstLine, ...rest] = error.message.split("\n"); if (firstLine) cliDeps.logError(firstLine); for (const line of rest) { if (line) cliDeps.logMessage(line); } } else { cliDeps.logError(`${error}`); } cliDeps.exit(1); return; } } try { switch (resolvedCommand) { case "init": await cliDeps.runInit(commandArgs); break; case "config": await cliDeps.runConfig(commandArgs); break; case "run": await cliDeps.startReview(commandArgs); break; case "fix": await cliDeps.runFix(commandArgs); break; case "_fix-foreground": await cliDeps.runFixForeground(commandArgs); break; case "apply": await cliDeps.runApply(commandArgs); break; case "_run-foreground": await cliDeps.runForeground(commandArgs); break; case "status": await cliDeps.runStatus(); break; case "stop": await cliDeps.runStop(commandArgs); break; case "log": await cliDeps.runLog(commandArgs); break; case "prune": await cliDeps.runPrune(commandArgs); break; case "doctor": await cliDeps.runDoctor(commandArgs); break; case "update": await cliDeps.runUpdate(commandArgs); break; case "list": await cliDeps.runList(); break; default: cliDeps.logError(`Unknown command: ${command}`); cliDeps.log(`\n${cliDeps.printUsage()}`); cliDeps.exit(1); return; } } catch (error) { reportCliError(cliDeps, error); } } export function runCliEntrypoint( run?: () => Promise, deps: Pick = DEFAULT_CLI_DEPS ): void { const runFn = run ?? runCli; runFn().catch((error) => { deps.logError(`Fatal error: ${error}`); deps.exit(1); }); } if (import.meta.main) { runCliEntrypoint(); }