diff --git a/packages/cli/src/commands/provider/ls.ts b/packages/cli/src/commands/provider/ls.ts index 1e56b60f73..ceb01de247 100644 --- a/packages/cli/src/commands/provider/ls.ts +++ b/packages/cli/src/commands/provider/ls.ts @@ -7,9 +7,9 @@ export interface ProviderListItem { provider: ProviderSnapshotEntry["provider"]; label: string; status: string; - enabled: "Enabled" | "Disabled"; + enabled: boolean; defaultMode: string; - modes: string; + modes: Array<{ id: string; label: string }>; } /** Schema for provider ls output */ @@ -28,9 +28,13 @@ export const providerLsSchema: OutputSchema = { return undefined; }, }, - { header: "ENABLED", field: "enabled", width: 10 }, + { header: "ENABLED", field: (item) => (item.enabled ? "Enabled" : "Disabled"), width: 10 }, { header: "DEFAULT MODE", field: "defaultMode", width: 14 }, - { header: "MODES", field: "modes", width: 30 }, + { + header: "MODES", + field: (item) => item.modes.map((mode) => mode.label).join(", "), + width: 30, + }, ], }; @@ -54,9 +58,9 @@ export async function runLsCommand( provider: entry.provider, label: entry.label ?? entry.provider, status: entry.status === "ready" ? "available" : entry.status, - enabled: !entry.enabled ? "Disabled" : "Enabled", + enabled: entry.enabled, defaultMode: entry.defaultModeId ?? "default", - modes: (entry.modes ?? []).map((mode) => mode.label).join(", "), + modes: (entry.modes ?? []).map(({ id, label }) => ({ id, label })), })), schema: providerLsSchema, }; diff --git a/packages/cli/tests/15-provider.test.ts b/packages/cli/tests/15-provider.test.ts index 3081ee3584..2c819320e0 100644 --- a/packages/cli/tests/15-provider.test.ts +++ b/packages/cli/tests/15-provider.test.ts @@ -22,6 +22,7 @@ */ import assert from "node:assert"; +import YAML from "yaml"; import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { @@ -43,7 +44,8 @@ interface ProviderListRow { provider: string; label: string; status: string; - enabled: string; + enabled: boolean; + modes: Array<{ id: string; label: string }>; } interface ProviderDiagnostic { @@ -246,6 +248,7 @@ try { // Test 3: provider ls --json outputs valid JSON { console.log("Test 3: provider ls --json outputs valid JSON"); + await runProviderModelsJson("codex"); const result = await ctx.paseo(["provider", "ls", "--json"]); assert.strictEqual(result.exitCode, 0, "should exit 0"); const data = JSON.parse(result.stdout.trim()); @@ -264,15 +267,33 @@ try { "should include opencode", ); const rows = data as ProviderListRow[]; + const codex = rows.find((p) => p.provider === "codex"); + assert(codex, "should include codex"); + assert(Array.isArray(codex.modes), "mode choices should be structured, not display text"); + assert( + codex.modes.some((mode) => mode.id === "full-access" && mode.label === "Full Access"), + "mode choices should include an ID usable with run --mode and its display label", + ); + assert( + codex.modes.every((mode) => mode.label.length > 0), + "modes should keep display labels", + ); for (const provider of ["claude", "codex", "opencode"] as const) { const row = rows.find((p) => p.provider === provider); assert(row, `should include ${provider}`); - assert.strictEqual(row.enabled, "Enabled", `${provider} should report Enabled`); + assert.strictEqual(row.enabled, true, `${provider} should report enabled`); } const omp = rows.find((p) => p.provider === "omp"); assert(omp, "should include omp"); - assert.strictEqual(omp.enabled, "Disabled", "omp should report Disabled by default"); + assert.strictEqual(omp.enabled, false, "omp should report disabled by default"); + const yamlResult = await ctx.paseo(["provider", "ls", "--format", "yaml"]); + assert.strictEqual(yamlResult.exitCode, 0, "YAML provider listing should exit 0"); + const yamlRows = YAML.parse(yamlResult.stdout) as ProviderListRow[]; + const yamlCodex = yamlRows.find((p) => p.provider === "codex"); + assert(yamlCodex, "YAML should include codex"); + assert.strictEqual(yamlCodex.enabled, true, "YAML should preserve boolean enabled state"); + assert.deepStrictEqual(yamlCodex.modes, codex.modes, "YAML should preserve the same mode IDs"); console.log("✓ provider ls --json outputs valid JSON\n"); } @@ -305,11 +326,11 @@ try { const data = JSON.parse(result.stdout.trim()) as ProviderListRow[]; const claude = data.find((p) => p.provider === "claude"); assert(claude, "disabled claude provider should stay in provider ls"); - assert.strictEqual(claude.enabled, "Disabled", "disabled provider should report Disabled"); + assert.strictEqual(claude.enabled, false, "disabled provider should report false"); const opencode = data.find((p) => p.provider === "opencode"); assert(opencode, "enabled opencode provider should stay in provider ls"); - assert.strictEqual(opencode.enabled, "Enabled", "enabled provider should report Enabled"); + assert.strictEqual(opencode.enabled, true, "enabled provider should report true"); const modelsResult = await runPaseoCli(disabledCtx, ["provider", "models", "claude"]); assert.notStrictEqual( @@ -364,10 +385,6 @@ try { ids.every((id) => id.startsWith("gpt-")), "all codex model IDs should be from the gpt family", ); - assert( - ids.some((id) => id.includes("codex")), - "codex model list should include at least one codex-optimized model", - ); assert( data.every((m) => m.model && m.id && m.description), "every codex model should have model, id, and description fields",