import { spawnSync } from "node:child_process"; import { existsSync, mkdirSync, mkdtempSync, readdirSync, readFileSync, rmSync, writeFileSync, } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { afterAll, beforeAll, describe, expect, it } from "vitest"; import { buildExtension, ROOT, rootVersion } from "./build.ts"; import { registrationConflicts } from "./conflicts.ts"; import { BUILD_MANIFEST, listFiles, publishPackage } from "./publish.ts"; import { readBuildSpec } from "./spec.ts"; import { forbiddenImports, moduleImports, verifyCollection, verifyPackage, } from "./verify.ts"; const PI_PACKAGE_ROOT = join( ROOT, "node_modules", "@earendil-works", "pi-coding-agent", ); function temp(prefix: string): string { return mkdtempSync(join(tmpdir(), prefix)); } describe("ultra package", () => { let distRoot: string; let pkg: string; beforeAll(async () => { distRoot = temp("pi-ext-dist-"); pkg = (await buildExtension("ultra", { distRoot })).dir; }, 60_000); afterAll(() => { rmSync(distRoot, { recursive: true, force: true }); }); it("emits a complete self-contained package with declared resources", () => { const files = listFiles(pkg); expect(files).toContain("index.js"); expect(files).toContain("dynamic-validator-worker.js"); expect(files).toContain("package.json"); expect(files).toContain("prompts/ultra-authoring/SKILL.md"); expect(files).toContain("workflows/feature.json"); expect(files.some((f) => /^implementation-[a-z0-9]+\.js$/u.test(f))).toBe( true, ); expect(files.some((f) => f.includes("/node_modules/"))).toBe(false); expect(existsSync(join(distRoot, ".staging"))).toBe(false); const manifest = JSON.parse( readFileSync(join(pkg, "package.json"), "utf8"), ); expect(manifest).toMatchObject({ name: "@bugabinga/pi-ext-ultra", version: rootVersion(), private: true, type: "module", main: "index.js", pi: { extensions: ["index.js"] }, }); expect(manifest.scripts).toBeUndefined(); expect(manifest.dependencies).toBeUndefined(); expect(Object.keys(manifest.peerDependencies)).toEqual( expect.arrayContaining(["@earendil-works/pi-coding-agent", "typebox"]), ); }); it("keeps the lazy runtime out of the entry chunk", () => { const entry = readFileSync(join(pkg, "index.js"), "utf8"); expect(entry).not.toContain("unknown sub-agent extension"); expect(moduleImports("index.js", entry)).toEqual( expect.arrayContaining([ expect.stringMatching(/^\.\/implementation-[a-z0-9]+\.js$/u), ]), ); }); it("imports only Node built-ins, Pi modules, and package-private chunks", () => { expect(forbiddenImports(pkg)).toEqual([]); }); it("loads relocated outside the checkout, activates the lazy runtime, and exposes the authoring skill", async () => { const report = await verifyPackage(pkg, { userSettings: { ultra: { autoEnable: true } }, }); expect(report.errors).toEqual([]); expect(report.ok).toBe(true); expect(report.extensions).toHaveLength(1); const [ultra] = report.extensions; expect(ultra.path).not.toContain(ROOT); expect(ultra.commands).toContain("ultra"); expect(ultra.tools).toContain("run_workflow"); expect(ultra.activeTools).toContain("run_workflow"); expect(report.skills).toEqual([ expect.stringMatching(/\/prompts\/ultra-authoring\/SKILL\.md$/u), ]); expect(report.skills[0]).not.toContain(ROOT); }, 120_000); it("runs the validator worker from a relocated package against the running Pi", () => { const root = temp("pi-ext-worker-"); try { const relocated = join(root, "ultra"); spawnSync("cp", ["-r", pkg, relocated]); const draft = join(root, "draft"); mkdirSync(draft); writeFileSync( join(draft, "manifest.json"), JSON.stringify({ name: "hello-tool", description: "says hello" }), ); writeFileSync( join(draft, "index.ts"), `export default function (pi) { pi.registerTool({ name: "hello_tool", label: "Hello", description: "hello", parameters: { type: "object", properties: {} }, async execute() { return { content: [{ type: "text", text: "hi" }], details: {} }; }, }); } `, ); const result = spawnSync( process.execPath, [ "--no-warnings", join(relocated, "dynamic-validator-worker.js"), draft, ], { encoding: "utf8", env: { ...process.env, ULTRA_PI_PACKAGE_ROOT: PI_PACKAGE_ROOT, PI_CODING_AGENT_DIR: join(root, "agent"), }, timeout: 60_000, }, ); const line = result.stdout .split("\n") .find((candidate) => candidate.startsWith("ULTRA_VALIDATION_RESULT=")); expect(line, result.stdout + result.stderr).toBeDefined(); const report = JSON.parse( (line ?? "").slice("ULTRA_VALIDATION_RESULT=".length), ); expect(report.diagnostics).toEqual([]); expect(report.ok).toBe(true); } finally { rmSync(root, { recursive: true, force: true }); } }, 90_000); }); describe("the-system package", () => { let distRoot: string; let pkg: string; beforeAll(async () => { distRoot = temp("pi-ext-dist-"); pkg = (await buildExtension("the-system", { distRoot })).dir; }, 60_000); afterAll(() => { rmSync(distRoot, { recursive: true, force: true }); }); it("bundles every third-party dependency and ships skills plus browser assets", () => { const files = listFiles(pkg); expect(files).toContain("skills/vertical-slices/SKILL.md"); expect(files).toContain("skills/the-system-mermaid/SKILL.md"); expect(files).toContain("assets/mermaid.min.js"); expect(files).toContain("assets/minisearch.js"); expect(files.some((f) => f.startsWith("node_modules/"))).toBe(false); expect(forbiddenImports(pkg)).toEqual([]); const manifest = JSON.parse( readFileSync(join(pkg, "package.json"), "utf8"), ); expect(manifest.dependencies).toBeUndefined(); const renderer = files.find((f) => /^system-renderer-[a-z0-9]+\.js$/u.test(f), ); expect(renderer).toBeDefined(); expect(readFileSync(join(pkg, renderer ?? ""), "utf8")).toContain( "./assets/", ); }); it("loads relocated with its skills served from the package", async () => { const report = await verifyPackage(pkg); expect(report.errors).toEqual([]); expect(report.ok).toBe(true); expect(report.extensions[0]?.commands).toContain("system"); expect(report.skills).toEqual([ expect.stringMatching(/\/skills\/vertical-slices\/SKILL\.md$/u), ]); expect(report.skills[0]).not.toContain(ROOT); }, 120_000); }); describe("collection", () => { it("reports identity, tool, command, flag, and skill conflicts", () => { expect( registrationConflicts( [ { path: "/a/index.js", tools: ["x"], commands: ["go"], flags: [] }, { path: "/b/index.js", tools: ["x"], commands: [], flags: ["f"] }, { path: "/c/index.js", tools: [], commands: ["go"], flags: ["f"] }, ], ["@p/one", "@p/two", "@p/one"], ["/a/skills/s/SKILL.md", "/b/skills/s/SKILL.md"], ), ).toEqual([ "command go registered by /a/index.js, /c/index.js", "flag f registered by /b/index.js, /c/index.js", "package @p/one registered by @p/one, @p/one", "skill s registered by /a/skills/s/SKILL.md, /b/skills/s/SKILL.md", "tool x registered by /a/index.js, /b/index.js", ]); expect( registrationConflicts( [{ path: "/a/index.js", tools: ["x"], commands: ["go"], flags: [] }], ["@p/one"], ["/a/skills/s/SKILL.md"], ), ).toEqual([]); }); it("loads several generated packages together without conflicts", async () => { const distRoot = temp("pi-ext-dist-"); try { const dirs: string[] = []; for (const name of ["pfui", "footer", "ultra", "nushell"]) dirs.push((await buildExtension(name, { distRoot })).dir); const report = await verifyCollection(dirs); expect(report.errors).toEqual([]); expect(report.conflicts).toEqual([]); expect(report.ok).toBe(true); expect(report.extensions.map((e) => e.path.split("/").at(-2))).toEqual([ "pfui", "footer", "ultra", "nushell", ]); } finally { rmSync(distRoot, { recursive: true, force: true }); } }, 180_000); }); describe("publishPackage", () => { function stage(root: string, files: Record): string { const staging = join( root, `staging-${Math.random().toString(36).slice(2)}`, ); for (const [name, content] of Object.entries(files)) { mkdirSync(join(staging, name, ".."), { recursive: true }); writeFileSync(join(staging, name), content); } return staging; } it("keeps previous chunks for one generation and switches entries last", () => { const root = temp("pi-ext-publish-"); try { const target = join(root, "dist", "demo"); publishPackage( stage(root, { "index.js": "import './chunk-a.js'", "chunk-a.js": "a", "prompts/one.md": "one", "package.json": "{}", }), target, ["index.js"], ); expect(listFiles(target)).toEqual([ BUILD_MANIFEST, "chunk-a.js", "index.js", "package.json", "prompts/one.md", ]); publishPackage( stage(root, { "index.js": "import './chunk-b.js'", "chunk-b.js": "b", "package.json": "{}", }), target, ["index.js"], ); expect(listFiles(target)).toEqual([ BUILD_MANIFEST, "chunk-a.js", "chunk-b.js", "index.js", "package.json", "prompts/one.md", ]); expect(readFileSync(join(target, "index.js"), "utf8")).toBe( "import './chunk-b.js'", ); publishPackage( stage(root, { "index.js": "import './chunk-c.js'", "chunk-c.js": "c", "package.json": "{}", }), target, ["index.js"], ); expect(listFiles(target)).toEqual([ BUILD_MANIFEST, "chunk-b.js", "chunk-c.js", "index.js", "package.json", ]); expect(readdirSync(root).filter((n) => n.startsWith("staging-"))).toEqual( [], ); } finally { rmSync(root, { recursive: true, force: true }); } }); }); describe("buildExtension", () => { function fakeRepo(): { root: string; ext: string } { const root = temp("pi-ext-repo-"); writeFileSync( join(root, "package.json"), JSON.stringify({ name: "fake", version: "9.9.9" }), ); const ext = join(root, "extensions", "demo"); mkdirSync(ext, { recursive: true }); writeFileSync( join(ext, "package.json"), JSON.stringify({ name: "@bugabinga/pi-ext-demo", description: "demo", license: "MIT", peerDependencies: { "@earendil-works/pi-coding-agent": "*" }, }), ); writeFileSync( join(ext, "index.ts"), 'import { join } from "node:path";\nexport default function demo() { return join("a", "b"); }\n', ); return { root, ext }; } it("leaves the previous package intact when a rebuild fails", async () => { const { root, ext } = fakeRepo(); try { const built = await buildExtension("demo", { root }); const before = listFiles(built.dir).map((f) => [ f, readFileSync(join(built.dir, f), "utf8"), ]); expect( JSON.parse(readFileSync(join(built.dir, "package.json"), "utf8")), ).toMatchObject({ version: "9.9.9", pi: { extensions: ["index.js"] } }); writeFileSync(join(ext, "index.ts"), 'import "./missing.ts";\n'); await expect(buildExtension("demo", { root })).rejects.toThrow(); const after = listFiles(built.dir).map((f) => [ f, readFileSync(join(built.dir, f), "utf8"), ]); expect(after).toEqual(before); expect(existsSync(join(root, "dist", ".staging"))).toBe(false); } finally { rmSync(root, { recursive: true, force: true }); } }); it("rejects undeclared or missing build declarations", () => { const { root, ext } = fakeRepo(); try { writeFileSync( join(ext, "package.json"), JSON.stringify({ name: "@bugabinga/pi-ext-demo", "pi-ext": { resources: ["missing"] }, }), ); expect(() => readBuildSpec(ext)).toThrow(/missing does not exist/u); writeFileSync( join(ext, "package.json"), JSON.stringify({ name: "@bugabinga/pi-ext-demo", "pi-ext": { externals: { left: "" } }, }), ); expect(() => readBuildSpec(ext)).toThrow(/incompatibility reason/u); writeFileSync( join(ext, "package.json"), JSON.stringify({ name: "@bugabinga/pi-ext-demo", dependencies: { left: "^1" }, "pi-ext": { externals: { left: "native addon" } }, }), ); expect(readBuildSpec(ext).externals).toEqual({ left: "native addon" }); } finally { rmSync(root, { recursive: true, force: true }); } }); });