import { access } from "node:fs/promises"; import path from "node:path"; import { readTextFileIfSmall, relative, repositoryRoot, walkFiles, } from "./repository-files.mjs"; const requiredDocuments = [ "AGENTS.md", "docs/README.md", "docs/agent-playbook.md", "docs/implementation-plan.md", "docs/quality-gates.md", "docs/decisions.md", ]; const failures = []; for (const required of requiredDocuments) { try { await access(path.join(repositoryRoot, required)); } catch { failures.push(`missing required document: ${required}`); } } const markdownFiles = (await walkFiles()).filter((file) => file.endsWith(".md")); const markdownLink = /!?\[[^\]]*\]\(([^)]+)\)/g; for (const markdownFile of markdownFiles) { const content = await readTextFileIfSmall(markdownFile); if (content === null) continue; for (const match of content.matchAll(markdownLink)) { let target = match[1].trim(); if (target.startsWith("<") && target.endsWith(">")) { target = target.slice(1, -1); } target = target.split(/\s+["']/u, 1)[0]; if ( target === "" || target.startsWith("#") || /^[a-z][a-z\d+.-]*:/iu.test(target) ) { continue; } const pathPart = target.split("#", 1)[0].split("?", 1)[0]; const decodedPath = decodeURIComponent(pathPart); const resolved = path.resolve(path.dirname(markdownFile), decodedPath); try { await access(resolved); } catch { failures.push(`${relative(markdownFile)} -> ${target}`); } } } if (failures.length > 0) { console.error("Documentation verification failed:"); for (const failure of failures) console.error(`- ${failure}`); process.exitCode = 1; } else { console.log(`Documentation verification passed (${markdownFiles.length} Markdown files).`); }