From b73dbff64b9b895ddc6f8d1e789a88c10f2a3992 Mon Sep 17 00:00:00 2001 From: licat <1834199672@qq.com> Date: Mon, 22 Jun 2026 23:22:27 +0800 Subject: [PATCH] feat(opencode): discover plugin package directories under plugins/ --- packages/opencode/src/config/plugin.ts | 21 ++++++++++++++++++++- packages/opencode/src/plugin/shared.ts | 2 +- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/packages/opencode/src/config/plugin.ts b/packages/opencode/src/config/plugin.ts index 60bba4d63634..9da618179924 100644 --- a/packages/opencode/src/config/plugin.ts +++ b/packages/opencode/src/config/plugin.ts @@ -1,7 +1,7 @@ import { Glob } from "@opencode-ai/core/util/glob" import { ConfigPluginV1 } from "@opencode-ai/core/v1/config/plugin" import { pathToFileURL } from "url" -import { isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared" +import { INDEX_FILES, isPathPluginSpec, parsePluginSpecifier, resolvePathPluginTarget } from "@/plugin/shared" import path from "path" export type Scope = "global" | "local" @@ -26,6 +26,25 @@ export async function load(dir: string) { })) { plugins.push(pathToFileURL(item).href) } + + // Discover plugin packages dropped as immediate subdirectories under plugin/ or plugins/. + // A subdirectory qualifies only when it looks like a plugin (a package.json or an index file), and we + // emit the directory URL so the loader resolves the real entrypoint via exports/main or the index file, + // matching how a directory plugin declared in config is resolved. The single `*` segment keeps this at + // depth one, so package internals like src/ or node_modules/ are never picked up as separate plugins. + const seen = new Set() + for (const item of await Glob.scan(`{plugin,plugins}/*/{package.json,${INDEX_FILES.join(",")}}`, { + cwd: dir, + absolute: true, + dot: true, + symlink: true, + })) { + const url = pathToFileURL(path.dirname(item)).href + if (seen.has(url)) continue + seen.add(url) + plugins.push(url) + } + return plugins } diff --git a/packages/opencode/src/plugin/shared.ts b/packages/opencode/src/plugin/shared.ts index 1a519359bde6..b7be17c18b73 100644 --- a/packages/opencode/src/plugin/shared.ts +++ b/packages/opencode/src/plugin/shared.ts @@ -51,7 +51,7 @@ export type PluginEntry = { entry?: string } -const INDEX_FILES = ["index.ts", "index.tsx", "index.js", "index.mjs", "index.cjs"] +export const INDEX_FILES = ["index.ts", "index.tsx", "index.js", "index.mjs", "index.cjs"] export function pluginSource(spec: string): PluginSource { if (isPathPluginSpec(spec)) return "file"