From f2a4011371913ce1cea12efd9e121ed297639069 Mon Sep 17 00:00:00 2001 From: "opencode-agent[bot]" <219766164+opencode-agent[bot]@users.noreply.github.com> Date: Fri, 17 Jul 2026 13:42:44 -0500 Subject: [PATCH] fix(cli): restore plugin list diagnostics (#37540) Co-authored-by: Aiden Cline --- packages/cli/src/commands/commands.ts | 4 ++++ .../cli/src/commands/handlers/plugin/list.ts | 24 +++++++++++++++++++ packages/cli/src/index.ts | 3 +++ packages/core/src/plugin/supervisor.ts | 6 ++++- packages/core/test/config/plugin.test.ts | 21 ++++++++++++---- 5 files changed, 52 insertions(+), 6 deletions(-) create mode 100644 packages/cli/src/commands/handlers/plugin/list.ts diff --git a/packages/cli/src/commands/commands.ts b/packages/cli/src/commands/commands.ts index 89b109c961..771f57f42a 100644 --- a/packages/cli/src/commands/commands.ts +++ b/packages/cli/src/commands/commands.ts @@ -114,6 +114,10 @@ export const Commands = Spec.make(typeof OPENCODE_CLI_NAME === "string" ? OPENCO }), ], }), + Spec.make("plugin", { + description: "Manage plugins", + commands: [Spec.make("list", { description: "List active plugins" })], + }), Spec.make("migrate", { description: "Migrate v1 data to v2" }), Spec.make("mini", { description: "Start the minimal interactive interface", diff --git a/packages/cli/src/commands/handlers/plugin/list.ts b/packages/cli/src/commands/handlers/plugin/list.ts new file mode 100644 index 0000000000..f7f9a258ca --- /dev/null +++ b/packages/cli/src/commands/handlers/plugin/list.ts @@ -0,0 +1,24 @@ +import { EOL } from "node:os" +import { Effect } from "effect" +import { OpenCode } from "@opencode-ai/client" +import { Service } from "@opencode-ai/client/effect/service" +import { Commands } from "../../commands" +import { Runtime } from "../../../framework/runtime" +import { ServiceConfig } from "../../../services/service-config" + +export default Runtime.handler( + Commands.commands.plugin.commands.list, + Effect.fn("cli.plugin.list")(function* () { + const options = yield* ServiceConfig.options() + const found = yield* Service.discover(options) + const endpoint = found ?? (yield* Service.ensure(options)) + const client = OpenCode.make({ baseUrl: endpoint.url, headers: Service.headers(endpoint) }) + const response = yield* Effect.promise(() => client.plugin.list({ location: { directory: process.cwd() } })) + const plugins = response.data.toSorted((a, b) => a.id.localeCompare(b.id)) + if (plugins.length === 0) { + process.stdout.write("No plugins loaded" + EOL) + return + } + process.stdout.write(plugins.map((plugin) => plugin.id).join(EOL) + EOL) + }), +) diff --git a/packages/cli/src/index.ts b/packages/cli/src/index.ts index 9346dbe35f..04f3b5aa50 100755 --- a/packages/cli/src/index.ts +++ b/packages/cli/src/index.ts @@ -32,6 +32,9 @@ const Handlers = Runtime.handlers(Commands, { auth: () => import("./commands/handlers/mcp/auth"), logout: () => import("./commands/handlers/mcp/logout"), }, + plugin: { + list: () => import("./commands/handlers/plugin/list"), + }, migrate: () => import("./commands/handlers/migrate"), mini: () => import("./commands/handlers/mini"), run: () => import("./commands/handlers/run"), diff --git a/packages/core/src/plugin/supervisor.ts b/packages/core/src/plugin/supervisor.ts index 382474cdd1..ee91e4cfc3 100644 --- a/packages/core/src/plugin/supervisor.ts +++ b/packages/core/src/plugin/supervisor.ts @@ -142,7 +142,11 @@ const resolve = Effect.fn("PluginSupervisor.resolve")(function* ( continue } - const plugin = yield* load(operation).pipe(Effect.catchCause(() => Effect.succeed(undefined))) + const plugin = yield* load(operation).pipe( + Effect.catchCause((cause) => + Effect.logWarning("failed to load plugin", { target: operation.target, cause }).pipe(Effect.as(undefined)), + ), + ) if (!plugin) continue const previous = packages.get(operation.target) if (previous) enabled.delete(previous.id) diff --git a/packages/core/test/config/plugin.test.ts b/packages/core/test/config/plugin.test.ts index 626e94f758..9d68b5f408 100644 --- a/packages/core/test/config/plugin.test.ts +++ b/packages/core/test/config/plugin.test.ts @@ -18,7 +18,7 @@ import { PluginSupervisor } from "@opencode-ai/core/plugin/supervisor" import { ModelV2 } from "@opencode-ai/core/model" import { ProviderV2 } from "@opencode-ai/core/provider" import { AbsolutePath } from "@opencode-ai/core/schema" -import { Effect } from "effect" +import { Effect, Logger } from "effect" import { Database } from "../../src/database/database" import { tmpdir } from "../fixture/tmpdir" import { testEffect } from "../lib/effect" @@ -111,8 +111,15 @@ describe("PluginSupervisor config", () => { ), ) - it.live("ignores invalid packages and continues loading", () => - withLocation( + it.live("logs invalid packages and continues loading", () => { + const output: string[] = [] + const logger = Logger.map(Logger.formatStructured, (entry) => { + if (!Array.isArray(entry.message) || entry.message[0] !== "failed to load plugin") return + const details = entry.message[1] + if (typeof details !== "object" || details === null || !("target" in details)) return + if (typeof details.target === "string") output.push(details.target) + }) + return withLocation( { plugins: [ "-*", @@ -130,9 +137,13 @@ describe("PluginSupervisor config", () => { expect(yield* agents.get(AgentV2.ID.make("configured"))).toMatchObject({ description: "Loaded after invalid plugins", }) + expect(output).toEqual([ + path.join(import.meta.dir, "../plugin/fixtures/missing-plugin.ts"), + path.join(import.meta.dir, "../plugin/fixtures/invalid-plugin.ts"), + ]) }), - ), - ) + ).pipe(Effect.provide(Logger.layer([logger]))) + }) it.live("loads auto-discovered plugin files", () => withLocation(