diff --git a/assets/oh-my-opencode.schema.json b/assets/oh-my-opencode.schema.json index d43ff4501..607988931 100644 --- a/assets/oh-my-opencode.schema.json +++ b/assets/oh-my-opencode.schema.json @@ -36,7 +36,9 @@ "agent-browser", "dev-browser", "frontend-ui-ux", - "git-master" + "git-master", + "review-work", + "ai-slop-remover" ] } }, diff --git a/src/shared/external-plugin-detector.test.ts b/src/shared/external-plugin-detector.test.ts index a220dff90..64c27e2d3 100644 --- a/src/shared/external-plugin-detector.test.ts +++ b/src/shared/external-plugin-detector.test.ts @@ -102,6 +102,32 @@ describe("external-plugin-detector", () => { expect(result.pluginName).toContain("opencode-notifier") }) + test("should safely handle tuple-format plugin entries without crashing (fixes #3122)", () => { + // given - opencode.json with array/tuple plugin entries + const opencodeDir = path.join(tempDir, ".opencode") + fs.mkdirSync(opencodeDir, { recursive: true }) + fs.writeFileSync( + path.join(opencodeDir, "opencode.json"), + JSON.stringify({ + plugin: [ + "oh-my-opencode", + ["advanced-tuple-plugin", { debug: true }], + "opencode-notifier" + ] + }) + ) + + // when + const result = detectExternalNotificationPlugin(tempDir) + + // then - should detect opencode-notifier without crashing on the tuple entry + expect(result.detected).toBe(true) + expect(result.pluginName).toBe("opencode-notifier") + expect(result.allPlugins).toContain("oh-my-opencode") + expect(result.allPlugins).toContain("advanced-tuple-plugin") + expect(result.allPlugins).not.toContain(["advanced-tuple-plugin", { debug: true }]) + }) + test("should handle JSONC format with comments", () => { // given - opencode.jsonc with comments const opencodeDir = path.join(tempDir, ".opencode") diff --git a/src/shared/load-opencode-plugins.ts b/src/shared/load-opencode-plugins.ts index 607333e07..5517c74b1 100644 --- a/src/shared/load-opencode-plugins.ts +++ b/src/shared/load-opencode-plugins.ts @@ -5,7 +5,7 @@ import * as path from "node:path" import { parseJsoncSafe } from "./jsonc-parser" interface OpencodeConfig { - plugin?: string[] + plugin?: (string | [string, ...unknown[]])[] } function getWindowsAppdataDir(): string | null { @@ -44,7 +44,9 @@ export function loadOpencodePlugins(directory: string): string[] { const result = parseJsoncSafe(content) const plugins = result.data?.plugin ?? [] - for (const plugin of plugins) { + for (const rawPlugin of plugins) { + const plugin = typeof rawPlugin === "string" ? rawPlugin : Array.isArray(rawPlugin) ? rawPlugin[0] : null + if (typeof plugin !== "string") continue if (seenPluginEntries.has(plugin)) continue seenPluginEntries.add(plugin) pluginEntries.push(plugin)