feat(setup): verify installed skill catalog identity

This commit is contained in:
morluto
2026-07-16 18:51:05 +08:00
parent 8de25d0230
commit b2d1f2affd
15 changed files with 203 additions and 78 deletions
+35
View File
@@ -0,0 +1,35 @@
import { readFile } from "node:fs/promises";
import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url";
import { ensureGeneratedFile } from "./lib/generated-file.mjs";
const arguments_ = new Set(process.argv.slice(2));
for (const argument of arguments_)
if (argument !== "--check")
throw new Error(`Unknown skill metadata option: ${argument}`);
const root = join(dirname(fileURLToPath(import.meta.url)), "..");
const path = join(root, "skills/rea-analysis/SKILL.md");
const current = await readFile(path, "utf8");
const { CATALOG_IDENTITY } = await import(
`${pathToFileURL(join(root, "dist/catalogIdentity.js")).href}?${String(Date.now())}`
);
const withCount = current.replace(
/^\s{2}tool_count:\s*\d+\s*$/mu,
` tool_count: ${String(CATALOG_IDENTITY.counts.mcp_tools)}`,
);
const digestLine = ` catalog_digest: "${CATALOG_IDENTITY.digests.combined_sha256}"`;
const source = /^\s{2}catalog_digest:/mu.test(withCount)
? withCount.replace(
/^\s{2}catalog_digest:\s*"[a-f0-9]{64}"\s*$/mu,
digestLine,
)
: withCount.replace(/^(\s{2}tool_count:\s*\d+\s*)$/mu, `$1\n${digestLine}`);
await ensureGeneratedFile({
path,
source,
check: arguments_.has("--check"),
generateCommand: "npm run docs:generate",
});
+13 -24
View File
@@ -10,26 +10,20 @@ const README_PATHS = [
];
const familyLabel = (id) => (id === "electron" ? "Electron" : id);
const tableCounts = (content, path, total) => {
const tableCounts = (content, path, expectedCounts) => {
const lines = content.split(/\r?\n/u);
const headingIndex = lines.findIndex(
(line) => line.startsWith("## ") && line.includes(String(total)),
);
if (headingIndex === -1) {
throw new Error(`Missing ${total}-tool heading in ${path}`);
}
const counts = [];
for (const line of lines.slice(headingIndex + 1)) {
if (line.startsWith("## ")) break;
if (!line.trim().startsWith("|") || /^\|\s*-/u.test(line)) continue;
const count = Number(line.split("|")[2]?.trim());
if (!Number.isInteger(count)) {
if (counts.length === 0) continue;
throw new Error(`Non-numeric tool count in ${path}`);
for (const [index, line] of lines.entries()) {
if (!/^\|\s*-/u.test(line)) continue;
const counts = [];
for (const row of lines.slice(index + 1)) {
if (!row.trim().startsWith("|")) break;
const count = Number(row.split("|")[2]?.trim());
if (!Number.isInteger(count)) break;
counts.push(count);
}
counts.push(count);
if (counts.length === expectedCounts.length) return counts;
}
return counts;
throw new Error(`Missing tool-family inventory table in ${path}`);
};
const requireText = (issues, path, content, expected) => {
@@ -42,16 +36,11 @@ export const documentationFactIssues = async (root, catalog) => {
const expectedCounts = catalog.tools.families.map(({ count }) => count);
for (const path of README_PATHS) {
const content = await readFile(join(root, path), "utf8");
requireText(
issues,
path,
content,
`MCP_tools-${String(catalog.tools.total)}`,
);
requireText(issues, path, content, "MCP-tool_catalog");
for (const client of catalog.setup_clients)
requireText(issues, path, content, client.display_name);
try {
const actualCounts = tableCounts(content, path, catalog.tools.total);
const actualCounts = tableCounts(content, path, expectedCounts);
if (JSON.stringify(actualCounts) !== JSON.stringify(expectedCounts))
issues.push(
`${path}: tool family counts ${JSON.stringify(actualCounts)} do not match ${JSON.stringify(expectedCounts)}`,