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 | [string, ...unknown[]])[] } const opencodePluginsCache = new Map() 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 cachedPluginEntries = opencodePluginsCache.get(directory) if (cachedPluginEntries) { return cachedPluginEntries } 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 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) } } catch { continue } } opencodePluginsCache.set(directory, pluginEntries) return pluginEntries } export function clearOpencodePluginsCache(): void { opencodePluginsCache.clear() }