diff --git a/packages/cli/src/commands/models.ts b/packages/cli/src/commands/models.ts index 45e6fb0..5f0de07 100644 --- a/packages/cli/src/commands/models.ts +++ b/packages/cli/src/commands/models.ts @@ -9,7 +9,11 @@ export async function modelsListCommand(options: { file: string; provider?: stri for (const listing of listings) { const name = listing.provider; if (!listing.supportsDynamicListing) { - if (!options.json) { + if (options.json) { + process.stdout.write( + `${JSON.stringify({ provider: name, supportsDynamicListing: false, models: [] }, null, 2)}\n`, + ); + } else { console.log(chalk.yellow(`\n Provider '${name}' does not support dynamic model listing.`)); if (name === "claude") { console.log(chalk.dim(` Claude models are specified directly (e.g. claude-sonnet-4-6, claude-opus-4-6).`)); diff --git a/packages/cli/tests/unit/cli-contracts.test.ts b/packages/cli/tests/unit/cli-contracts.test.ts index 4545123..1ee6f29 100644 --- a/packages/cli/tests/unit/cli-contracts.test.ts +++ b/packages/cli/tests/unit/cli-contracts.test.ts @@ -466,6 +466,21 @@ test("models list keeps missing config guidance in the core runtime", async () = expect(result.stderr).toContain("File not found"); }); +test("models list JSON reports providers without dynamic listing support", async () => { + const dir = await makeTempDir(); + const configPath = await writeBailianVaultConfig(dir); + + const result = await runAgents(["models", "list", "--file", configPath, "--provider", "bailian", "--json"]); + + expect(result.exitCode).toBe(0); + expect(JSON.parse(result.stdout)).toEqual({ + provider: "bailian", + supportsDynamicListing: false, + models: [], + }); + expect(result.stderr).toBe(""); +}); + test("migrated CLI commands consume core service APIs instead of composing internals", async () => { const commands = ["destroy.ts", "session.ts", "state.ts", "deployment.ts", "validate.ts", "models.ts"]; const banned = [