fix(skills): parse metadata only from YAML frontmatter

This commit is contained in:
morluto committed 2026-10-26 15:49:58 +08:00
1 parent 0f64f1f855
commit 258a1dd3b7
11 files changed
+410 -31

No files matched your search

+10 -2
View File
@@ -3,6 +3,12 @@ import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { ensureGeneratedFile } from "./lib/generated-file.mjs"; import { ensureGeneratedFile } from "./lib/generated-file.mjs";
import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
const { parseSkillManifestMetadata } = await importVerifierSource(
new URL("../src/domain/skillManifest.ts", import.meta.url).href,
import.meta.url,
);
const arguments_ = new Set(process.argv.slice(2)); const arguments_ = new Set(process.argv.slice(2));
for (const argument of arguments_) for (const argument of arguments_)
@@ -25,8 +31,10 @@ const skill = await readFile(
join(root, ".agents/skills/reverse-engineer-anything/SKILL.md"), join(root, ".agents/skills/reverse-engineer-anything/SKILL.md"),
"utf8", "utf8",
); );
const skillVersion = /^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(skill)?.[1]; const skillMetadata = parseSkillManifestMetadata(skill);
if (skillVersion === undefined) if (!skillMetadata.ok) throw skillMetadata.error;
const skillVersion = skillMetadata.value.version;
if (skillVersion === null)
throw new Error("Missing reverse-engineer-anything skill metadata version"); throw new Error("Missing reverse-engineer-anything skill metadata version");
const versionOf = (name) => { const versionOf = (name) => {
const value = lock.packages?.[`node_modules/${name}`]?.version; const value = lock.packages?.[`node_modules/${name}`]?.version;
+12 -13
View File
@@ -3,6 +3,12 @@ import { dirname, join } from "node:path";
import { fileURLToPath, pathToFileURL } from "node:url"; import { fileURLToPath, pathToFileURL } from "node:url";
import { ensureGeneratedFile } from "./lib/generated-file.mjs"; import { ensureGeneratedFile } from "./lib/generated-file.mjs";
import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
const { addSkillManifestToolCount } = await importVerifierSource(
new URL("../src/domain/skillManifest.ts", import.meta.url).href,
import.meta.url,
);
const arguments_ = new Set(process.argv.slice(2)); const arguments_ = new Set(process.argv.slice(2));
for (const argument of arguments_) for (const argument of arguments_)
@@ -23,22 +29,15 @@ const { FULL_CATALOG_IDENTITY } = await import(
const { PRODUCT_IDENTITY } = await import( const { PRODUCT_IDENTITY } = await import(
`${pathToFileURL(join(root, "dist/identity.js")).href}?${cacheBust}` `${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. // A shipped skill must run the version it ships with, not whatever `@latest` resolves to.
const latestSpecifier = PRODUCT_IDENTITY.packageSpecifier; const latestSpecifier = PRODUCT_IDENTITY.packageSpecifier;
const pinnedSpecifier = PRODUCT_IDENTITY.registrationPackageSpecifier; const pinnedSpecifier = PRODUCT_IDENTITY.registrationPackageSpecifier;
const source = current const generated = addSkillManifestToolCount(
.replace( current,
versionLine, FULL_CATALOG_IDENTITY.counts.mcp_tools,
`$&\n tool_count: ${String(FULL_CATALOG_IDENTITY.counts.mcp_tools)}`, );
) if (!generated.ok) throw generated.error;
.replaceAll(latestSpecifier, pinnedSpecifier); const source = generated.value.replaceAll(latestSpecifier, pinnedSpecifier);
const paths = await filePaths(sourceRoot); const paths = await filePaths(sourceRoot);
// Rebuild this owned output directory so removed references cannot survive a build. // Rebuild this owned output directory so removed references cannot survive a build.
if (!check) await rm(outputRoot, { recursive: true, force: true }); if (!check) await rm(outputRoot, { recursive: true, force: true });
+10 -1
View File
@@ -1,3 +1,5 @@
import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
import { createHash } from "node:crypto"; import { createHash } from "node:crypto";
import { import {
chmod, chmod,
@@ -22,6 +24,11 @@ import {
} from "./lib/verifier-cleanup.mjs"; } from "./lib/verifier-cleanup.mjs";
import { completeVerifierRun, createVerifierRun } from "./lib/verifier-run.mjs"; import { completeVerifierRun, createVerifierRun } from "./lib/verifier-run.mjs";
const { parseSkillManifestMetadata } = await importVerifierSource(
new URL("../src/domain/skillManifest.ts", import.meta.url).href,
import.meta.url,
);
const { evaluateCodexEvents } = await tsImport( const { evaluateCodexEvents } = await tsImport(
new URL("../tests/evaluation/model-evals/CodexAgentEval.ts", import.meta.url) new URL("../tests/evaluation/model-evals/CodexAgentEval.ts", import.meta.url)
.href, .href,
@@ -148,8 +155,10 @@ await withVerifierCleanup(
join(skillDestination, "SKILL.md"), join(skillDestination, "SKILL.md"),
"utf8", "utf8",
); );
const skillMetadata = parseSkillManifestMetadata(installedSkill);
if ( if (
!installedSkill.includes(` version: "${PRODUCT_IDENTITY.skillVersion}"`) !skillMetadata.ok ||
skillMetadata.value.version !== PRODUCT_IDENTITY.skillVersion
) )
throw new Error("Setup did not install the current packaged REA skill"); throw new Error("Setup did not install the current packaged REA skill");
const requestedScenarioIds = new Set( const requestedScenarioIds = new Set(
+11 -4
View File
@@ -1,3 +1,5 @@
import { importVerifierSource } from "./lib/verifier-source-loader.mjs";
import { mkdir } from "node:fs/promises"; import { mkdir } from "node:fs/promises";
import { join } from "node:path"; import { join } from "node:path";
import { FULL_CATALOG_IDENTITY } from "../dist/catalogIdentity.js"; import { FULL_CATALOG_IDENTITY } from "../dist/catalogIdentity.js";
@@ -5,6 +7,11 @@ import { FULL_CATALOG_IDENTITY } from "../dist/catalogIdentity.js";
import { parseTarListing } from "./lib/verify-package-listing.mjs"; import { parseTarListing } from "./lib/verify-package-listing.mjs";
import { verifyPackedBridge } from "./verify-packed-bridge.mjs"; import { verifyPackedBridge } from "./verify-packed-bridge.mjs";
const { parseSkillManifestMetadata } = await importVerifierSource(
new URL("../src/domain/skillManifest.ts", import.meta.url).href,
import.meta.url,
);
/** Create a tarball and assert production packaging constraints. */ /** Create a tarball and assert production packaging constraints. */
export async function verifyPackagePack({ root, workspace, commands }) { export async function verifyPackagePack({ root, workspace, commands }) {
const packDirectory = join(workspace, "pack"); const packDirectory = join(workspace, "pack");
@@ -55,11 +62,11 @@ export async function verifyPackagePack({ root, workspace, commands }) {
"package/skills/reverse-engineer-anything/SKILL.md", "package/skills/reverse-engineer-anything/SKILL.md",
]) ])
).stdout; ).stdout;
const skillMetadata = parseSkillManifestMetadata(skill);
if ( if (
!skill.includes( !skillMetadata.ok ||
` tool_count: ${String(FULL_CATALOG_IDENTITY.counts.mcp_tools)}\n`, skillMetadata.value.toolCount !== FULL_CATALOG_IDENTITY.counts.mcp_tools ||
) || skillMetadata.value.hasCatalogDigest
skill.includes(" catalog_digest:")
) )
throw new Error( throw new Error(
"packaged skill metadata did not match the catalog shipped in the package", "packaged skill metadata did not match the catalog shipped in the package",
+8 -9
View File
@@ -2,6 +2,7 @@ import { copyFile, lstat, mkdir, readFile } from "node:fs/promises";
import { constants as fsConstants } from "node:fs"; import { constants as fsConstants } from "node:fs";
import { dirname, join } from "node:path"; import { dirname, join } from "node:path";
import { err, ok, type Result } from "../domain/result.js"; import { err, ok, type Result } from "../domain/result.js";
import { parseSkillManifestMetadata } from "../domain/skillManifest.js";
import { readBoundedFileBytes } from "../process/BoundedFileBytes.js"; import { readBoundedFileBytes } from "../process/BoundedFileBytes.js";
import type { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js"; import type { ArtifactResourceScope } from "../artifacts/ArtifactResourceScope.js";
import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js"; import { ArtifactReaderFailure } from "../artifacts/ArtifactReader.js";
@@ -698,17 +699,15 @@ export const readInstalledSkillIdentity = async (
? inspection.value.manifest ? inspection.value.manifest
: inspection.error.manifest; : inspection.error.manifest;
const issues = inspection.ok ? [] : inspection.error.issues; const issues = inspection.ok ? [] : inspection.error.issues;
const toolCount = const parsedMetadata =
content === undefined content === undefined ? undefined : parseSkillManifestMetadata(content);
? undefined const metadata = parsedMetadata?.ok
: /^\s{2}tool_count:\s*(\d+)\s*$/mu.exec(content)?.[1]; ? parsedMetadata.value
: parsedMetadata?.error.metadata;
return { return {
canonical: inspection.ok && inspection.value.canonical, canonical: inspection.ok && inspection.value.canonical,
version: version: metadata?.version ?? null,
content === undefined toolCount: metadata?.toolCount ?? null,
? null
: (/^\s{2}version:\s*"([^"]+)"\s*$/mu.exec(content)?.[1] ?? null),
toolCount: toolCount === undefined ? null : Number.parseInt(toolCount, 10),
...(issues.length === 0 ? {} : { issues }), ...(issues.length === 0 ? {} : { issues }),
}; };
}; };
+120
View File
@@ -0,0 +1,120 @@
import { expect, it } from "vitest";
import {
addSkillManifestToolCount,
parseSkillManifestMetadata,
} from "./skillManifest.js";
const manifest = (metadata: string, body = "\n# Instructions\n") =>
`---\nname: reverse-engineer-anything\nmetadata:\n${metadata}---\n${body}`;
it.each(["'49'", '"49"'])(
"reads semantic metadata with version %s",
(version) => {
expect(
parseSkillManifestMetadata(
manifest(` version: ${version}\n tool_count: 0\n`),
),
).toEqual({
ok: true,
value: {
version: "49",
toolCount: 0,
hasToolCount: true,
hasCatalogDigest: false,
},
});
},
);
it("does not read counterfeit metadata from the Markdown body", () => {
const content =
'---\nname: reverse-engineer-anything\n---\n\n```yaml\nmetadata:\n version: "counterfeit"\n tool_count: 42\n```\n';
expect(parseSkillManifestMetadata(content)).toEqual({
ok: true,
value: {
version: null,
toolCount: null,
hasToolCount: false,
hasCatalogDigest: false,
},
});
expect(addSkillManifestToolCount(content, 5)).toMatchObject({
ok: false,
error: { message: "Missing authored skill version" },
});
});
it.each(["-1", "1.5", "9007199254740992", '"7"', "null"])(
"retains a valid version when tool_count is invalid: %s",
(count) => {
expect(
parseSkillManifestMetadata(
manifest(` version: '49'\n tool_count: ${count}\n`),
),
).toMatchObject({
ok: false,
error: {
_tag: "SkillManifestMetadataError",
metadata: { version: "49", toolCount: null },
},
});
},
);
it.each([
" version: [\n",
" version: '49'\n version: '50'\n",
" version: '49'\n tool_count: 1\n tool_count: 2\n",
])("rejects invalid or duplicate YAML metadata", (metadata) => {
expect(parseSkillManifestMetadata(manifest(metadata))).toMatchObject({
ok: false,
});
});
it("retains a valid count when the version is invalid", () => {
expect(
parseSkillManifestMetadata(manifest(" version: 49\n tool_count: 7\n")),
).toMatchObject({
ok: false,
error: { metadata: { version: null, toolCount: 7 } },
});
});
it("updates bounded frontmatter without changing Markdown examples or comments", () => {
const body =
'\n# Instructions\n\n```yaml\nmetadata:\n version: "body-only"\n tool_count: 99\n```\n';
const content = manifest(" version: '49' # keep version comment\n", body);
const generated = addSkillManifestToolCount(content, 7);
expect(generated.ok).toBe(true);
if (!generated.ok) throw generated.error;
expect(generated.value.endsWith(body)).toBe(true);
expect(generated.value).toContain("version: '49' # keep version comment");
expect(parseSkillManifestMetadata(generated.value)).toMatchObject({
ok: true,
value: { version: "49", toolCount: 7 },
});
});
it.each(["tool_count: 0", "catalog_digest: abc"])(
"rejects authored generated metadata: %s",
(field) => {
expect(
addSkillManifestToolCount(manifest(` version: '49'\n ${field}\n`), 7),
).toMatchObject({
ok: false,
error: { message: expect.stringContaining("Catalog metadata belongs") },
});
},
);
it("rejects unsupported YAML tags without treating their scalars as metadata", () => {
expect(
parseSkillManifestMetadata(manifest(" version: !custom '49'\n")),
).toMatchObject({
ok: false,
error: {
message: expect.stringContaining("Unsupported skill YAML frontmatter"),
metadata: { version: null },
},
});
});
+165
View File
@@ -0,0 +1,165 @@
import { isMap, parseDocument, type Document } from "yaml";
import { err, ok, type Result } from "./result.js";
export interface SkillManifestMetadata {
readonly version: string | null;
readonly toolCount: number | null;
readonly hasToolCount: boolean;
readonly hasCatalogDigest: boolean;
}
export class SkillManifestMetadataError extends Error {
readonly _tag = "SkillManifestMetadataError" as const;
constructor(
message: string,
readonly metadata: SkillManifestMetadata,
) {
super(message);
}
}
const emptyMetadata = (): SkillManifestMetadata => ({
version: null,
toolCount: null,
hasToolCount: false,
hasCatalogDigest: false,
});
/** The frontmatter range owns YAML; body examples never supply metadata. */
const readFrontmatter = (
content: string,
): Result<
{
document: Document;
body: string;
},
SkillManifestMetadataError
> => {
const opening = /^---\r?\n/u.exec(content);
if (opening === null)
return err(
new SkillManifestMetadataError(
"Skill manifest has no YAML frontmatter",
emptyMetadata(),
),
);
const remainder = content.slice(opening[0].length);
const closing = /^---(?:\r?\n|$)/mu.exec(remainder);
if (closing === null)
return err(
new SkillManifestMetadataError(
"Skill manifest YAML frontmatter is unterminated",
emptyMetadata(),
),
);
const document = parseDocument(remainder.slice(0, closing.index), {
logLevel: "silent",
});
if (document.errors.length > 0)
return err(
new SkillManifestMetadataError(
`Invalid skill YAML frontmatter: ${document.errors.map((error) => error.message).join("; ")}`,
emptyMetadata(),
),
);
if (document.warnings.length > 0)
return err(
new SkillManifestMetadataError(
`Unsupported skill YAML frontmatter: ${document.warnings.map((warning) => warning.message).join("; ")}`,
emptyMetadata(),
),
);
if (!isMap(document.contents))
return err(
new SkillManifestMetadataError(
"Skill YAML frontmatter must be a mapping",
emptyMetadata(),
),
);
return ok({
document,
body: remainder.slice(closing.index + closing[0].length),
});
};
const metadataOf = (
document: Document,
): Result<SkillManifestMetadata, SkillManifestMetadataError> => {
const metadata = document.get("metadata", true);
if (metadata === undefined) return ok(emptyMetadata());
if (!isMap(metadata))
return err(
new SkillManifestMetadataError(
"Skill metadata must be a mapping",
emptyMetadata(),
),
);
const version = document.getIn(["metadata", "version"]);
const toolCount = document.getIn(["metadata", "tool_count"]);
const facts: SkillManifestMetadata = {
version: typeof version === "string" && version.length > 0 ? version : null,
toolCount:
typeof toolCount === "number" &&
Number.isSafeInteger(toolCount) &&
toolCount >= 0
? toolCount
: null,
hasToolCount: metadata.has("tool_count"),
hasCatalogDigest: metadata.has("catalog_digest"),
};
const issues = [];
if (metadata.has("version") && facts.version === null)
issues.push("version must be a nonempty string");
if (facts.hasToolCount && facts.toolCount === null)
issues.push("tool_count must be a safe nonnegative integer");
return issues.length === 0
? ok(facts)
: err(
new SkillManifestMetadataError(
`Invalid skill metadata: ${issues.join("; ")}`,
facts,
),
);
};
export const parseSkillManifestMetadata = (
content: string,
): Result<SkillManifestMetadata, SkillManifestMetadataError> => {
const frontmatter = readFrontmatter(content);
return frontmatter.ok ? metadataOf(frontmatter.value.document) : frontmatter;
};
/** Update only authored YAML frontmatter, retaining the instruction body. */
export const addSkillManifestToolCount = (
content: string,
toolCount: number,
): Result<string, SkillManifestMetadataError> => {
const frontmatter = readFrontmatter(content);
if (!frontmatter.ok) return frontmatter;
const metadata = metadataOf(frontmatter.value.document);
if (!metadata.ok) return metadata;
if (metadata.value.version === null)
return err(
new SkillManifestMetadataError(
"Missing authored skill version",
metadata.value,
),
);
if (metadata.value.hasToolCount || metadata.value.hasCatalogDigest)
return err(
new SkillManifestMetadataError(
"Catalog metadata belongs in the generated skill, not the authored source",
metadata.value,
),
);
if (!Number.isSafeInteger(toolCount) || toolCount < 0)
return err(
new SkillManifestMetadataError(
"Generated tool_count must be a safe nonnegative integer",
metadata.value,
),
);
const { document, body } = frontmatter.value;
document.setIn(["metadata", "tool_count"], toolCount);
return ok(`---\n${document.toString({ lineWidth: 0 })}---\n${body}`);
};
@@ -205,6 +205,9 @@ const createBuildCacheFixture = async (): Promise<string> => {
"scripts/clean-build-output.mjs", "scripts/clean-build-output.mjs",
"scripts/generate-package-metadata.mjs", "scripts/generate-package-metadata.mjs",
"scripts/lib/generated-file.mjs", "scripts/lib/generated-file.mjs",
"scripts/lib/verifier-source-loader.mjs",
"src/domain/skillManifest.ts",
"src/domain/result.ts",
".agents/skills/reverse-engineer-anything/SKILL.md", ".agents/skills/reverse-engineer-anything/SKILL.md",
"src/generatedPackageMetadata.ts", "src/generatedPackageMetadata.ts",
]; ];
@@ -1,5 +1,5 @@
import { execFile } from "node:child_process"; import { execFile } from "node:child_process";
import { copyFile, mkdir, readFile, rm, writeFile } from "node:fs/promises"; import { copyFile, cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
import { dirname, join } from "node:path"; import { dirname, join } from "node:path";
import { fileURLToPath } from "node:url"; import { fileURLToPath } from "node:url";
import { promisify } from "node:util"; import { promisify } from "node:util";
@@ -17,10 +17,19 @@ async function fixture(eol = "\n") {
for (const path of [ for (const path of [
"scripts/generate-skill-metadata.mjs", "scripts/generate-skill-metadata.mjs",
"scripts/lib/generated-file.mjs", "scripts/lib/generated-file.mjs",
"scripts/lib/verifier-source-loader.mjs",
"src/domain/skillManifest.ts",
"src/domain/result.ts",
]) { ]) {
await mkdir(dirname(join(root, path)), { recursive: true }); await mkdir(dirname(join(root, path)), { recursive: true });
await copyFile(join(repositoryRoot, path), join(root, path)); await copyFile(join(repositoryRoot, path), join(root, path));
} }
await mkdir(join(root, "node_modules"));
await cp(
join(repositoryRoot, "node_modules/yaml"),
join(root, "node_modules/yaml"),
{ recursive: true },
);
const sourceRoot = join(root, ".agents/skills/reverse-engineer-anything"); const sourceRoot = join(root, ".agents/skills/reverse-engineer-anything");
const outputRoot = join(root, "skills/reverse-engineer-anything"); const outputRoot = join(root, "skills/reverse-engineer-anything");
await mkdir(join(sourceRoot, "references"), { recursive: true }); await mkdir(join(sourceRoot, "references"), { recursive: true });
@@ -81,7 +90,13 @@ it.each(["\n", "\r\n"])(
expect( expect(
await readFile(join(f.sourceRoot, "references/guide.md"), "utf8"), await readFile(join(f.sourceRoot, "references/guide.md"), "utf8"),
).toBe(`# Guide${eol}Preserve evidence.${eol}`); ).toBe(`# Guide${eol}Preserve evidence.${eol}`);
await expect(f.generate("--check")).resolves.toMatchObject({ stderr: "" }); const checked = await f.generate("--check");
expect(
checked.stderr.replace(
/^\(node:\d+\) ExperimentalWarning: stripTypeScriptTypes is an experimental feature and might change at any time\r?\n\(Use `node --trace-warnings \.\.\.` to show where the warning was created\)\r?\n$/u,
"",
),
).toBe("");
}, },
); );
@@ -143,3 +158,30 @@ it("rejects catalog-dependent metadata in the authored source", async () => {
), ),
}); });
}); });
it("generates metadata from quoted frontmatter while preserving body metadata examples", async () => {
const f = await fixture();
const body =
'\n# Instructions\n\n```yaml\nmetadata:\n version: "body-only"\n tool_count: 99\n catalog_digest: example\n```\n';
await writeFile(
join(f.sourceRoot, "SKILL.md"),
"---\nname: reverse-engineer-anything\nmetadata:\n version: '33'\n---\n" +
body,
);
await f.generate();
const generated = await readFile(join(f.outputRoot, "SKILL.md"), "utf8");
expect(generated.endsWith(body)).toBe(true);
expect(generated).toContain("version: '33'");
expect(generated).toContain("tool_count: 139");
});
it("rejects body-only versions instead of generating metadata inside examples", async () => {
const f = await fixture();
await writeFile(
join(f.sourceRoot, "SKILL.md"),
'---\nname: reverse-engineer-anything\n---\n\n```yaml\nmetadata:\n version: "body-only"\n```\n',
);
await expect(f.generate()).rejects.toMatchObject({
stderr: expect.stringContaining("Missing authored skill version"),
});
});
@@ -720,3 +720,26 @@ it("doctor verifies installed instructions and references even when metadata is
); );
} }
}); });
it("reads installed frontmatter metadata without treating body examples as identity", async () => {
const home = await createTestTempDirectory("rea-installed-metadata-");
const installed = await installCanonicalSkill(home, ["claude_code"]);
expect(installed.ok).toBe(true);
const destination = skillDestinations(home, ["claude_code"])[0];
if (destination === undefined) throw new Error("Missing skill destination");
const manifest = join(destination.path, "SKILL.md");
await writeFile(
manifest,
"---\nname: reverse-engineer-anything\nmetadata:\n version: '49'\n tool_count: -1\n---\n\n```yaml\nmetadata:\n version: \"counterfeit\"\n tool_count: 42\n```\n",
);
expect(await readInstalledSkillIdentity(home, ["claude_code"])).toMatchObject(
{ version: "49", toolCount: null, canonical: false },
);
await writeFile(
manifest,
'---\nname: reverse-engineer-anything\n---\n\n```yaml\nmetadata:\n version: "counterfeit"\n tool_count: 42\n```\n',
);
expect(await readInstalledSkillIdentity(home, ["claude_code"])).toMatchObject(
{ version: null, toolCount: null, canonical: false },
);
});
+4
View File
@@ -64,6 +64,9 @@
"package-lock.json", "package-lock.json",
"scripts/generate-package-metadata.mjs", "scripts/generate-package-metadata.mjs",
"scripts/lib/generated-file.mjs", "scripts/lib/generated-file.mjs",
"scripts/lib/verifier-source-loader.mjs",
"src/domain/skillManifest.ts",
"src/domain/result.ts",
".agents/skills/reverse-engineer-anything/SKILL.md", ".agents/skills/reverse-engineer-anything/SKILL.md",
"src/generatedPackageMetadata.ts" "src/generatedPackageMetadata.ts"
] ]
@@ -213,6 +216,7 @@
"package-lock.json", "package-lock.json",
"scripts/generate-skill-metadata.mjs", "scripts/generate-skill-metadata.mjs",
"scripts/lib/generated-file.mjs", "scripts/lib/generated-file.mjs",
"scripts/lib/verifier-source-loader.mjs",
".agents/skills/reverse-engineer-anything/**" ".agents/skills/reverse-engineer-anything/**"
], ],
"outputs": ["skills/**"] "outputs": ["skills/**"]