mirror of
https://github.com/morluto/rea.git
synced 2026-10-11 21:39:55 +00:00
146 lines
5.6 KiB
TypeScript
146 lines
5.6 KiB
TypeScript
import { execFile } from "node:child_process";
|
|
import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
|
|
import { dirname, join } from "node:path";
|
|
import { fileURLToPath } from "node:url";
|
|
import { promisify } from "node:util";
|
|
import { expect, it } from "vitest";
|
|
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
|
|
const exec = promisify(execFile);
|
|
const repositoryRoot = fileURLToPath(new URL("../../..", import.meta.url));
|
|
const authored =
|
|
'---\nname: reverse-engineer-anything\nmetadata:\n version: "33"\n---\n\n# Instructions\n\nInspect the caller-selected artifact.\n';
|
|
|
|
async function fixture(eol = "\n") {
|
|
const root = await createTestTempDirectory("rea-generated-skill-");
|
|
for (const path of [
|
|
"scripts/generate-skill-metadata.mjs",
|
|
"scripts/lib/generated-file.mjs",
|
|
]) {
|
|
await mkdir(dirname(join(root, path)), { recursive: true });
|
|
await copyFile(join(repositoryRoot, path), join(root, path));
|
|
}
|
|
const sourceRoot = join(root, ".agents/skills/reverse-engineer-anything");
|
|
const outputRoot = join(root, "skills/reverse-engineer-anything");
|
|
await mkdir(join(sourceRoot, "references"), { recursive: true });
|
|
await mkdir(join(root, "dist"));
|
|
await writeFile(join(root, "package.json"), '{"type":"module"}\n');
|
|
await writeFile(
|
|
join(root, "dist/identity.js"),
|
|
'export const PRODUCT_IDENTITY = { packageSpecifier: "rea-agents@latest", registrationPackageSpecifier: "[email protected]" };\n',
|
|
);
|
|
await writeFile(join(sourceRoot, "SKILL.md"), authored.replaceAll("\n", eol));
|
|
await writeFile(
|
|
join(sourceRoot, "references/guide.md"),
|
|
`# Guide${eol}Preserve evidence.${eol}`,
|
|
);
|
|
const identity = async (count: number, digest: string) =>
|
|
writeFile(
|
|
join(root, "dist/catalogIdentity.js"),
|
|
`export const CATALOG_IDENTITY = ${JSON.stringify({ counts: { mcp_tools: count }, digests: { combined_sha256: digest } })};\n`,
|
|
);
|
|
await identity(139, "a".repeat(64));
|
|
const generate = (...args: string[]) =>
|
|
exec(
|
|
process.execPath,
|
|
[join(root, "scripts/generate-skill-metadata.mjs"), ...args],
|
|
{ cwd: root },
|
|
);
|
|
return { sourceRoot, outputRoot, identity, generate };
|
|
}
|
|
|
|
it.each(["\n", "\r\n"])(
|
|
"generates revision-specific metadata without rewriting authored instructions (%j)",
|
|
async (eol) => {
|
|
const f = await fixture(eol);
|
|
await f.generate();
|
|
const first = await readFile(join(f.outputRoot, "SKILL.md"), "utf8");
|
|
expect(first).not.toContain("catalog_digest:");
|
|
expect(first).toContain("tool_count: 139");
|
|
expect(
|
|
first.replace(/^ {2}(?:tool_count|catalog_digest):[^\n]*\n/gmu, ""),
|
|
).toBe(authored);
|
|
expect(
|
|
await readFile(join(f.outputRoot, "references/guide.md"), "utf8"),
|
|
).toBe("# Guide\nPreserve evidence.\n");
|
|
await f.identity(139, "b".repeat(64));
|
|
await f.generate();
|
|
expect(await readFile(join(f.outputRoot, "SKILL.md"), "utf8")).toBe(first);
|
|
await f.identity(140, "b".repeat(64));
|
|
await expect(f.generate("--check")).rejects.toMatchObject({
|
|
stderr: expect.stringContaining("missing or stale"),
|
|
});
|
|
await f.generate();
|
|
expect(await readFile(join(f.outputRoot, "SKILL.md"), "utf8")).toContain(
|
|
"tool_count: 140",
|
|
);
|
|
expect(await readFile(join(f.sourceRoot, "SKILL.md"), "utf8")).toBe(
|
|
authored.replaceAll("\n", eol),
|
|
);
|
|
expect(
|
|
await readFile(join(f.sourceRoot, "references/guide.md"), "utf8"),
|
|
).toBe(`# Guide${eol}Preserve evidence.${eol}`);
|
|
await expect(f.generate("--check")).resolves.toMatchObject({ stderr: "" });
|
|
},
|
|
);
|
|
|
|
it("rejects missing, stale, and extra packaged references and removes deleted source files on rebuild", async () => {
|
|
const f = await fixture();
|
|
await f.generate();
|
|
const guide = join(f.outputRoot, "references/guide.md");
|
|
await rm(guide);
|
|
await expect(f.generate("--check")).rejects.toMatchObject({
|
|
stderr: expect.stringContaining("guide.md is missing or stale"),
|
|
});
|
|
await f.generate();
|
|
await writeFile(guide, "modified reference\n");
|
|
await expect(f.generate("--check")).rejects.toMatchObject({
|
|
stderr: expect.stringContaining("guide.md is missing or stale"),
|
|
});
|
|
await f.generate();
|
|
await writeFile(
|
|
join(f.outputRoot, "references/removed.md"),
|
|
"obsolete instructions\n",
|
|
);
|
|
await expect(f.generate("--check")).rejects.toMatchObject({
|
|
stderr: expect.stringContaining("file inventory drifted"),
|
|
});
|
|
await rm(join(f.sourceRoot, "references/guide.md"));
|
|
await f.generate();
|
|
await expect(readFile(guide)).rejects.toMatchObject({ code: "ENOENT" });
|
|
await expect(
|
|
readFile(join(f.outputRoot, "references/removed.md")),
|
|
).rejects.toMatchObject({ code: "ENOENT" });
|
|
await f.generate("--check");
|
|
});
|
|
|
|
it("pins authored `@latest` commands to the packaged version", async () => {
|
|
const f = await fixture();
|
|
await writeFile(
|
|
join(f.sourceRoot, "SKILL.md"),
|
|
authored.replace(
|
|
"Inspect the caller-selected artifact.",
|
|
"Run `npx -y rea-agents@latest doctor --json` first.",
|
|
),
|
|
);
|
|
await f.generate();
|
|
const generated = await readFile(join(f.outputRoot, "SKILL.md"), "utf8");
|
|
expect(generated).toContain("npx -y [email protected] doctor --json");
|
|
expect(generated).not.toContain("rea-agents@latest");
|
|
await f.generate("--check");
|
|
});
|
|
|
|
it("rejects catalog-dependent metadata in the authored source", async () => {
|
|
const f = await fixture();
|
|
await writeFile(
|
|
join(f.sourceRoot, "SKILL.md"),
|
|
authored.replace(' version: "33"', ' version: "33"\n tool_count: 139'),
|
|
);
|
|
await expect(f.generate()).rejects.toMatchObject({
|
|
stderr: expect.stringContaining(
|
|
"Catalog metadata belongs in the generated skill",
|
|
),
|
|
});
|
|
});
|