From 6053ee7c349824bff91c5c80598b1bd6f4536bf8 Mon Sep 17 00:00:00 2001 From: Mohamed Boudra Date: Thu, 12 Feb 2026 11:53:12 +0700 Subject: [PATCH] fix(cli): list all non-archived agents by default and drop ls --ui --- packages/cli/src/cli.ts | 7 ++- packages/cli/src/commands/agent/index.ts | 7 ++- packages/cli/src/commands/agent/ls.ts | 55 ++++++------------------ packages/cli/tests/04-agent-ls.test.ts | 13 ++++++ 4 files changed, 33 insertions(+), 49 deletions(-) diff --git a/packages/cli/src/cli.ts b/packages/cli/src/cli.ts index f1c77d3ed..928b32c9e 100644 --- a/packages/cli/src/cli.ts +++ b/packages/cli/src/cli.ts @@ -42,11 +42,10 @@ export function createCli(): Command { // Primary agent commands (top-level) program .command('ls') - .description('List agents. By default shows background agents (without ui=true) in current directory.') - .option('-a, --all', 'Include all statuses (not just running)') - .option('-g, --global', 'Show agents from all directories (not just current)') + .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 ', 'Filter by label (can be used multiple times)', collectMultiple, []) - .option('--ui', 'Show only UI agents (equivalent to --label ui=true)') .option('--json', 'Output in JSON format') .option('--host ', 'Daemon host:port (default: localhost:6767)') .action(withOutput(runLsCommand)) diff --git a/packages/cli/src/commands/agent/index.ts b/packages/cli/src/commands/agent/index.ts index 0ef44363f..1f0e2a51c 100644 --- a/packages/cli/src/commands/agent/index.ts +++ b/packages/cli/src/commands/agent/index.ts @@ -22,11 +22,10 @@ export function createAgentCommand(): Command { // Primary agent commands (same as top-level) agent .command('ls') - .description('List agents. By default shows background agents (without ui=true) in current directory.') - .option('-a, --all', 'Include all statuses (not just running)') - .option('-g, --global', 'Show agents from all directories (not just current)') + .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 ', 'Filter by label (can be used multiple times)', collectMultiple, []) - .option('--ui', 'Show only UI agents (equivalent to --label ui=true)') .option('--json', 'Output in JSON format') .option('--host ', 'Daemon host:port (default: localhost:6767)') .action(withOutput(runLsCommand)) diff --git a/packages/cli/src/commands/agent/ls.ts b/packages/cli/src/commands/agent/ls.ts index b9fd9b2dd..3a2b7fd78 100644 --- a/packages/cli/src/commands/agent/ls.ts +++ b/packages/cli/src/commands/agent/ls.ts @@ -74,26 +74,22 @@ function toListItem(agent: AgentSnapshotPayload): AgentListItem { export type AgentLsResult = ListResult export interface AgentLsOptions extends CommandOptions { - /** -a: Include all statuses (not just running/idle) */ + /** -a: Include archived agents */ all?: boolean - /** -g: Show agents globally (not just current directory) */ + /** Legacy flag retained for CLI compatibility */ global?: boolean /** Filter by specific status */ status?: string - /** Filter by specific cwd (overrides default cwd filtering) */ + /** Filter by specific cwd */ cwd?: string /** Filter by labels (key=value format) */ label?: string[] - /** Filter to UI agents only (equivalent to --label ui=true) */ - ui?: boolean } /** - * Agent ls command with correct semantics from design doc: - * - `paseo agent ls` → running/idle agents in current directory - * - `paseo agent ls -a` → all statuses in current directory - * - `paseo agent ls -g` → running/idle agents globally - * - `paseo agent ls -ag` → everything everywhere + * Agent ls command semantics: + * - `paseo agent ls` → all non-archived agents + * - `paseo agent ls -a` → include archived agents */ export async function runLsCommand( options: AgentLsOptions, @@ -117,39 +113,26 @@ export async function runLsCommand( try { let agents = await client.fetchAgents() - // Status filtering: - // By default, only show running/idle agents (not error, archived, etc.) - // With -a flag, show all statuses + // By default, exclude archived agents. `-a` includes them. if (!options.all) { - agents = agents.filter((a) => { - // Show running and idle agents, exclude archived - return (a.status === 'running' || a.status === 'idle') && !a.archivedAt - }) + agents = agents.filter((a) => !a.archivedAt) } - // If explicit status filter is provided, use it + // If explicit status filter is provided, apply it. if (options.status) { agents = agents.filter((a) => a.status === options.status) } - // Directory filtering: - // By default, only show agents in current working directory - // With -g flag, show agents globally (all directories) - if (!options.global) { - const currentCwd = options.cwd ?? process.cwd() + // Optional cwd filter. + if (options.cwd) { + const targetCwd = options.cwd.replace(/\/$/, '') agents = agents.filter((a) => { - // Normalize paths for comparison const agentCwd = a.cwd.replace(/\/$/, '') - const targetCwd = currentCwd.replace(/\/$/, '') - // Match exact cwd or subdirectories return agentCwd === targetCwd || agentCwd.startsWith(targetCwd + '/') }) } - // Label filtering: - // Parse --label flags and --ui flag - // --ui is equivalent to --label ui=true - // By default (no --ui flag), show background agents (those WITHOUT ui=true) + // Parse --label filters (key=value). const labelFilters: Record = {} if (options.label) { for (const labelStr of options.label) { @@ -161,14 +144,9 @@ export async function runLsCommand( } } } - // Add ui=true filter if --ui flag is set - if (options.ui) { - labelFilters['ui'] = 'true' - } - // Apply label filtering + // Apply label filtering only when explicitly requested. if (Object.keys(labelFilters).length > 0) { - // Filter to agents that have ALL specified labels (AND semantics) agents = agents.filter((a) => { const agentLabels = a.labels for (const [key, value] of Object.entries(labelFilters)) { @@ -178,11 +156,6 @@ export async function runLsCommand( } return true }) - } else { - // Default: show background agents only (those without ui=true) - agents = agents.filter((a) => { - return a.labels['ui'] !== 'true' - }) } await client.close() diff --git a/packages/cli/tests/04-agent-ls.test.ts b/packages/cli/tests/04-agent-ls.test.ts index 7c599289b..1a0e6a559 100644 --- a/packages/cli/tests/04-agent-ls.test.ts +++ b/packages/cli/tests/04-agent-ls.test.ts @@ -16,6 +16,7 @@ * - paseo ls --json returns valid JSON (or error) * - paseo ls -a flag is accepted * - paseo ls -g flag is accepted + * - paseo ls does not support --ui */ import assert from 'node:assert' @@ -52,6 +53,7 @@ try { assert(result.stdout.includes('-g'), 'help should mention -g flag') assert(result.stdout.includes('--global'), 'help should mention --global flag') assert(result.stdout.includes('--host'), 'help should mention --host option') + assert(!result.stdout.includes('--ui'), 'help should not mention --ui') console.log('✓ paseo ls --help shows options\n') } @@ -138,6 +140,17 @@ try { assert(!output.includes('error: option'), 'should not have option parsing error') console.log('✓ -q (quiet) flag is accepted\n') } + + // Test 9: paseo ls --ui is rejected (flag removed) + { + console.log('Test 9: paseo ls --ui is rejected') + const result = + await $`PASEO_HOST=localhost:${port} PASEO_HOME=${paseoHome} npx paseo ls --ui`.nothrow() + assert.notStrictEqual(result.exitCode, 0, 'should fail for removed --ui flag') + const output = result.stdout + result.stderr + assert(output.includes('unknown option'), 'should report unknown option for --ui') + console.log('✓ paseo ls --ui is rejected\n') + } } finally { // Clean up temp directory await rm(paseoHome, { recursive: true, force: true })