mirror of
https://github.com/getpaseo/paseo.git
synced 2026-07-29 12:01:31 +00:00
Adds a unified diagnostic system for Paseo setup: - Doctor module in packages/server with provider, config, and runtime checks - GET /api/doctor HTTP endpoint on the daemon - `paseo doctor` CLI command (local checks by default, --remote for daemon) - Tests for the doctor report shape and summary correctness
224 lines
10 KiB
TypeScript
224 lines
10 KiB
TypeScript
import { Command } from 'commander'
|
|
import { createRequire } from 'node:module'
|
|
import { createAgentCommand } from './commands/agent/index.js'
|
|
import { createDaemonCommand } from './commands/daemon/index.js'
|
|
import { createPermitCommand } from './commands/permit/index.js'
|
|
import { createProviderCommand } from './commands/provider/index.js'
|
|
import { createSpeechCommand } from './commands/speech/index.js'
|
|
import { createWorktreeCommand } from './commands/worktree/index.js'
|
|
import { startCommand as daemonStartCommand } from './commands/daemon/start.js'
|
|
import { runStatusCommand as runDaemonStatusCommand } from './commands/daemon/status.js'
|
|
import { runRestartCommand as runDaemonRestartCommand } from './commands/daemon/restart.js'
|
|
import { runDaemonUpdateCommandOrExit } from './commands/daemon/update.js'
|
|
import { runLsCommand } from './commands/agent/ls.js'
|
|
import { runRunCommand } from './commands/agent/run.js'
|
|
import { runLogsCommand } from './commands/agent/logs.js'
|
|
import { runDeleteCommand } from './commands/agent/delete.js'
|
|
import { runStopCommand } from './commands/agent/stop.js'
|
|
import { runSendCommand } from './commands/agent/send.js'
|
|
import { runInspectCommand } from './commands/agent/inspect.js'
|
|
import { runWaitCommand } from './commands/agent/wait.js'
|
|
import { runAttachCommand } from './commands/agent/attach.js'
|
|
import { runDoctorCommand } from './commands/doctor.js'
|
|
import { withOutput } from './output/index.js'
|
|
import { onboardCommand } from './commands/onboard.js'
|
|
|
|
const require = createRequire(import.meta.url)
|
|
|
|
type CliPackageJson = {
|
|
version?: unknown
|
|
}
|
|
|
|
function resolveCliVersion(): string {
|
|
const packageJson = require('../package.json') as CliPackageJson
|
|
if (typeof packageJson.version === 'string' && packageJson.version.trim().length > 0) {
|
|
return packageJson.version.trim()
|
|
}
|
|
throw new Error('Unable to resolve @getpaseo/cli version from package.json.')
|
|
}
|
|
|
|
const VERSION = resolveCliVersion()
|
|
|
|
// Helper function to collect multiple option values into an array
|
|
function collectMultiple(value: string, previous: string[]): string[] {
|
|
return previous.concat([value])
|
|
}
|
|
|
|
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 <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')
|
|
|
|
// Primary agent commands (top-level)
|
|
program
|
|
.command('ls')
|
|
.description('List agents. By default excludes archived agents.')
|
|
.option('-a, --all', 'Include archived agents')
|
|
.option('-g, --global', 'Legacy no-op (kept for compatibility)')
|
|
.option('--label <key=value>', 'Filter by label (can be used multiple times)', collectMultiple, [])
|
|
.option('--thinking <id>', 'Filter by thinking option ID')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runLsCommand))
|
|
|
|
program
|
|
.command('run')
|
|
.description('Create and start an agent with a task')
|
|
.argument('<prompt>', 'The task/prompt for the agent')
|
|
.option('-d, --detach', 'Run in background (detached)')
|
|
.option('--name <name>', 'Assign a name/title to the agent')
|
|
.option('--provider <provider>', 'Agent provider: claude | codex | opencode', 'claude')
|
|
.option('--model <model>', 'Model to use (e.g., claude-sonnet-4-20250514, claude-3-5-haiku-20241022)')
|
|
.option('--thinking <id>', 'Thinking option ID to use for this run')
|
|
.option('--mode <mode>', 'Provider-specific mode (e.g., plan, default, bypass)')
|
|
.option('--worktree <name>', 'Create agent in a new git worktree')
|
|
.option('--base <branch>', 'Base branch for worktree (default: current branch)')
|
|
.option('--image <path>', 'Attach image(s) to the initial prompt (can be used multiple times)', collectMultiple, [])
|
|
.option('--cwd <path>', 'Working directory (default: current)')
|
|
.option('--label <key=value>', 'Add label(s) to the agent (can be used multiple times)', collectMultiple, [])
|
|
.option('--output-schema <schema>', 'Output JSON matching the provided schema file path or inline JSON schema')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runRunCommand))
|
|
|
|
program
|
|
.command('attach')
|
|
.description("Attach to a running agent's output stream")
|
|
.argument('<id>', 'Agent ID (or prefix)')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(runAttachCommand)
|
|
|
|
program
|
|
.command('logs')
|
|
.description('View agent activity/timeline')
|
|
.argument('<id>', 'Agent ID (or prefix)')
|
|
.option('-f, --follow', 'Follow log output (streaming)')
|
|
.option('--tail <n>', 'Show last n entries')
|
|
.option('--filter <type>', 'Filter by event type (tools, text, errors, permissions)')
|
|
.option('--since <time>', 'Show logs since timestamp')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(runLogsCommand)
|
|
|
|
program
|
|
.command('stop')
|
|
.description('Interrupt an agent if it is running (no-op for idle agents)')
|
|
.argument('[id]', 'Agent ID (or prefix) - optional if --all or --cwd specified')
|
|
.option('--all', 'Stop all agents')
|
|
.option('--cwd <path>', 'Stop all agents in directory')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runStopCommand))
|
|
|
|
program
|
|
.command('delete')
|
|
.description('Delete an agent (interrupt if running, then hard-delete)')
|
|
.argument('[id]', 'Agent ID (or prefix) - optional if --all or --cwd specified')
|
|
.option('--all', 'Delete all agents')
|
|
.option('--cwd <path>', 'Delete all agents in directory')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runDeleteCommand))
|
|
|
|
program
|
|
.command('send')
|
|
.description('Send a message/task to an existing agent')
|
|
.argument('<id>', 'Agent ID (or prefix)')
|
|
.argument('<prompt>', 'The message to send')
|
|
.option('--no-wait', 'Return immediately without waiting for completion')
|
|
.option('--image <path>', 'Attach image(s) to the message', collectMultiple, [])
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runSendCommand))
|
|
|
|
program
|
|
.command('inspect')
|
|
.description('Show detailed information about an agent')
|
|
.argument('<id>', 'Agent ID (or prefix)')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runInspectCommand))
|
|
|
|
program
|
|
.command('wait')
|
|
.description('Wait for an agent to become idle')
|
|
.argument('<id>', 'Agent ID (or prefix)')
|
|
.option('--timeout <seconds>', 'Maximum wait time (default: no limit)')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (default: local socket/pipe, then localhost:6767)')
|
|
.action(withOutput(runWaitCommand))
|
|
|
|
// Top-level local daemon shortcuts
|
|
program.addCommand(onboardCommand())
|
|
program.addCommand(daemonStartCommand())
|
|
|
|
program
|
|
.command('update')
|
|
.description('Update local daemon package (alias for "paseo daemon update")')
|
|
.option('--home <path>', 'Paseo home directory (default: ~/.paseo)')
|
|
.option('-y, --yes', 'Restart automatically after update')
|
|
.action(async (options: { home?: string; yes?: boolean }) => {
|
|
await runDaemonUpdateCommandOrExit(options)
|
|
})
|
|
|
|
program
|
|
.command('status')
|
|
.description('Show local daemon status (alias for "paseo daemon status")')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--home <path>', 'Paseo home directory (default: ~/.paseo)')
|
|
.action(withOutput(runDaemonStatusCommand))
|
|
|
|
program
|
|
.command('restart')
|
|
.description('Restart local daemon (alias for "paseo daemon restart")')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--home <path>', 'Paseo home directory (default: ~/.paseo)')
|
|
.option('--timeout <seconds>', 'Wait timeout before force step (default: 15)')
|
|
.option('--force', 'Send SIGKILL if graceful stop times out')
|
|
.option('--listen <listen>', 'Listen target for restarted daemon (host:port, port, or unix socket)')
|
|
.option('--port <port>', 'Port for restarted daemon listen target')
|
|
.option('--no-relay', 'Disable relay on restarted daemon')
|
|
.option('--no-mcp', 'Disable Agent MCP on restarted daemon')
|
|
.option(
|
|
'--allowed-hosts <hosts>',
|
|
'Comma-separated Host allowlist values (example: "localhost,.example.com" or "true")'
|
|
)
|
|
.action(withOutput(runDaemonRestartCommand))
|
|
|
|
program
|
|
.command('doctor')
|
|
.description('Diagnose your Paseo setup (agents, config, runtime)')
|
|
.option('--remote', 'Fetch diagnostics from the running daemon instead of checking locally')
|
|
.option('--json', 'Output in JSON format')
|
|
.option('--host <host>', 'Daemon host target (used with --remote)')
|
|
.action(withOutput(runDoctorCommand))
|
|
|
|
// Advanced agent commands (less common operations)
|
|
program.addCommand(createAgentCommand())
|
|
|
|
// Daemon commands
|
|
program.addCommand(createDaemonCommand())
|
|
|
|
// Permission commands
|
|
program.addCommand(createPermitCommand())
|
|
|
|
// Provider commands
|
|
program.addCommand(createProviderCommand())
|
|
|
|
// Speech model commands
|
|
program.addCommand(createSpeechCommand())
|
|
|
|
// Worktree commands
|
|
program.addCommand(createWorktreeCommand())
|
|
|
|
return program
|
|
}
|