From 6947ab18dc636e88055d15044b39db07c7e475d0 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Mon, 12 Jan 2026 01:25:39 +0000 Subject: [PATCH] fix: load plugin packages from config dirs --- src/plugins/discovery.test.ts | 28 ++++++++++++++++++++++++ src/plugins/discovery.ts | 40 +++++++++++++++++++++++++++++++++++ 2 files changed, 68 insertions(+) diff --git a/src/plugins/discovery.test.ts b/src/plugins/discovery.test.ts index aa6e536c8..c6010cc59 100644 --- a/src/plugins/discovery.test.ts +++ b/src/plugins/discovery.test.ts @@ -131,4 +131,32 @@ describe("discoverClawdbotPlugins", () => { const ids = candidates.map((c) => c.idHint); expect(ids).toContain("voice-call"); }); + + it("treats configured directory paths as plugin packages", async () => { + const stateDir = makeTempDir(); + const packDir = path.join(stateDir, "packs", "demo-plugin-dir"); + fs.mkdirSync(packDir, { recursive: true }); + + fs.writeFileSync( + path.join(packDir, "package.json"), + JSON.stringify({ + name: "@clawdbot/demo-plugin-dir", + clawdbot: { extensions: ["./index.js"] }, + }), + "utf-8", + ); + fs.writeFileSync( + path.join(packDir, "index.js"), + "module.exports = {}", + "utf-8", + ); + + const { candidates } = await withStateDir(stateDir, async () => { + const { discoverClawdbotPlugins } = await import("./discovery.js"); + return discoverClawdbotPlugins({ extraPaths: [packDir] }); + }); + + const ids = candidates.map((c) => c.idHint); + expect(ids).toContain("demo-plugin-dir"); + }); }); diff --git a/src/plugins/discovery.ts b/src/plugins/discovery.ts index adaed274e..1bda8482d 100644 --- a/src/plugins/discovery.ts +++ b/src/plugins/discovery.ts @@ -214,6 +214,46 @@ function discoverFromPath(params: { } if (stat.isDirectory()) { + const manifest = readPackageManifest(resolved); + const extensions = manifest ? resolvePackageExtensions(manifest) : []; + + if (extensions.length > 0) { + for (const extPath of extensions) { + const source = path.resolve(resolved, extPath); + addCandidate({ + candidates: params.candidates, + seen: params.seen, + idHint: deriveIdHint({ + filePath: source, + packageName: manifest?.name, + hasMultipleExtensions: extensions.length > 1, + }), + source, + origin: params.origin, + workspaceDir: params.workspaceDir, + manifest, + }); + } + return; + } + + const indexCandidates = ["index.ts", "index.js", "index.mjs", "index.cjs"]; + const indexFile = indexCandidates + .map((candidate) => path.join(resolved, candidate)) + .find((candidate) => fs.existsSync(candidate)); + + if (indexFile && isExtensionFile(indexFile)) { + addCandidate({ + candidates: params.candidates, + seen: params.seen, + idHint: path.basename(resolved), + source: indexFile, + origin: params.origin, + workspaceDir: params.workspaceDir, + }); + return; + } + discoverInDirectory({ dir: resolved, origin: params.origin,