scripts/docs-check.mjs
Rama main · réplica de lectura del repositorio original.
import { readdir, readFile, stat } from "node:fs/promises";
import path from "node:path";
const root = process.cwd();
const layers = ["community", "standards", "owner", "ai"];
const failures = [];
async function exists(file) {
try { await stat(file); return true; } catch { return false; }
}
const docsIndexPath = path.join(root, "docs", "README.md");
if (!(await exists(docsIndexPath))) {
failures.push("docs/README.md is missing");
} else {
const docsIndex = await readFile(docsIndexPath, "utf8");
for (const layer of layers) {
if (!docsIndex.includes(`${layer}/README.md`)) {
failures.push(`docs/README.md does not index ${layer}/README.md`);
}
}
}
for (const layer of layers) {
const dir = path.join(root, "docs", layer);
const indexPath = path.join(dir, "README.md");
if (!(await exists(indexPath))) {
failures.push(`docs/${layer}/README.md is missing`);
continue;
}
const index = await readFile(indexPath, "utf8");
const entries = (await readdir(dir))
.filter((name) => name.endsWith(".md") && name !== "README.md");
for (const entry of entries) {
if (!index.includes(entry)) {
failures.push(`docs/${layer}/README.md does not index ${entry}`);
}
const content = await readFile(path.join(dir, entry), "utf8");
if (layer === "owner" && !/[\u3400-\u9fff]/u.test(content)) {
failures.push(`docs/owner/${entry} should be written in Chinese`);
}
if (layer === "community" && !content.includes("<!-- bilingual -->")) {
failures.push(`docs/community/${entry} is missing the bilingual marker`);
}
}
}
const requiredSkills = [
["docs-sync", "docs-sync"],
["palmtty-theme", "palmtty-theme"],
["palmtty-theme-review", "palmtty-theme-review"]
];
for (const [directory, name] of requiredSkills) {
const skillPath = path.join(root, ".agents", "skills", directory, "SKILL.md");
if (!(await exists(skillPath))) {
failures.push(`.agents/skills/${directory}/SKILL.md is missing`);
continue;
}
const skill = (await readFile(skillPath, "utf8")).replaceAll("\r\n", "\n");
const frontmatter = skill.match(/^---\n([\s\S]*?)\n---/);
const lines = frontmatter?.[1].split("\n") ?? [];
const hasName = lines.some((line) => line.trim() === `name: ${name}`);
const hasDescription = lines.some(
(line) => line.trim().startsWith("description:")
);
if (!frontmatter || !hasName || !hasDescription) {
failures.push(
`.agents/skills/${directory}/SKILL.md is missing required Agent Skills frontmatter`
);
}
}
if (failures.length) {
console.error("Documentation contract check failed:");
for (const failure of failures) console.error(`- ${failure}`);
process.exit(1);
}
console.log("Documentation contract check passed.");