feat(M1): Baseline verification, quality tooling foundation, and current-state report

This commit is contained in:
Stefano Fiorini
2026-05-03 19:26:55 -05:00
parent 2deab1c1b4
commit 0443381aa0
19 changed files with 4100 additions and 2 deletions
+137
View File
@@ -0,0 +1,137 @@
#!/usr/bin/env node
/**
* run-link-check.mjs — markdown link-check runner (M1, S-104)
*
* Runs markdown-link-check across README.md, docs/, and every SKILL.md
* (excluding node_modules and generated agent-variant directories).
*
* Modes:
* --offline (default) — checks only repo-relative links and #anchor links.
* All http/https links are ignored. Safe for CI and local dev
* without network access.
* --online — checks all links, including external URLs, with timeouts
* and retries as configured in markdown-link-check.online.json.
*
* Exit codes:
* 0 — all checked links are alive (or ignored in offline mode)
* 1 — one or more broken links found
*/
import { spawnSync } from "node:child_process";
import { readdirSync, existsSync } from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const REPO_ROOT = path.resolve(__dirname, "../..");
// ── CLI arguments ──────────────────────────────────────────────────────────
const online = process.argv.includes("--online");
const configFile = online
? path.join(REPO_ROOT, "markdown-link-check.online.json")
: path.join(REPO_ROOT, "markdown-link-check.json");
// ── File discovery ─────────────────────────────────────────────────────────
const SKIP_PATHS = new Set([
"skills/atlassian/codex",
"skills/atlassian/claude-code",
"skills/atlassian/cursor",
"skills/atlassian/opencode",
"skills/atlassian/pi",
"skills/web-automation/claude-code",
"skills/web-automation/cursor",
"skills/web-automation/opencode",
"skills/web-automation/pi",
"pi-package",
]);
function shouldSkip(absPath) {
const rel = path.relative(REPO_ROOT, absPath);
for (const skip of SKIP_PATHS) {
if (rel === skip || rel.startsWith(skip + path.sep)) return true;
}
const parts = rel.split(path.sep);
return parts.includes("node_modules");
}
function collectMarkdownFiles(dir) {
const found = [];
let entries;
try {
entries = readdirSync(dir, { withFileTypes: true });
} catch {
return found;
}
for (const entry of entries) {
const full = path.join(dir, entry.name);
if (shouldSkip(full)) continue;
if (entry.isDirectory()) {
found.push(...collectMarkdownFiles(full));
} else if (
entry.isFile() &&
(entry.name.endsWith(".md") || entry.name === "SKILL.md")
) {
found.push(full);
}
}
return found;
}
// ── Collect target files ───────────────────────────────────────────────────
const files = [
path.join(REPO_ROOT, "README.md"),
...collectMarkdownFiles(path.join(REPO_ROOT, "docs")),
...collectMarkdownFiles(path.join(REPO_ROOT, "skills")),
].filter(existsSync);
// De-duplicate (README.md could appear twice)
const uniqueFiles = [...new Set(files)];
if (uniqueFiles.length === 0) {
console.log("link-check: no markdown files found — nothing to check.");
process.exit(0);
}
console.log(
`link-check: checking ${uniqueFiles.length} file(s) ` +
`[mode: ${online ? "online" : "offline"}]…`
);
// ── Run markdown-link-check ────────────────────────────────────────────────
const mlcBin = path.join(
REPO_ROOT,
"node_modules",
".bin",
"markdown-link-check"
);
let failures = 0;
for (const file of uniqueFiles.sort()) {
const result = spawnSync(
mlcBin,
["--config", configFile, "--quiet", file],
{ encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }
);
const output = (result.stdout + result.stderr).trim();
const rel = path.relative(REPO_ROOT, file);
if (result.status !== 0) {
failures += 1;
console.error(`\n--- ${rel} ---`);
if (output) console.error(output);
}
}
if (failures > 0) {
console.error(
`\nlink-check: ${failures} file(s) have broken links. ` +
`See docs/CLEANUP-BASELINE.md for the as-is baseline.`
);
process.exit(1);
} else {
console.log("link-check: all links OK.");
process.exit(0);
}