import { pairCommand } from "./commands/daemon/pair.js"; import { Command, Option } from "commander"; import { createAgentCommand } from "./commands/agent/index.js"; import { createDaemonCommand } from "./commands/daemon/index.js"; import { createPermitCommand } from "./commands/permit/index.js"; import { createUsageCommand } from "./commands/usage/index.js"; import { createProviderCommand } from "./commands/provider/index.js"; import { createPluginCommand } from "./commands/plugin/index.js"; import { createProjectCommand } from "./commands/project/index.js"; import { createScheduleCommand } from "./commands/schedule/index.js"; import { createSpeechCommand } from "./commands/speech/index.js"; import { createScriptCommand } from "./commands/script/index.js"; import { createTerminalCommand } from "./commands/terminal/index.js"; import { createWorktreeCommand } from "./commands/worktree/index.js"; import { createWorkspaceCommand } from "./commands/workspace/index.js"; import { createHeartbeatCommand } from "./commands/heartbeat/index.js"; import { createHubCommand } from "./commands/hub/index.js"; import { createHooksCommand } from "./commands/hooks.js"; import { startCommand as daemonStartCommand } from "./commands/daemon/start.js"; import { daemonStatusCommand } from "./commands/daemon/status.js"; import { daemonRestartCommand } from "./commands/daemon/restart.js"; import { daemonReloadCommand } from "./commands/daemon/reload.js"; import { addLsOptions, runLsCommand } from "./commands/agent/ls.js"; import { addRunOptions, runRunCommand } from "./commands/agent/run.js"; import { addLogsOptions, runLogsCommand } from "./commands/agent/logs.js"; import { addDeleteOptions, runDeleteCommand } from "./commands/agent/delete.js"; import { addStopOptions, runStopCommand } from "./commands/agent/stop.js"; import { addSendOptions, runSendCommand } from "./commands/agent/send.js"; import { addInspectOptions, runInspectCommand } from "./commands/agent/inspect.js"; import { addWaitOptions, runWaitCommand } from "./commands/agent/wait.js"; import { addArchiveOptions, runArchiveCommand } from "./commands/agent/archive.js"; import { addAttachOptions, runAttachCommand } from "./commands/agent/attach.js"; import { addImportOptions, runImportCommand } from "./commands/agent/import.js"; import { withOutput } from "./output/index.js"; import { runCloneCommand } from "./commands/clone.js"; import { onboardCommand } from "./commands/onboard.js"; import { addDaemonHostOption, addJsonAndDaemonHostOptions, withGlobalOptions, } from "./utils/command-options.js"; import { resolveCliVersion } from "./version.js"; const VERSION = resolveCliVersion(); export function createCli(): Command { const program = new Command(); program .name("paseo") .description("Paseo CLI - control your AI coding agents from the command line") .version(VERSION, "-v, --version", "output the version number") // Global output options .option("-o, --format ", "output format: table, json, yaml", "table") .option("--json", "output in JSON format (alias for --format json)") .option("-q, --quiet", "minimal output (IDs only)") .option("--no-headers", "omit table headers") .option("--no-color", "disable colored output"); addDaemonHostOption(program); // Primary agent commands (top-level) addJsonAndDaemonHostOptions(addLsOptions(program.command("ls"))).action(withOutput(runLsCommand)); addJsonAndDaemonHostOptions(addRunOptions(program.command("run"))).action( withOutput(runRunCommand), ); addJsonAndDaemonHostOptions(addImportOptions(program.command("import"))).action( withOutput(runImportCommand), ); addJsonAndDaemonHostOptions( program .command("clone") .description("Clone a GitHub repo and register it as a Paseo workspace") .argument("", "GitHub repo in owner/repo format or a full git remote URL") .requiredOption("--dir ", "Parent directory to clone into (for example: ~/workspace)"), ) .addOption( new Option("--protocol ", "Protocol for owner/repo shorthand repositories").choices( ["https", "ssh"], ), ) .action(withOutput(runCloneCommand)); addDaemonHostOption(addAttachOptions(program.command("attach"))).action( withGlobalOptions(runAttachCommand), ); addDaemonHostOption(addLogsOptions(program.command("logs"))).action( withGlobalOptions(runLogsCommand), ); addJsonAndDaemonHostOptions(addStopOptions(program.command("stop"))).action( withOutput(runStopCommand), ); addJsonAndDaemonHostOptions(addDeleteOptions(program.command("delete"))).action( withOutput(runDeleteCommand), ); addJsonAndDaemonHostOptions(addSendOptions(program.command("send"))).action( withOutput(runSendCommand), ); addJsonAndDaemonHostOptions(addInspectOptions(program.command("inspect"))).action( withOutput(runInspectCommand), ); addJsonAndDaemonHostOptions(addWaitOptions(program.command("wait"))).action( withOutput(runWaitCommand), ); addJsonAndDaemonHostOptions(addArchiveOptions(program.command("archive"))).action( withOutput(runArchiveCommand), ); // Top-level local daemon shortcuts program.addCommand(onboardCommand()); program.addCommand(daemonStartCommand()); program.addCommand(createHooksCommand()); program.addCommand(daemonStatusCommand()); program.addCommand(daemonRestartCommand()); program.addCommand(daemonReloadCommand()); program.addCommand(pairCommand()); // Advanced agent commands (less common operations) program.addCommand(createAgentCommand()); // Daemon commands program.addCommand(createDaemonCommand()); program.addCommand(createHubCommand()); // Chat commands // Terminal commands program.addCommand(createTerminalCommand()); // Workspace script commands program.addCommand(createScriptCommand()); // Schedule commands program.addCommand(createScheduleCommand()); program.addCommand(createHeartbeatCommand()); // Permission commands program.addCommand(createPermitCommand()); // Provider commands program.addCommand(createProviderCommand()); program.addCommand(createUsageCommand()); program.addCommand(createPluginCommand()); // Speech model commands program.addCommand(createSpeechCommand()); // Workspace commands program.addCommand(createProjectCommand()); program.addCommand(createWorkspaceCommand()); // COMPAT(worktreeCli): legacy command alias added before workspace was the product unit. // Added in v0.2.0; remove after 2027-01-17. program.addCommand(createWorktreeCommand(), { hidden: true }); // Stop root parsing at the command so `plugin update --version` belongs to update. // Keep global options available after a command, as they were before positional parsing. program.enablePositionalOptions(); for (const command of program.commands) { for (const option of program.options) { if (option.long === "--version") continue; if (!command.options.some((local) => local.long === option.long)) command.addOption(option); if (option.long === "--home" || option.long === "--host") continue; command.on(`option:${option.name()}`, () => { const key = option.attributeName(); program.setOptionValueWithSource(key, command.opts()[key], "cli"); }); } if (command.name() !== "plugin") command.version(VERSION, "-v, --version", "output the version number"); } const enforceSelectorDuplicates = (command: Command) => { for (const option of command.options) { if (option.long !== "--home" && option.long !== "--host") continue; option.argParser((value: string, previous: string | undefined) => { if (previous !== undefined && previous !== value) throw { code: "TARGET_AMBIGUOUS", message: `Conflicting duplicate ${option.long} selectors.`, }; return value; }); } command.commands.forEach(enforceSelectorDuplicates); }; enforceSelectorDuplicates(program); return program; }