From e95e805ec8e35607ef33fd2ae817a8b198bdba01 Mon Sep 17 00:00:00 2001 From: shenghui kevin Date: Sat, 21 Feb 2026 05:02:39 -0800 Subject: [PATCH 1/4] fix(cli): set non-zero exit code when run command encounters errors Co-Authored-By: Claude Opus 4.6 --- packages/opencode/src/cli/cmd/run.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/run.ts b/packages/opencode/src/cli/cmd/run.ts index f3781f1abd86..2578031dab75 100644 --- a/packages/opencode/src/cli/cmd/run.ts +++ b/packages/opencode/src/cli/cmd/run.ts @@ -582,7 +582,7 @@ export const RunCommand = cmd({ } await share(sdk, sessionID) - loop().catch((e) => { + const done = loop().catch((e) => { console.error(e) process.exit(1) }) @@ -606,6 +606,11 @@ export const RunCommand = cmd({ parts: [...files, { type: "text", text: message }], }) } + + await done + if (error) { + process.exitCode = 1 + } } if (args.attach) { From 6ab12d211d1db5ebed101652822dd1dd6d60656c Mon Sep 17 00:00:00 2001 From: shenghui kevin Date: Sat, 21 Feb 2026 05:02:55 -0800 Subject: [PATCH 2/4] feat(cli): add --format json option to models command The session list command supports --format json for machine-readable output, but the models command only supported plain text. This adds a --format option with choices "text" (default) and "json" to bring consistency across CLI commands and enable scripting use cases. Closes #14550 Co-Authored-By: Claude Opus 4.6 --- packages/opencode/src/cli/cmd/models.ts | 45 ++++++++++++++++++------- 1 file changed, 32 insertions(+), 13 deletions(-) diff --git a/packages/opencode/src/cli/cmd/models.ts b/packages/opencode/src/cli/cmd/models.ts index 156dae91c676..44dcd4d227b3 100644 --- a/packages/opencode/src/cli/cmd/models.ts +++ b/packages/opencode/src/cli/cmd/models.ts @@ -24,6 +24,12 @@ export const ModelsCommand = cmd({ describe: "refresh the models cache from models.dev", type: "boolean", }) + .option("format", { + describe: "output format", + type: "string", + choices: ["text", "json"] as const, + default: "text", + }) }, handler: async (args) => { if (args.refresh) { @@ -36,6 +42,18 @@ export const ModelsCommand = cmd({ async fn() { const providers = await Provider.list() + function collectModels(providerID: string) { + const provider = providers[providerID] + return Object.entries(provider.models) + .sort(([a], [b]) => a.localeCompare(b)) + .map(([modelID, model]) => ({ + id: `${providerID}/${modelID}`, + provider: providerID, + model: modelID, + ...model, + })) + } + function printModels(providerID: string, verbose?: boolean) { const provider = providers[providerID] const sortedModels = Object.entries(provider.models).sort(([a], [b]) => a.localeCompare(b)) @@ -49,18 +67,7 @@ export const ModelsCommand = cmd({ } } - if (args.provider) { - const provider = providers[args.provider] - if (!provider) { - UI.error(`Provider not found: ${args.provider}`) - return - } - - printModels(args.provider, args.verbose) - return - } - - const providerIDs = Object.keys(providers).sort((a, b) => { + const targetProviderIDs = args.provider ? [args.provider] : Object.keys(providers).sort((a, b) => { const aIsOpencode = a.startsWith("opencode") const bIsOpencode = b.startsWith("opencode") if (aIsOpencode && !bIsOpencode) return -1 @@ -68,7 +75,19 @@ export const ModelsCommand = cmd({ return a.localeCompare(b) }) - for (const providerID of providerIDs) { + if (args.provider && !providers[args.provider]) { + UI.error(`Provider not found: ${args.provider}`) + return + } + + if (args.format === "json") { + const models = targetProviderIDs.flatMap(collectModels) + process.stdout.write(JSON.stringify(models, null, 2)) + process.stdout.write(EOL) + return + } + + for (const providerID of targetProviderIDs) { printModels(providerID, args.verbose) } }, From 74e42aa45e734859e81f4cdc6a7fcaab9fb5d866 Mon Sep 17 00:00:00 2001 From: shenghui kevin Date: Sat, 21 Feb 2026 05:02:57 -0800 Subject: [PATCH 3/4] fix(config): log warning when plugin path resolution fails When import.meta.resolve fails for a plugin path, the error was completely swallowed by an empty catch block. This adds a warning log so users can diagnose plugin loading issues. Co-Authored-By: Claude Opus 4.6 --- packages/opencode/src/config/config.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/packages/opencode/src/config/config.ts b/packages/opencode/src/config/config.ts index aad0fd76c4be..ee7c9f356958 100644 --- a/packages/opencode/src/config/config.ts +++ b/packages/opencode/src/config/config.ts @@ -1332,7 +1332,9 @@ export namespace Config { const plugin = data.plugin[i] try { data.plugin[i] = import.meta.resolve!(plugin, options.path) - } catch (err) {} + } catch (err) { + log.warn("failed to resolve plugin", { plugin, error: err }) + } } } return data From 058086e55aa75b20fb0d6dca9e79f18fd632e0fb Mon Sep 17 00:00:00 2001 From: shenghui kevin Date: Sat, 21 Feb 2026 05:22:46 -0800 Subject: [PATCH 4/4] fix: resolve TS2783 by spreading model before explicit properties The `model` object already contains an `id` field, so spreading it after the explicit `id` property triggers TS2783. Move the spread before the explicit properties so our composite id takes precedence. Co-Authored-By: Claude Opus 4.6 --- packages/opencode/src/cli/cmd/models.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/opencode/src/cli/cmd/models.ts b/packages/opencode/src/cli/cmd/models.ts index 44dcd4d227b3..99d80b67ed8f 100644 --- a/packages/opencode/src/cli/cmd/models.ts +++ b/packages/opencode/src/cli/cmd/models.ts @@ -47,10 +47,10 @@ export const ModelsCommand = cmd({ return Object.entries(provider.models) .sort(([a], [b]) => a.localeCompare(b)) .map(([modelID, model]) => ({ + ...model, id: `${providerID}/${modelID}`, provider: providerID, model: modelID, - ...model, })) }