#!/usr/bin/env node /** * add-frontmatter.mjs — one-shot helper that ensures every documentation * file under docs//*.md and docs/README.md has a YAML frontmatter * header with `title`, `version`, and `lastUpdated`. * * Idempotent: docs that already have a `---` block at the top are skipped * (the existing frontmatter is preserved as-is). Files without a leading * `# Title` heading fall back to the basename humanized as a title. * * Excludes: docs/i18n/, docs/screenshots/, docs/superpowers/, * docs/diagrams/exported/. Subfolder READMEs are included. * * Usage: node scripts/docs/add-frontmatter.mjs [--version X.Y.Z] [--date YYYY-MM-DD] */ import { promises as fs } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const ROOT = path.resolve(__dirname, "..", ".."); const DOCS_DIR = path.join(ROOT, "docs"); const EXCLUDE_PREFIXES = [ "docs/i18n/", "docs/screenshots/", "docs/superpowers/", "docs/diagrams/exported/", ]; const args = process.argv.slice(2); let version = "3.8.0"; let lastUpdated = "2026-05-13"; for (let i = 0; i < args.length; i += 1) { if (args[i] === "--version" && args[i + 1]) { version = args[i + 1]; i += 1; } else if (args[i] === "--date" && args[i + 1]) { lastUpdated = args[i + 1]; i += 1; } } async function walk(dir, files = []) { const entries = await fs.readdir(dir, { withFileTypes: true }); for (const entry of entries) { const full = path.join(dir, entry.name); if (entry.isDirectory()) { await walk(full, files); } else if (entry.isFile() && entry.name.endsWith(".md")) { files.push(full); } } return files; } function isExcluded(rel) { return EXCLUDE_PREFIXES.some((p) => rel === p || rel.startsWith(p)); } function hasFrontmatter(content) { return /^---\r?\n/.test(content); } function extractTopHeading(content) { const lines = content.replace(/^/, "").split(/\r?\n/); for (const line of lines) { const m = line.match(/^#\s+(.+?)\s*$/); if (m) return m[1].trim(); // Allow blank/HTML-comment lines before the first heading if (line.trim() === "" || /^