import { mkdir, writeFile } from "node:fs/promises"; import { join } from "node:path"; import type { ExtensionAPI } from "@earendil-works/pi-coding-agent"; import { requireBrain } from "./context.ts"; import { collectBlocks } from "./intent-blocks.ts"; import { renderTarget } from "./build-renderers.ts"; import { isValidIdentifier } from "./utils.ts"; import { readEnolaConfig } from "./brain-home.ts"; import { enolaGateCheck, runEnolaBaseline } from "./enola.ts"; export function registerBuild(pi: ExtensionAPI) { pi.registerCommand("brain:build", { description: "Generate code from approved intent blocks (usage: /brain:build )", handler: async (args, ctx) => { const trimmed = args.trim(); if (!trimmed) { ctx.ui.notify("Usage: /brain:build ", "warning"); return; } const parts = trimmed.split(/\s+/); if (parts.length !== 2) { ctx.ui.notify("Usage: /brain:build ", "warning"); return; } const [scope, target] = parts; if (!isValidIdentifier(scope) || !isValidIdentifier(target)) { ctx.ui.notify("Scope and target must be simple identifiers (letters, numbers, -, _).", "warning"); return; } const home = await requireBrain(ctx.cwd); if (!home) { ctx.ui.notify("No pi-brain home found.", "error"); return; } const enolaConfig = await readEnolaConfig(home); if (enolaConfig.enabled && enolaConfig.gateBuild) { const gate = await enolaGateCheck(home, "/brain:build"); if (!gate.proceed) { ctx.ui.notify(gate.message, "warning"); return; } } const blocks = await collectBlocks(home, scope); const rendered = renderTarget(blocks, target); if (rendered === null) { ctx.ui.notify(`Unknown build target: ${target}`, "error"); return; } const header = [ "// Generated by /brain:build", `// Scope: ${scope}`, `// Target: ${target}`, `// Source blocks: ${blocks.map((b) => `${b.source}#${b.name}`).join(", ")}`, "// Review before applying; do not edit this header manually.", "", ].join("\n"); const output = header + rendered; const outputDir = join(home.path, "wiki", scope, "ai-suggestions", "build", target); const outputPath = join(outputDir, "generated.ts"); await mkdir(outputDir, { recursive: true }); await writeFile(outputPath, output, "utf-8"); if (enolaConfig.enabled && enolaConfig.autoBaseline) { const baseline = await runEnolaBaseline(home); ctx.ui.notify(`Generated ${outputPath}\n\n${baseline.summary ?? "Baseline updated."}\n\n${output.slice(0, 1000)}`, "info"); } else { ctx.ui.notify(`Generated ${outputPath}\n\n${output.slice(0, 1200)}`, "info"); } }, }); }