From 182d4ceea33beabe7c4712b04f1f5459e613de44 Mon Sep 17 00:00:00 2001 From: Neon <10284121+neonspectra@users.noreply.github.com> Date: Sat, 18 Apr 2026 13:16:39 +0200 Subject: [PATCH] fix manifest glob expansion and add regression test (#3350) --- .../coding-agent/src/core/package-manager.ts | 28 ++++++++++++++++--- .../coding-agent/test/package-manager.test.ts | 27 ++++++++++++++++++ 2 files changed, 51 insertions(+), 4 deletions(-) diff --git a/packages/coding-agent/src/core/package-manager.ts b/packages/coding-agent/src/core/package-manager.ts index 0100e247..e4f9311b 100644 --- a/packages/coding-agent/src/core/package-manager.ts +++ b/packages/coding-agent/src/core/package-manager.ts @@ -4,6 +4,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync, statSync, wri import { homedir, tmpdir } from "node:os"; import { basename, dirname, join, relative, resolve, sep } from "node:path"; import type { Readable } from "node:stream"; +import { globSync } from "glob"; import ignore from "ignore"; import { minimatch } from "minimatch"; import { CONFIG_DIR_NAME } from "../config.js"; @@ -215,6 +216,14 @@ function isPattern(s: string): boolean { return s.startsWith("!") || s.startsWith("+") || s.startsWith("-") || s.includes("*") || s.includes("?"); } +function isOverridePattern(s: string): boolean { + return s.startsWith("!") || s.startsWith("+") || s.startsWith("-"); +} + +function hasGlobPattern(s: string): boolean { + return s.includes("*") || s.includes("?"); +} + function splitPatterns(entries: string[]): { plain: string[]; patterns: string[] } { const plain: string[] = []; const patterns: string[] = []; @@ -1897,7 +1906,7 @@ export class DefaultPackageManager implements PackageManager { const entries = manifest?.[resourceType as keyof PiManifest]; if (entries && entries.length > 0) { const allFiles = this.collectFilesFromManifestEntries(entries, packageRoot, resourceType); - const manifestPatterns = entries.filter(isPattern); + const manifestPatterns = entries.filter(isOverridePattern); const enabledByManifest = manifestPatterns.length > 0 ? applyPatterns(allFiles, manifestPatterns, packageRoot) : new Set(allFiles); return { allFiles: Array.from(enabledByManifest), enabledByManifest }; @@ -1936,7 +1945,7 @@ export class DefaultPackageManager implements PackageManager { if (!entries) return; const allFiles = this.collectFilesFromManifestEntries(entries, root, resourceType); - const patterns = entries.filter(isPattern); + const patterns = entries.filter(isOverridePattern); const enabledPaths = applyPatterns(allFiles, patterns, root); for (const f of allFiles) { @@ -1947,8 +1956,19 @@ export class DefaultPackageManager implements PackageManager { } private collectFilesFromManifestEntries(entries: string[], root: string, resourceType: ResourceType): string[] { - const plain = entries.filter((entry) => !isPattern(entry)); - const resolved = plain.map((entry) => resolve(root, entry)); + const sourceEntries = entries.filter((entry) => !isOverridePattern(entry)); + const resolved = sourceEntries.flatMap((entry) => { + if (!hasGlobPattern(entry)) { + return [resolve(root, entry)]; + } + + return globSync(entry, { + cwd: root, + absolute: true, + dot: false, + nodir: false, + }).map((match) => resolve(match)); + }); return this.collectFilesFromPaths(resolved, resourceType); } diff --git a/packages/coding-agent/test/package-manager.test.ts b/packages/coding-agent/test/package-manager.test.ts index e8521ac9..82a7a6fd 100644 --- a/packages/coding-agent/test/package-manager.test.ts +++ b/packages/coding-agent/test/package-manager.test.ts @@ -884,6 +884,33 @@ Content`, expect(result.skills.some((r) => isEnabled(r, "good-skill", "includes"))).toBe(true); expect(result.skills.some((r) => r.path.includes("bad-skill"))).toBe(false); }); + + it("should expand positive glob manifest entries before collecting skills", async () => { + const pkgDir = join(tempDir, "skill-manifest-glob-pkg"); + mkdirSync(join(pkgDir, "plugins/pdf-to-markdown/skills/pdf-to-markdown"), { recursive: true }); + mkdirSync(join(pkgDir, "plugins/nutrient-dws/skills/document-processor-api"), { recursive: true }); + writeFileSync( + join(pkgDir, "plugins/pdf-to-markdown/skills/pdf-to-markdown", "SKILL.md"), + "---\nname: pdf-to-markdown\ndescription: PDF to Markdown\n---\nContent", + ); + writeFileSync( + join(pkgDir, "plugins/nutrient-dws/skills/document-processor-api", "SKILL.md"), + "---\nname: document-processor-api\ndescription: DWS\n---\nContent", + ); + writeFileSync( + join(pkgDir, "package.json"), + JSON.stringify({ + name: "skill-manifest-glob-pkg", + pi: { + skills: ["./plugins/*/skills"], + }, + }), + ); + + const result = await packageManager.resolveExtensionSources([pkgDir]); + expect(result.skills.some((r) => isEnabled(r, "pdf-to-markdown", "includes"))).toBe(true); + expect(result.skills.some((r) => isEnabled(r, "document-processor-api", "includes"))).toBe(true); + }); }); describe("pattern filtering in package filters", () => {