repositories / pi-ext
pi-ext
bugabingas pi extensions
owned by admin
extensions/the-system/plan-status.ts
Rawimport { readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
import path from "node:path";
import {
parseAuthoredDocument,
resolveGovernedPath,
SystemDocumentError,
validateBundleCandidate,
} from "./documents.ts";
import type { ProjectIndex } from "./project-index.ts";
export type PlanStatus = "draft" | "approved";
const FRONTMATTER = /^(---\r?\n)([\s\S]*?)(\r?\n---(?:\r?\n|$))/;
const STATUS_LINE = /^status:[^\r\n]*$/gm;
export function planStatusSource(source: string, status: PlanStatus): string {
const block = FRONTMATTER.exec(source);
if (!block)
throw new SystemDocumentError(
"status.frontmatter",
"Plan requires one leading YAML frontmatter block",
);
const open = block[1] ?? "";
const body = block[2] ?? "";
const close = block[3] ?? "";
const matches = body.match(STATUS_LINE) ?? [];
if (matches.length !== 1)
throw new SystemDocumentError(
"status.line",
`Plan frontmatter requires exactly one status line; found ${matches.length}`,
);
const updated = body.replace(STATUS_LINE, `status: ${status}`);
return `${open}${updated}${close}${source.slice(block[0].length)}`;
}
export function writePlanStatus(
index: ProjectIndex,
id: string,
status: PlanStatus,
): void {
const document = index.documents.find(
(candidate) => candidate.metadata.id === id,
);
if (!document)
throw new SystemDocumentError(
"status.missing-item",
`No System document: ${id}`,
);
if (document.metadata.type !== "plan")
throw new SystemDocumentError(
"status.item-type",
`Only plans carry a status: ${id}`,
);
if (document.metadata.status === status) return;
const target = resolveGovernedPath(index.root, document.path);
const markdown = planStatusSource(readFileSync(target, "utf8"), status);
const diagnostics = validateBundleCandidate(
index.documents.map((candidate) => ({
path: candidate.path,
markdown: candidate.markdown,
})),
{ path: document.path, markdown },
index.prefix,
);
if (diagnostics.length > 0)
throw new SystemDocumentError(
"status.invalid",
`Plan would become invalid: ${diagnostics.map((item) => item.message).join("; ")}`,
);
const written = parseAuthoredDocument(markdown, index.prefix).metadata;
if (written.type !== "plan" || written.status !== status)
throw new SystemDocumentError(
"status.invalid",
`Plan status did not change to ${status}: ${id}`,
);
const temporary = path.join(
path.dirname(target),
`.status-${process.pid}-${Date.now()}.md`,
);
writeFileSync(temporary, markdown, { encoding: "utf8", flag: "wx" });
try {
renameSync(temporary, target);
} finally {
rmSync(temporary, { force: true });
}
}