refactor: use --json flag alias instead of --format json throughout CLI

This commit is contained in:
Mohamed Boudra
2026-02-01 12:33:18 +07:00
parent dac1538d1f
commit a473302a9f
22 changed files with 313 additions and 167 deletions
+8 -13
View File
@@ -32,7 +32,8 @@ export function createCli(): Command {
.description('Paseo CLI - control your AI coding agents from the command line')
.version(VERSION, '-v, --version', 'output the version number')
// Global output options
.option('-f, --format <format>', 'output format: table, json, yaml', 'table')
.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')
@@ -47,12 +48,7 @@ export function createCli(): Command {
.option('--ui', 'Show only UI agents (equivalent to --label ui=true)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((options, command) => {
if (options.json) {
command.parent.opts().format = 'json'
}
return withOutput(runLsCommand)(options, command)
})
.action(withOutput(runLsCommand))
program
.command('run')
@@ -69,6 +65,7 @@ export function createCli(): Command {
.option('--cwd <path>', 'Working directory (default: current)')
.option('--label <key=value>', 'Add label(s) to the agent (can be used multiple times)', collectMultiple, [])
.option('--ui', 'Mark as UI agent (equivalent to --label ui=true)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runRunCommand))
@@ -96,6 +93,7 @@ export function createCli(): Command {
.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:port (default: localhost:6767)')
.action(withOutput(runStopCommand))
@@ -106,6 +104,7 @@ export function createCli(): Command {
.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:port (default: localhost:6767)')
.action(withOutput(runSendCommand))
@@ -115,18 +114,14 @@ export function createCli(): Command {
.argument('<id>', 'Agent ID (or prefix)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((id, options, command) => {
if (options.json) {
command.parent.opts().format = 'json'
}
return withOutput(runInspectCommand)(id, options, command)
})
.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: 600)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runWaitCommand))
+8 -6
View File
@@ -29,12 +29,7 @@ export function createAgentCommand(): Command {
.option('--ui', 'Show only UI agents (equivalent to --label ui=true)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((options, command) => {
if (options.json) {
command.parent.parent.opts().format = 'json'
}
return withOutput(runLsCommand)(options, command)
})
.action(withOutput(runLsCommand))
agent
.command('run')
@@ -48,6 +43,7 @@ export function createAgentCommand(): Command {
.option('--cwd <path>', 'Working directory (default: current)')
.option('--label <key=value>', 'Add label(s) to the agent (can be used multiple times)', collectMultiple, [])
.option('--ui', 'Mark as UI agent (equivalent to --label ui=true)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runRunCommand))
@@ -74,6 +70,7 @@ export function createAgentCommand(): Command {
.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:port (default: localhost:6767)')
.action(withOutput(runStopCommand))
@@ -83,6 +80,7 @@ export function createAgentCommand(): Command {
.argument('<id>', 'Agent ID (or prefix)')
.argument('<prompt>', 'The message to send')
.option('--no-wait', 'Return immediately without waiting for completion')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runSendCommand))
@@ -90,6 +88,7 @@ export function createAgentCommand(): Command {
.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:port (default: localhost:6767)')
.action(withOutput(runInspectCommand))
@@ -98,6 +97,7 @@ export function createAgentCommand(): Command {
.description('Wait for an agent to become idle')
.argument('<id>', 'Agent ID (or prefix)')
.option('--timeout <seconds>', 'Maximum wait time (default: 600)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runWaitCommand))
@@ -108,6 +108,7 @@ export function createAgentCommand(): Command {
.argument('<id>', 'Agent ID (or prefix)')
.argument('[mode]', 'Mode to set (required unless --list)')
.option('--list', 'List available modes for this agent')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runModeCommand))
@@ -116,6 +117,7 @@ export function createAgentCommand(): Command {
.description('Archive an agent (soft-delete)')
.argument('<id>', 'Agent ID (or prefix)')
.option('--force', 'Force archive running agent')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runArchiveCommand))
+36 -19
View File
@@ -20,6 +20,15 @@ export interface AgentLogsOptions extends CommandOptions {
export type AgentLogsResult = void
function parseTailCount(raw: string | undefined): number | undefined {
if (raw === undefined) return undefined
const parsed = Number.parseInt(raw, 10)
if (Number.isNaN(parsed) || parsed < 0) {
return undefined
}
return parsed
}
/**
* Check if a timeline item matches the filter type
*/
@@ -110,6 +119,12 @@ export async function runLogsCommand(
// For follow mode, we stream events continuously
if (options.follow) {
if (options.tail !== undefined && parseTailCount(options.tail) === undefined) {
console.error(`Error: Invalid --tail value: ${options.tail}`)
console.error('Usage: --tail <n> (where n is >= 0)')
await client.close().catch(() => {})
process.exit(1)
}
await runFollowMode(client, resolvedId, options)
return
}
@@ -159,18 +174,22 @@ export async function runLogsCommand(
timelineItems = timelineItems.filter((item) => matchesFilter(item, options.filter))
}
// Apply tail limit
if (options.tail) {
const tailCount = parseInt(options.tail, 10)
if (!isNaN(tailCount) && tailCount > 0) {
timelineItems = timelineItems.slice(-tailCount)
}
const tailCount = parseTailCount(options.tail)
if (options.tail !== undefined && tailCount === undefined) {
console.error(`Error: Invalid --tail value: ${options.tail}`)
console.error('Usage: --tail <n> (where n is >= 0)')
await client.close().catch(() => {})
process.exit(1)
}
await client.close()
// Use curateAgentActivity to format the transcript
const transcript = curateAgentActivity(timelineItems)
if (tailCount === 0) {
return
}
const transcript = curateAgentActivity(timelineItems, tailCount !== undefined ? { maxItems: tailCount } : undefined)
console.log(transcript)
} catch (err) {
const message = err instanceof Error ? err.message : String(err)
@@ -188,6 +207,9 @@ async function runFollowMode(
agentId: string,
options: AgentLogsOptions
): Promise<void> {
const DEFAULT_FOLLOW_TAIL = 10
const tailCount = parseTailCount(options.tail) ?? DEFAULT_FOLLOW_TAIL
// First, get existing timeline
const snapshotPromise = new Promise<AgentTimelineItem[]>((resolve) => {
const timeout = setTimeout(() => resolve([]), 10000)
@@ -218,22 +240,17 @@ async function runFollowMode(
existingItems = existingItems.filter((item) => matchesFilter(item, options.filter))
}
// Apply tail to existing items
if (options.tail) {
const tailCount = parseInt(options.tail, 10)
if (!isNaN(tailCount) && tailCount > 0) {
existingItems = existingItems.slice(-tailCount)
// Print existing transcript (tail-like behavior)
if (tailCount > 0) {
const existingTranscript = curateAgentActivity(existingItems, { maxItems: tailCount })
if (existingTranscript !== 'No activity to display.') {
console.log(existingTranscript)
}
}
// Print existing transcript
const existingTranscript = curateAgentActivity(existingItems)
if (existingTranscript !== 'No activity to display.') {
console.log(existingTranscript)
}
// Subscribe to new events
console.log('\n--- Following logs (Ctrl+C to stop) ---\n')
const tailLabel = tailCount === 0 ? 'no history' : `last ${tailCount} entr${tailCount === 1 ? 'y' : 'ies'}`
console.log(`\n--- Following logs (${tailLabel}; Ctrl+C to stop) ---\n`)
const unsubscribe = client.on('agent_stream', (msg: unknown) => {
const message = msg as AgentStreamMessage
+3 -6
View File
@@ -15,22 +15,19 @@ export function createDaemonCommand(): Command {
.description('Show daemon status')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((options, command) => {
if (options.json) {
command.parent.parent.opts().format = 'json'
}
return withOutput(runStatusCommand)(options, command)
})
.action(withOutput(runStatusCommand))
daemon
.command('stop')
.description('Stop the daemon')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runStopCommand))
daemon
.command('restart')
.description('Restart the daemon')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runRestartCommand))
+3 -6
View File
@@ -12,12 +12,7 @@ export function createPermitCommand(): Command {
.description('List all pending permissions')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((options, command) => {
if (options.json) {
command.parent.parent.opts().format = 'json'
}
return withOutput(runLsCommand)(options, command)
})
.action(withOutput(runLsCommand))
permit
.command('allow')
@@ -26,6 +21,7 @@ export function createPermitCommand(): Command {
.argument('[req_id]', 'Permission request ID (optional if --all)')
.option('--all', 'Allow all pending permissions for this agent')
.option('--input <json>', 'Modified input parameters (JSON)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runAllowCommand))
@@ -37,6 +33,7 @@ export function createPermitCommand(): Command {
.option('--all', 'Deny all pending permissions for this agent')
.option('--message <msg>', 'Denial reason message')
.option('--interrupt', 'Stop agent after denial')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runDenyCommand))
+2 -12
View File
@@ -11,12 +11,7 @@ export function createProviderCommand(): Command {
.description('List available providers and status')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((options, command) => {
if (options.json) {
command.parent.parent.opts().format = 'json'
}
return withOutput(runLsCommand)(options, command)
})
.action(withOutput(runLsCommand))
provider
.command('models')
@@ -24,12 +19,7 @@ export function createProviderCommand(): Command {
.argument('<provider>', 'Provider name (claude, codex, opencode)')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((provider, options, command) => {
if (options.json) {
command.parent.parent.opts().format = 'json'
}
return withOutput(runModelsCommand)(provider, options, command)
})
.action(withOutput(runModelsCommand))
return provider
}
+2 -6
View File
@@ -11,17 +11,13 @@ export function createWorktreeCommand(): Command {
.description('List Paseo-managed git worktrees')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action((options, command) => {
if (options.json) {
command.parent.parent.opts().format = 'json'
}
return withOutput(runLsCommand)(options, command)
})
.action(withOutput(runLsCommand))
worktree
.command('archive')
.description('Archive a worktree (removes worktree and associated branch)')
.argument('<name>', 'Worktree name or branch name')
.option('--json', 'Output in JSON format')
.option('--host <host>', 'Daemon host:port (default: localhost:6767)')
.action(withOutput(runArchiveCommand))
+18 -2
View File
@@ -5,7 +5,7 @@
*/
import type { Command } from 'commander'
import type { AnyCommandResult, OutputOptions } from './types.js'
import type { AnyCommandResult, CommandError, OutputOptions } from './types.js'
import { render, renderError, toCommandError, defaultOutputOptions } from './render.js'
/** Options that include output settings from global options */
@@ -13,10 +13,26 @@ export interface CommandOptions extends Partial<OutputOptions> {
[key: string]: unknown
}
function normalizeFormat(raw: unknown): OutputOptions['format'] {
const value = typeof raw === 'string' ? raw.trim().toLowerCase() : ''
// Common user expectation: "cli" means "table/human"
if (value === 'cli') return 'table'
if (value === 'table' || value === 'json' || value === 'yaml') return value
const error: CommandError = {
code: 'INVALID_FORMAT',
message: `Unsupported output format: ${String(raw)}`,
details: 'Supported formats: table, json, yaml',
}
throw error
}
/** Extract output options from command options */
function extractOutputOptions(options: CommandOptions): OutputOptions {
return {
format: (options.format as OutputOptions['format']) ?? defaultOutputOptions.format,
format: options.json ? 'json' : normalizeFormat(options.format ?? defaultOutputOptions.format),
quiet: options.quiet ?? defaultOutputOptions.quiet,
noHeaders: options.headers === false, // Commander uses --no-headers -> headers: false
noColor: options.color === false, // Commander uses --no-color -> color: false