From 290f7f95431316162068091ebf7c43001636ada8 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Sat, 4 Apr 2026 01:32:42 +0900 Subject: [PATCH] fix(shared): merge project and user opencode plugin detection Stop dropping user-level plugin conflicts when an empty project config exists by collecting plugin entries from every supported OpenCode config path. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- src/shared/external-plugin-detector.ts | 49 +--------------------- src/shared/load-opencode-plugins.ts | 58 ++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 48 deletions(-) create mode 100644 src/shared/load-opencode-plugins.ts diff --git a/src/shared/external-plugin-detector.ts b/src/shared/external-plugin-detector.ts index a73149d68..1ebc90967 100644 --- a/src/shared/external-plugin-detector.ts +++ b/src/shared/external-plugin-detector.ts @@ -3,15 +3,8 @@ * Used to prevent crashes from concurrent notification plugins. */ -import * as fs from "node:fs" -import * as path from "node:path" -import * as os from "node:os" +import { loadOpencodePlugins } from "./load-opencode-plugins" import { log } from "./logger" -import { parseJsoncSafe } from "./jsonc-parser" - -interface OpencodeConfig { - plugin?: string[] -} /** * Known notification plugins that conflict with oh-my-opencode's session-notification. @@ -34,46 +27,6 @@ const KNOWN_SKILL_PLUGINS = [ "@opencode/skills", ] -function getWindowsAppdataDir(): string | null { - return process.env.APPDATA || null -} - -function getConfigPaths(directory: string): string[] { - const crossPlatformDir = path.join(os.homedir(), ".config") - const paths = [ - path.join(directory, ".opencode", "opencode.json"), - path.join(directory, ".opencode", "opencode.jsonc"), - path.join(crossPlatformDir, "opencode", "opencode.json"), - path.join(crossPlatformDir, "opencode", "opencode.jsonc"), - ] - - if (process.platform === "win32") { - const appdataDir = getWindowsAppdataDir() - if (appdataDir) { - paths.push(path.join(appdataDir, "opencode", "opencode.json")) - paths.push(path.join(appdataDir, "opencode", "opencode.jsonc")) - } - } - - return paths -} - -function loadOpencodePlugins(directory: string): string[] { - for (const configPath of getConfigPaths(directory)) { - try { - if (!fs.existsSync(configPath)) continue - const content = fs.readFileSync(configPath, "utf-8") - const result = parseJsoncSafe(content) - if (result.data) { - return result.data.plugin ?? [] - } - } catch { - continue - } - } - return [] -} - /** * Check if a plugin entry matches a known notification plugin. * Handles various formats: "name", "name@version", "npm:name", "file://path/name" diff --git a/src/shared/load-opencode-plugins.ts b/src/shared/load-opencode-plugins.ts new file mode 100644 index 000000000..607333e07 --- /dev/null +++ b/src/shared/load-opencode-plugins.ts @@ -0,0 +1,58 @@ +import * as fs from "node:fs" +import * as os from "node:os" +import * as path from "node:path" + +import { parseJsoncSafe } from "./jsonc-parser" + +interface OpencodeConfig { + plugin?: string[] +} + +function getWindowsAppdataDir(): string | null { + return process.env.APPDATA || null +} + +function getConfigPaths(directory: string): string[] { + const crossPlatformDir = path.join(os.homedir(), ".config") + const paths = [ + path.join(directory, ".opencode", "opencode.json"), + path.join(directory, ".opencode", "opencode.jsonc"), + path.join(crossPlatformDir, "opencode", "opencode.json"), + path.join(crossPlatformDir, "opencode", "opencode.jsonc"), + ] + + if (process.platform === "win32") { + const appdataDir = getWindowsAppdataDir() + if (appdataDir) { + paths.push(path.join(appdataDir, "opencode", "opencode.json")) + paths.push(path.join(appdataDir, "opencode", "opencode.jsonc")) + } + } + + return paths +} + +export function loadOpencodePlugins(directory: string): string[] { + const pluginEntries: string[] = [] + const seenPluginEntries = new Set() + + for (const configPath of getConfigPaths(directory)) { + try { + if (!fs.existsSync(configPath)) continue + + const content = fs.readFileSync(configPath, "utf-8") + const result = parseJsoncSafe(content) + const plugins = result.data?.plugin ?? [] + + for (const plugin of plugins) { + if (seenPluginEntries.has(plugin)) continue + seenPluginEntries.add(plugin) + pluginEntries.push(plugin) + } + } catch { + continue + } + } + + return pluginEntries +}