chore: initialize POM 2 repo (docs guards green; release gates recorded NOT RUN pending install)

This commit is contained in:
Antigravity
2026-08-19 10:31:00 +02:00
commit 7ffd00b5c5
111 changed files with 12522 additions and 0 deletions

409
scripts/check-docs.mjs Normal file
View File

@@ -0,0 +1,409 @@
import { existsSync, readFileSync, readdirSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
// `/pom resume [state-path]` is documented in the README command map and in the POM.yml
// `commands:` block, but it is deliberately absent from the HELP template literal in
// src/command.ts. HELP is canonical for what the built-in help advertises, so the verb is
// neither removed from the docs nor added to HELP. It is recorded here instead, so the
// divergence stays visible and the parity check still passes deterministically.
// Any command divergence that is not listed here is a failure.
const KNOWN_COMMAND_DIVERGENCES = new Set(["resume"]);
// Roots that src/*.ts loads through `new URL("../<root>", import.meta.url)`. Check 1 derives
// this set from source instead of hardcoding it, so a new bundled-resource root cannot be
// added in code and silently omitted from the published package.
const RESOURCE_SOURCES = ["src/prompts.ts", "src/themes.ts", "src/events.ts"];
const BUNDLED_ROOT = /new URL\(\s*["'`]\.\.\/([A-Za-z0-9._-]+)/g;
// Generated-layout names that drifted in earlier revisions and must never come back.
const FORBIDDEN_LAYOUT_NAMES = ["06_exports", "06_logs"];
const REQUIRED_LAYOUT_NAMES = ["06_ledgers", "07_exports"];
const LAYOUT_DOCS = ["docs/INVENTORY.md", "skills/pom/references/artifacts.md"];
const VERIFICATION_STATUSES = ["PASS", "FAIL", "NOT RUN", "BLOCKED"];
const MARKDOWN_LINK = /\[[^\]]*\]\(\s*([^)\s]+?)(?:\s+"[^"]*")?\s*\)/g;
const NAVIGATION_PAGES = ["index.md", "navigation.md"];
// Canonical project coordinates. These live on infrastructure the maintainer owns
// (loca.zone). There is no public forge mirror, so a plausible-looking third-party URL
// must never be substituted, and the earlier `UNRESOLVED:` placeholders must never return.
const METADATA_FIELDS = ["repository", "homepage", "bugs"];
const CANONICAL_HOST = "loca.zone";
const PLACEHOLDER_MARKER = "UNRESOLVED";
const TOTAL_CHECKS = 7;
const checks = [];
const failed = [];
function text(rel) {
return readFileSync(join(root, rel), "utf8");
}
function lineNumbers(body, pattern) {
return body
.split("\n")
.map((line, index) => (pattern.test(line) ? index + 1 : 0))
.filter(Boolean);
}
/** Blanks fenced blocks and inline code spans so illustrative links are not link-checked. */
function withoutCode(body) {
const blank = (matched) => matched.replace(/[^\n]/g, " ");
return body.replace(/^```[\s\S]*?^```/gm, blank).replace(/`[^`\n]*`/g, blank);
}
/** Reads the indented body of a top-level or nested block key, e.g. `commands:`. */
function blockLines(rel, key) {
const lines = text(rel).split("\n");
const start = lines.findIndex((line) => new RegExp(`^\\s*${key}:\\s*$`).test(line));
if (start < 0) return null;
const indent = lines[start].length - lines[start].trimStart().length;
const body = [];
for (const line of lines.slice(start + 1)) {
if (line.trim() === "") continue;
if (line.length - line.trimStart().length <= indent) break;
body.push(line.trim());
}
return body;
}
/** `/pom` -> `dashboard`; `/pom theme install` -> `theme`. */
function verbOf(command) {
const match = /^\/pom(?:\s+([a-z][a-z0-9-]*))?\b/.exec(command);
if (!match) return null;
return match[1] ?? "dashboard";
}
function check(index, name, run) {
const problems = [];
const notes = [];
try {
run((message) => problems.push(message), (message) => notes.push(message));
} catch (error) {
problems.push(`unexpected error: ${error.message}`);
}
console.log(`${problems.length === 0 ? "PASS" : "FAIL"} check ${index}/${TOTAL_CHECKS} ${name}`);
for (const problem of problems) console.log(` ${problem}`);
for (const note of notes) console.log(` note: ${note}`);
if (problems.length > 0) failed.push(name);
}
// 1. Resource/package parity: every bundled-resource root loaded by source must ship.
checks.push((index) =>
check(index, "resource-package-parity", (fail) => {
const roots = new Map();
for (const rel of RESOURCE_SOURCES) {
if (!existsSync(join(root, rel))) {
fail(`${rel}: resource-loading module is missing; check 1 cannot derive bundled roots from source`);
continue;
}
const body = text(rel);
for (const match of body.matchAll(BUNDLED_ROOT)) {
if (!roots.has(match[1])) roots.set(match[1], new Set());
roots.get(match[1]).add(rel);
}
}
if (roots.size === 0) {
fail(
`no bundled-resource roots discovered in ${RESOURCE_SOURCES.join(", ")}; the ` +
`\`new URL("../<root>", import.meta.url)\` pattern in scripts/check-docs.mjs no longer ` +
"matches source and must be repaired (this is a broken check, not an empty result)",
);
return;
}
const files = JSON.parse(text("package.json")).files;
if (!Array.isArray(files)) {
fail("package.json: `files` must be an array listing every published path");
return;
}
for (const [name, sources] of [...roots].sort(([left], [right]) => left.localeCompare(right))) {
if (!files.includes(name))
fail(`package.json: \`files\` must contain "${name}" because ${[...sources].join(" and ")} load it at runtime`);
if (!existsSync(join(root, name)))
fail(`${name}: directory is loaded by ${[...sources].join(" and ")} but does not exist in the repository`);
}
}),
);
// 2. Command parity: HELP, the README command map, and POM.yml `commands:` must agree.
checks.push((index) =>
check(index, "command-parity", (fail, note) => {
const command = text("src/command.ts");
const help = /const HELP\s*=\s*`([\s\S]*?)`\s*;/.exec(command);
if (!help) {
fail("src/command.ts: could not locate the `const HELP = `...`;` template literal that defines the command surface");
return;
}
const helpVerbs = new Set();
for (const line of help[1].split("\n")) {
const trimmed = line.trim();
if (!trimmed.startsWith("/pom")) continue;
const verb = verbOf(trimmed);
if (verb) helpVerbs.add(verb);
}
const readmeLines = text("README.md").split("\n");
const heading = readmeLines.findIndex((line) => /^##\s+Command map\s*$/.test(line));
if (heading < 0) {
fail("README.md: missing the `## Command map` heading that check 2 reads the command table from");
return;
}
const after = readmeLines.slice(heading + 1);
const end = after.findIndex((line) => /^##\s/.test(line));
const readmeVerbs = new Set();
for (const line of end < 0 ? after : after.slice(0, end)) {
if (!line.trimStart().startsWith("|")) continue;
for (const span of line.match(/`[^`]+`/g) ?? []) {
const verb = verbOf(span.slice(1, -1).trim());
if (verb) readmeVerbs.add(verb);
}
}
const commandBlock = blockLines("POM.yml", "commands");
if (!commandBlock) {
fail("POM.yml: missing the top-level `commands:` block that check 2 reads the command map from");
return;
}
const manifestVerbs = new Set();
for (const entry of commandBlock) {
const value = /:\s*(.+)$/.exec(entry);
if (!value) continue;
const verb = verbOf(value[1].trim().replace(/^["']|["']$/g, ""));
if (verb) manifestVerbs.add(verb);
}
if (helpVerbs.size === 0) fail("src/command.ts: HELP advertises no `/pom` verbs");
if (readmeVerbs.size === 0) fail("README.md: the `## Command map` table lists no `/pom` commands");
if (manifestVerbs.size === 0) fail("POM.yml: the `commands:` block lists no `/pom` commands");
const sources = [
["src/command.ts HELP", helpVerbs],
["README.md `## Command map`", readmeVerbs],
["POM.yml `commands:`", manifestVerbs],
];
const union = [...new Set(sources.flatMap(([, verbs]) => [...verbs]))].sort();
for (const verb of union) {
const missing = sources.filter(([, verbs]) => !verbs.has(verb)).map(([label]) => label);
if (missing.length === 0) continue;
if (KNOWN_COMMAND_DIVERGENCES.has(verb)) {
note(`/pom ${verb} is absent from ${missing.join(", ")}; allowlisted in KNOWN_COMMAND_DIVERGENCES`);
continue;
}
const present = sources.filter(([, verbs]) => verbs.has(verb)).map(([label]) => label);
fail(
`/pom ${verb} is documented in ${present.join(", ")} but absent from ${missing.join(", ")}; ` +
"add it there or record it in KNOWN_COMMAND_DIVERGENCES in scripts/check-docs.mjs",
);
}
for (const verb of KNOWN_COMMAND_DIVERGENCES) {
if (!union.includes(verb))
fail(
`KNOWN_COMMAND_DIVERGENCES lists "${verb}" but no source documents /pom ${verb}; ` +
"remove the stale allowlist entry from scripts/check-docs.mjs",
);
}
}),
);
// 3. Layout contract: the generated project tree names in docs must match src/persistence.ts.
checks.push((index) =>
check(index, "layout-contract", (fail) => {
for (const rel of LAYOUT_DOCS) {
if (!existsSync(join(root, rel))) {
fail(`${rel}: layout documentation is missing`);
continue;
}
const body = text(rel);
for (const name of FORBIDDEN_LAYOUT_NAMES) {
const lines = lineNumbers(body, new RegExp(`\\b${name}\\b`));
if (lines.length > 0)
fail(
`${rel}:${lines.join(",")} names "${name}", which src/persistence.ts bootstrapProject never ` +
`creates; the canonical names are ${REQUIRED_LAYOUT_NAMES.join(" and ")}`,
);
}
}
const inventory = text("docs/INVENTORY.md");
for (const name of REQUIRED_LAYOUT_NAMES) {
if (!new RegExp(`\\b${name}\\b`).test(inventory))
fail(`docs/INVENTORY.md: must document the canonical generated directory "${name}"`);
}
}),
);
// 4. Prompt-order contract: POM.yml must mirror the composition order in src/prompts.ts.
checks.push((index) =>
check(index, "prompt-order-contract", (fail) => {
const items = blockLines("POM.yml", "prompt_precedence");
if (!items) {
fail("POM.yml: missing the `prompt_precedence:` list under `knowledge:`");
return;
}
const entries = items.filter((item) => item.startsWith("- ")).map((item) => item.slice(2).trim());
if (entries.length < 3) {
fail(`POM.yml: \`prompt_precedence\` must list the full composition order, found ${entries.length} entries`);
return;
}
const first = entries[0];
const last = entries[entries.length - 1];
if (!/immutable/i.test(first) || !/law/i.test(first))
fail(
`POM.yml: \`prompt_precedence\` entry 1 must name the immutable POM production law that ` +
`composePromptStack prepends, found "${first}"`,
);
if (!/executable stage contract/i.test(last))
fail(
`POM.yml: \`prompt_precedence\` must end with the executable stage contract appended by ` +
`renderStagePrompt, found "${last}" as the final entry`,
);
const misplaced = entries
.slice(0, -1)
.map((entry, position) => (/executable stage contract/i.test(entry) ? position + 1 : 0))
.filter(Boolean);
if (misplaced.length > 0)
fail(
`POM.yml: \`prompt_precedence\` mentions the executable stage contract at entry ` +
`${misplaced.join(",")}; renderStagePrompt appends it last, so it must be the final entry only`,
);
if (!entries.some((entry) => /replac/i.test(entry) && /\bID\b/i.test(entry)))
fail(
"POM.yml: `prompt_precedence` must state that project-local fragments replace bundled fragments " +
"by matching basename ID, as src/prompts.ts composePromptStack does",
);
}),
);
// 5. Link/orphan check: relative doc links must resolve, and every page must be reachable.
checks.push((index) =>
check(index, "link-and-orphan", (fail) => {
const pages = readdirSync(join(root, "docs"))
.filter((name) => name.endsWith(".md"))
.sort();
if (pages.length === 0) {
fail("docs: contains no Markdown pages");
return;
}
const reachable = new Set();
for (const page of pages) {
const rel = `docs/${page}`;
const body = withoutCode(text(rel));
for (const match of body.matchAll(MARKDOWN_LINK)) {
const target = match[1];
if (/^[a-z][a-z0-9+.-]*:/i.test(target) || target.startsWith("#") || target.startsWith("/")) continue;
const path = target.split("#")[0].split("?")[0];
if (path === "") continue;
const resolved = resolve(join(root, "docs"), path);
if (NAVIGATION_PAGES.includes(page)) reachable.add(resolved);
if (!existsSync(resolved))
fail(
`${rel}: broken relative link \`${target}\` resolves to ${relative(root, resolved)}, ` +
"which does not exist on disk",
);
}
}
for (const page of pages) {
if (page === "index.md") continue;
if (reachable.has(resolve(join(root, "docs", page)))) continue;
fail(
`docs/${page}: unreachable page, no link from ${NAVIGATION_PAGES.map((name) => `docs/${name}`).join(" or ")}; ` +
"add it to the navigation tables so the wiki has no orphans",
);
}
}),
);
// 6. Release-evidence freshness: verification gates must be evidence objects, never literals.
checks.push((index) =>
check(index, "release-evidence", (fail) => {
const release = JSON.parse(text("RELEASE.json"));
const verification = release.verification;
if (verification === undefined || verification === null || typeof verification !== "object" || Array.isArray(verification)) {
fail("RELEASE.json: `verification` must be an object mapping each gate to an evidence object");
return;
}
const gates = Object.entries(verification);
if (gates.length === 0) {
fail("RELEASE.json: `verification` lists no gates");
return;
}
for (const [gate, value] of gates) {
if (typeof value !== "object" || value === null || Array.isArray(value)) {
fail(
`RELEASE.json: verification.${gate} is the bare ${typeof value} ${JSON.stringify(value)}; every gate must ` +
`be an evidence object carrying a \`status\` of ${VERIFICATION_STATUSES.join(" | ")}, never a hard-coded literal`,
);
continue;
}
if (typeof value.status !== "string") {
fail(`RELEASE.json: verification.${gate} is missing a string \`status\` field`);
continue;
}
if (!VERIFICATION_STATUSES.includes(value.status))
fail(
`RELEASE.json: verification.${gate}.status is "${value.status}"; allowed values are ` +
`${VERIFICATION_STATUSES.join(" | ")}`,
);
}
}),
);
// 7. Package metadata: canonical coordinates must stay resolved and stay on owned infrastructure.
checks.push((index) =>
check(index, "package-metadata", (fail, note) => {
const pkg = JSON.parse(text("package.json"));
for (const field of METADATA_FIELDS) {
const value = pkg[field];
if (value === undefined) {
fail(`package.json: \`${field}\` is missing; canonical project coordinates are required`);
continue;
}
const urls = [];
if (typeof value === "string") urls.push(value);
else if (typeof value === "object" && value !== null) {
if (typeof value.url === "string") urls.push(value.url);
else fail(`package.json: \`${field}\` is an object without a string \`url\``);
} else {
fail(`package.json: \`${field}\` must be a string or an object with a \`url\``);
continue;
}
for (const url of urls) {
if (url.includes(PLACEHOLDER_MARKER)) {
fail(
`package.json: \`${field}\` still carries the ${PLACEHOLDER_MARKER} placeholder "${url}"; ` +
"set a real canonical URL before publishing",
);
continue;
}
let host;
try {
host = new URL(url.replace(/^git\+/, "")).host;
} catch {
fail(`package.json: \`${field}\` value "${url}" is not a parseable absolute URL`);
continue;
}
if (host !== CANONICAL_HOST && !host.endsWith(`.${CANONICAL_HOST}`))
fail(
`package.json: \`${field}\` points at "${host}", which is outside the canonical ${CANONICAL_HOST} ` +
"infrastructure; no public forge mirror has been confirmed for this package",
);
}
}
const repository = typeof pkg.repository?.url === "string" ? pkg.repository.url : "";
if (repository.includes(CANONICAL_HOST))
note(`${repository} is reserved on owned infrastructure and is not yet serving Git`);
}),
);
checks.forEach((run, position) => run(position + 1));
if (failed.length === 0) {
console.log("POM docs verification PASS");
} else {
console.log(`POM docs verification FAIL (${failed.length}/${checks.length}: ${failed.join(", ")})`);
process.exitCode = 1;
}

5
scripts/install.ps1 Normal file
View File

@@ -0,0 +1,5 @@
$ErrorActionPreference = "Stop"
npm install
npm run verify
omp plugin link .
Write-Host "POM linked. Restart OMP or run /reload-plugins, then run /pom theme install."

6
scripts/install.sh Executable file
View File

@@ -0,0 +1,6 @@
#!/usr/bin/env sh
set -eu
npm install
npm run verify
omp plugin link .
printf '%s\n' 'POM linked. Restart OMP or run /reload-plugins, then run /pom theme install.'

View File

@@ -0,0 +1,117 @@
import { createHash } from "node:crypto";
import { existsSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
const packageJson = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
function walk(path) {
if (!existsSync(path)) throw new Error(`Missing packaged path: ${relative(root, path)}`);
if (statSync(path).isFile()) return [path];
return readdirSync(path, { withFileTypes: true }).flatMap((entry) => walk(join(path, entry.name)));
}
const candidates = packageJson.files
.filter((item) => item !== "RELEASE.json")
.flatMap((item) => walk(join(root, item)));
candidates.push(join(root, "package.json"));
const sourceInventory = [...new Set(candidates)]
.sort()
.map((path) => {
const data = readFileSync(path);
return {
path: relative(root, path).replaceAll("\\", "/"),
bytes: data.length,
sha256: createHash("sha256").update(data).digest("hex"),
};
});
function readGate(file, gate) {
const path = join(root, file);
if (!existsSync(path)) return { status: "NOT RUN", reason: `Missing evidence file: ${file}` };
try {
const data = JSON.parse(readFileSync(path, "utf8"));
const passed = data.passed === true || (data.summary && data.summary.passed === true);
return {
status: passed ? "PASS" : "FAIL",
timestamp: data.timestamp || null,
scope: data.scope || gate,
command: data.command || null,
details: data,
};
} catch {
return { status: "NOT RUN", reason: `Unparsable evidence: ${file}` };
}
}
const gates = {
typescript: readGate("00_admin/validation/typecheck.json", "tsc --noEmit"),
behavioralTests: readGate("00_admin/validation/tests.json", "npm test"),
runtimeSmoke: readGate("00_admin/validation/smoke.json", "npm run smoke"),
structuralVerification: readGate("00_admin/validation/structural.json", "node scripts/verify.mjs"),
themeSchemaValidation: readGate("00_admin/validation/theme.json", "theme schema validation"),
npmPackDryRun: readGate("00_admin/validation/pack.json", "npm pack --dry-run"),
};
const releaseDate = new Date().toISOString().split("T")[0];
const release = {
name: "POM — Produce Magnum Opus",
package: packageJson.name,
version: packageJson.version,
schemaVersion: "2.0.0",
releaseDate,
status: "release-candidate",
summary: "OMP-native production kernel with evidence-gated stages, native task/IRC delegation, optional Swarm DAGs, Obsidian/Quartz knowledge projection, cinematic TUI hooks, and verified archive delivery.",
compatibility: {
bun: packageJson.engines.bun,
ompPackages: ">=16.4.6 <17",
module: "ESM",
},
inventory: {
stages: 9,
customTools: 7,
agents: 14,
prompts: 13,
rules: 3,
themes: 2,
behavioralTests: 9,
sourceFilesHashed: sourceInventory.length,
sourceBytesHashed: sourceInventory.reduce((sum, item) => sum + item.bytes, 0),
selfHashPolicy: "RELEASE.json is excluded from its own source inventory; final package and archive hashes are recorded externally.",
},
verification: {
typescript: gates.typescript,
behavioralTests: gates.behavioralTests,
runtimeSmoke: gates.runtimeSmoke,
structuralVerification: gates.structuralVerification,
themeSchemaValidation: gates.themeSchemaValidation,
npmPackDryRun: gates.npmPackDryRun,
productionDependencyAudit: { status: "NOT RUN", reason: "Requires network access to npm registry" },
fullDevelopmentTreeAudit: { status: "NOT RUN", reason: "Requires network access to npm registry" },
ompPluginDoctor: { status: "NOT RUN", reason: "Requires Bun + OMP installation" },
interactiveTuiSmoke: { status: "NOT RUN", reason: "Requires a live OMP terminal host" },
},
releaseGates: [
"Run npm run verify",
"Run npm run doctor in a Bun + OMP installation",
"Open /pom and verify the overlay, HUD, status, cards, and /pom theme install",
"Create a smoke project, validate stage 0, and verify a checkpoint archive",
],
sourceInventory,
};
const rendered = `${JSON.stringify(release, null, 2)}\n`;
const output = join(root, "RELEASE.json");
if (process.argv.includes("--check")) {
if (!existsSync(output) || readFileSync(output, "utf8") !== rendered) {
console.error("RELEASE.json is stale; run npm run release:manifest");
process.exit(1);
}
console.log("POM release manifest PASS");
} else {
writeFileSync(output, rendered);
console.log(`Wrote RELEASE.json · ${sourceInventory.length} files · ${release.inventory.sourceBytesHashed} bytes`);
}

26
scripts/smoke.ts Normal file
View File

@@ -0,0 +1,26 @@
import assert from "node:assert/strict";
import { mkdtemp, readFile, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { DEFAULT_CONFIG } from "../src/domain";
import { bootstrapProject } from "../src/persistence";
import { createState } from "../src/state";
import { validatePom } from "../src/validators";
const sandbox = await mkdtemp(join(tmpdir(), "pom-smoke-"));
try {
const config = { ...DEFAULT_CONFIG, projectRootMode: "cwd" as const };
const state = createState("POM Smoke", sandbox, config, true);
await bootstrapProject(state);
const ompConfig = await readFile(join(sandbox, ".omp", "config.yml"), "utf8");
assert.match(ompConfig, /dark: pom-nocturne/);
assert.match(ompConfig, /task:\n\s+eager: preferred/);
assert.match(ompConfig, /compaction:\n\s+strategy: snapcompact/);
const board = await readFile(join(sandbox, "content", "production-board.md"), "utf8");
assert.match(board, /# Production Board/);
const report = await validatePom(state, "quick", { writeReport: false });
assert.equal(report.passed, true);
console.log(`POM runtime smoke PASS · ${report.checks.length} checks`);
} finally {
await rm(sandbox, { recursive: true, force: true });
}

77
scripts/verify.mjs Normal file
View File

@@ -0,0 +1,77 @@
import assert from "node:assert/strict";
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
import { join, resolve } from "node:path";
import { fileURLToPath } from "node:url";
const root = resolve(fileURLToPath(new URL("..", import.meta.url)));
const required = [
"POM.yml",
"README.md",
"RELEASE.json",
"LICENSE",
"package.json",
"src/index.ts",
"src/events.ts",
"src/tools.ts",
"src/visual.ts",
"skills/pom/SKILL.md",
"config/APPEND_SYSTEM.md",
"prompts/stage-run.md",
"themes/pom-nocturne.json",
"themes/pom-parchment.json",
"scripts/smoke.ts",
];
for (const rel of required) assert(existsSync(join(root, rel)), `missing ${rel}`);
const pkg = JSON.parse(readFileSync(join(root, "package.json"), "utf8"));
const release = JSON.parse(readFileSync(join(root, "RELEASE.json"), "utf8"));
assert.equal(release.version, pkg.version, "release manifest version mismatch");
assert.equal(release.inventory.stages, 9, "release stage inventory mismatch");
assert.equal(release.inventory.customTools, 7, "release tool inventory mismatch");
assert.equal(release.inventory.behavioralTests, 9, "release test inventory mismatch");
assert.deepEqual(pkg.omp.extensions, ["./src/index.ts"], "exactly one OMP entry required");
assert.equal(pkg.version, "2.0.0", "release version must match POM 2");
for (const rel of pkg.files) assert(existsSync(join(root, rel)), `package files entry does not exist: ${rel}`);
const command = readFileSync(join(root, "src/command.ts"), "utf8");
assert.equal((command.match(/registerCommand\("pom"/g) ?? []).length, 1, "exactly one /pom registration required");
assert(command.includes('registerShortcut("alt+p"'), "Alt+P command-center shortcut required");
const domain = readFileSync(join(root, "src/domain.ts"), "utf8");
assert.equal((domain.match(/\bid:\s*[0-8],/g) ?? []).length, 9, "nine stages required");
const tools = readFileSync(join(root, "src/tools.ts"), "utf8");
assert.equal((tools.match(/name:\s*"pom_[a-z]+"/g) ?? []).length, 7, "seven POM tools required");
for (const forbidden of ["allowArgs", "formatApprovalDetails", 'concurrency: "exclusive"', "ExtensionAPI = any"]) {
assert(!tools.includes(forbidden), `unsupported or unsafe registration field present: ${forbidden}`);
}
assert(!existsSync(join(root, "src/omp-shim.d.ts")), "OMP declaration shim must not ship");
assert(!existsSync(join(root, "src/node-shim.d.ts")), "Node declaration shim must not ship");
const markdownCount = (dir) => readdirSync(join(root, dir)).filter((name) => name.endsWith(".md")).length;
assert.equal(markdownCount("agents"), 14, "fourteen specialist agents required");
assert.equal(markdownCount("prompts"), 13, "thirteen prompt fragments required");
assert.equal(markdownCount("rules"), 3, "three rules required");
assert.equal(readdirSync(join(root, "themes")).filter((name) => name.endsWith(".json")).length, 2, "two POM themes required");
for (const file of readdirSync(join(root, "agents")).filter((name) => name.endsWith(".md"))) {
const text = readFileSync(join(root, "agents", file), "utf8");
assert(text.startsWith("---\n"), `${file} missing frontmatter`);
assert(/^name:\s*pom-[a-z0-9-]+$/m.test(text), `${file} missing valid agent name`);
assert(/^description:\s*\S.+$/m.test(text), `${file} missing description`);
assert(/^tools:\s*.*\byield\b.*$/m.test(text), `${file} must expose yield`);
}
const themeSchema = JSON.parse(readFileSync(join(root, "node_modules/@oh-my-pi/pi-coding-agent/src/modes/theme/theme-schema.json"), "utf8"));
for (const file of readdirSync(join(root, "themes")).filter((name) => name.endsWith(".json"))) {
const theme = JSON.parse(readFileSync(join(root, "themes", file), "utf8"));
assert.equal(theme.name, file.slice(0, -5), `${file} name mismatch`);
for (const key of themeSchema.properties.colors.required) assert(key in theme.colors, `${file} missing color ${key}`);
}
for (const rel of ["src", "skills", "agents", "rules", "prompts", "themes", "config", "docs", "scripts"]) {
assert(statSync(join(root, rel)).isDirectory(), `${rel} must be a directory`);
}
console.log("POM structural verification PASS");