import assert from "node:assert/strict"; import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import path from "node:path"; import { initTheme } from "@earendil-works/pi-coding-agent"; import { afterEach, describe, it } from "vitest"; import { reserveBundleSlot } from "../bundle-slot.ts"; import { createBundleSlotTool } from "../bundle-slot-tool.ts"; const roots: string[] = []; const project = () => { const root = mkdtempSync(path.join(tmpdir(), "system-slot-")); roots.push(root); for (const directory of ["specs", "phases", "plans", "issues", "research"]) mkdirSync(path.join(root, ".system", directory), { recursive: true }); writeFileSync( path.join(root, ".system/config.json"), '{"prefix":"PX","schemaVersion":0}', ); return root; }; afterEach(() => { for (const root of roots.splice(0)) rmSync(root, { recursive: true, force: true }); }); const theme = { fg: (_style: string, text: string) => text, bold: (text: string) => text, } as never; const render = (component: { render(width: number): string[] }) => component.render(120).join("\n"); const renderContext = (expanded: boolean, lastComponent?: unknown) => ({ expanded, lastComponent, isError: false }) as never; describe("system_bundle_slot", () => { it("derives semantic slugs and reserves unique IDs on demand", () => { const root = project(); const reserved = new Set(); const first = reserveBundleSlot( root, { type: "phase", title: "Service Übergang" }, reserved, ); const second = reserveBundleSlot( root, { type: "phase", title: "Service Übergang" }, reserved, ); assert.equal(first.slug, "service-ubergang"); assert.notEqual(first.id, second.id); assert.match( first.path, /^\.system\/phases\/PX-PHASE-[A-Z0-9_]{8}-service-ubergang\/index\.md$/, ); }); it("uses concise prompt metadata and Pi-style tool rendering", () => { initTheme("default", false); const root = project(); const slot = reserveBundleSlot(root, { type: "plan", title: "Replace cache invalidation", }); const tool = createBundleSlotTool(); assert.equal(tool.label, "system_bundle_slot"); assert.equal( tool.description, "Reserve a unique System document ID and canonical bundle path from its type and semantic title.", ); assert.equal( tool.promptSnippet, "Reserve an ID and path for a System bundle", ); assert.notEqual(tool.promptSnippet, tool.description); assert.deepEqual(tool.promptGuidelines, [ "Resolve needed clarifications before calling system_bundle_slot.", "Call system_bundle_slot once per required bundle after choosing its final type and title; use each returned ID and path exactly.", ]); assert.ok(tool.renderCall); const callComponent = tool.renderCall( { type: "plan", title: slot.title }, theme, renderContext(false), ); assert.match( render(callComponent), /^system_bundle_slot plan · Replace cache invalidation/, ); assert.ok(tool.renderResult); const result = { content: [], details: slot }; const collapsedComponent = tool.renderResult( result, { expanded: false, isPartial: false }, theme, renderContext(false), ); const collapsed = render(collapsedComponent); const expandedComponent = tool.renderResult( result, { expanded: true, isPartial: false }, theme, renderContext(true, collapsedComponent), ); const expanded = render(expandedComponent); assert.equal(expandedComponent, collapsedComponent); assert.match(collapsed, new RegExp(`^✓ reserved ${slot.id}`)); assert.match(collapsed, /replace-cache-invalidation/); assert.match(collapsed, /to expand/); assert.doesNotMatch(collapsed, new RegExp(slot.path)); assert.match(expanded, /type plan/); assert.match(expanded, /title Replace cache invalidation/); assert.ok(expanded.includes(`path ${slot.path}`)); const errorResult = { content: [ { type: "text", text: "reservation failed\nEND-RESERVATION-DETAILS" }, ], details: {}, }; const error = render( tool.renderResult( errorResult, { expanded: false, isPartial: false }, theme, { ...renderContext(false), isError: true } as never, ), ); const expandedError = render( tool.renderResult( errorResult, { expanded: true, isPartial: false }, theme, { ...renderContext(true), isError: true } as never, ), ); assert.match(error, /^reservation failed/); assert.match(error, /to expand/); assert.doesNotMatch(error, /END-RESERVATION-DETAILS/); assert.match(expandedError, /END-RESERVATION-DETAILS/); }); });