mirror of
https://github.com/morluto/rea.git
synced 2026-10-12 02:19:24 +00:00
88 lines
3.1 KiB
JavaScript
88 lines
3.1 KiB
JavaScript
import { mkdir, readFile, readdir, rm } 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 sourceRoot = join(root, ".agents/skills/reverse-engineer-anything");
|
|
const outputRoot = join(root, "skills/reverse-engineer-anything");
|
|
const check = arguments_.has("--check");
|
|
const current = (
|
|
await readFile(join(sourceRoot, "SKILL.md"), "utf8")
|
|
).replaceAll("\r\n", "\n");
|
|
const cacheBust = String(Date.now());
|
|
const { CATALOG_IDENTITY } = await import(
|
|
`${pathToFileURL(join(root, "dist/catalogIdentity.js")).href}?${cacheBust}`
|
|
);
|
|
const { PRODUCT_IDENTITY } = await import(
|
|
`${pathToFileURL(join(root, "dist/identity.js")).href}?${cacheBust}`
|
|
);
|
|
if (/^\s{2}(?:tool_count|catalog_digest):/mu.test(current))
|
|
throw new Error(
|
|
"Catalog metadata belongs in the generated skill, not the authored source",
|
|
);
|
|
const versionLine = /^ {2}version: "[^"\r\n]+"$/mu;
|
|
if (!versionLine.test(current))
|
|
throw new Error("Missing authored skill version");
|
|
// A shipped skill must run the version it ships with, not whatever `@latest` resolves to.
|
|
const latestSpecifier = PRODUCT_IDENTITY.packageSpecifier;
|
|
const pinnedSpecifier = PRODUCT_IDENTITY.registrationPackageSpecifier;
|
|
const source = current
|
|
.replace(
|
|
versionLine,
|
|
`$&\n tool_count: ${String(CATALOG_IDENTITY.counts.mcp_tools)}`,
|
|
)
|
|
.replaceAll(latestSpecifier, pinnedSpecifier);
|
|
const paths = await filePaths(sourceRoot);
|
|
// Rebuild this owned output directory so removed references cannot survive a build.
|
|
if (!check) await rm(outputRoot, { recursive: true, force: true });
|
|
for (const relativePath of paths) {
|
|
const path = join(outputRoot, relativePath);
|
|
if (!check) await mkdir(dirname(path), { recursive: true });
|
|
await ensureGeneratedFile({
|
|
path,
|
|
source:
|
|
relativePath === "SKILL.md"
|
|
? source
|
|
: (await readFile(join(sourceRoot, relativePath), "utf8")).replaceAll(
|
|
"\r\n",
|
|
"\n",
|
|
),
|
|
check,
|
|
generateCommand: "npm run docs:generate",
|
|
});
|
|
}
|
|
if (
|
|
check &&
|
|
JSON.stringify(await filePaths(outputRoot)) !== JSON.stringify(paths)
|
|
)
|
|
throw new Error(
|
|
"Generated skill file inventory drifted; run npm run docs:generate",
|
|
);
|
|
|
|
async function filePaths(directory, prefix = "") {
|
|
const paths = [];
|
|
for (const entry of (await readdir(directory, { withFileTypes: true })).sort(
|
|
(left, right) =>
|
|
left.name < right.name ? -1 : left.name > right.name ? 1 : 0,
|
|
)) {
|
|
const relativePath = prefix === "" ? entry.name : `${prefix}/${entry.name}`;
|
|
if (entry.isDirectory())
|
|
paths.push(
|
|
...(await filePaths(join(directory, entry.name), relativePath)),
|
|
);
|
|
else if (entry.isFile()) paths.push(relativePath);
|
|
else
|
|
throw new Error(
|
|
`Skill bundle requires regular files or directories: ${relativePath}`,
|
|
);
|
|
}
|
|
return paths;
|
|
}
|