From 5f5cd080b978b226ab752fd57e1270850ec5889f Mon Sep 17 00:00:00 2001 From: Mario Zechner Date: Thu, 5 Feb 2026 22:32:12 +0100 Subject: [PATCH] fix(coding-agent): respect package.json pi.extensions manifest in settings extensions paths collectAutoExtensionEntries now checks if the directory itself has a package.json with pi.extensions (or index.ts) before scanning children. This fixes duplicate extension loading when a manifest-aware directory is specified directly in settings.json extensions array. Fixes #1274 --- packages/coding-agent/CHANGELOG.md | 4 +++ .../coding-agent/src/core/package-manager.ts | 7 ++++ .../coding-agent/test/package-manager.test.ts | 34 +++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/packages/coding-agent/CHANGELOG.md b/packages/coding-agent/CHANGELOG.md index 195be1d9..a214a6ef 100644 --- a/packages/coding-agent/CHANGELOG.md +++ b/packages/coding-agent/CHANGELOG.md @@ -2,6 +2,10 @@ ## [Unreleased] +### Fixed + +- Fixed extensions setting not respecting `package.json` `pi.extensions` manifest when directory is specified directly ([#1302](https://github.com/badlogic/pi-mono/pull/1302) by [@hjanuschka](https://github.com/hjanuschka)) + ## [0.52.3] - 2026-02-05 ### Fixed diff --git a/packages/coding-agent/src/core/package-manager.ts b/packages/coding-agent/src/core/package-manager.ts index bfeecd0c..bbb8faa7 100644 --- a/packages/coding-agent/src/core/package-manager.ts +++ b/packages/coding-agent/src/core/package-manager.ts @@ -402,6 +402,13 @@ function collectAutoExtensionEntries(dir: string): string[] { const entries: string[] = []; if (!existsSync(dir)) return entries; + // First check if this directory itself has explicit extension entries (package.json or index) + const rootEntries = resolveExtensionEntries(dir); + if (rootEntries) { + return rootEntries; + } + + // Otherwise, discover extensions from directory contents const ig = ignore(); addIgnoreRules(ig, dir, dir); diff --git a/packages/coding-agent/test/package-manager.test.ts b/packages/coding-agent/test/package-manager.test.ts index 061f04bc..2c7b1bf2 100644 --- a/packages/coding-agent/test/package-manager.test.ts +++ b/packages/coding-agent/test/package-manager.test.ts @@ -111,6 +111,40 @@ Content`, const result = await packageManager.resolve(); expect(result.prompts.some((r) => r.path === promptPath && !r.enabled)).toBe(true); }); + + it("should resolve directory with package.json pi.extensions in extensions setting", async () => { + // Create a package with pi.extensions in package.json + const pkgDir = join(tempDir, "my-extensions-pkg"); + mkdirSync(join(pkgDir, "extensions"), { recursive: true }); + writeFileSync( + join(pkgDir, "package.json"), + JSON.stringify({ + name: "my-extensions-pkg", + pi: { + extensions: ["./extensions/clip.ts", "./extensions/cost.ts"], + }, + }), + ); + writeFileSync(join(pkgDir, "extensions", "clip.ts"), "export default function() {}"); + writeFileSync(join(pkgDir, "extensions", "cost.ts"), "export default function() {}"); + writeFileSync(join(pkgDir, "extensions", "helper.ts"), "export const x = 1;"); // Not in manifest, shouldn't be loaded + + // Add the directory to extensions setting (not packages setting) + settingsManager.setExtensionPaths([pkgDir]); + + const result = await packageManager.resolve(); + + // Should find the extensions declared in package.json pi.extensions + expect(result.extensions.some((r) => r.path === join(pkgDir, "extensions", "clip.ts") && r.enabled)).toBe( + true, + ); + expect(result.extensions.some((r) => r.path === join(pkgDir, "extensions", "cost.ts") && r.enabled)).toBe( + true, + ); + + // Should NOT find helper.ts (not declared in manifest) + expect(result.extensions.some((r) => r.path.endsWith("helper.ts"))).toBe(false); + }); }); describe("ignore files", () => {