mirror of
https://github.com/vercel/vercel-plugin.git
synced 2026-09-14 15:39:47 +08:00
1d5f084780
Update build-manifest.ts to include bodyPath in each skill manifest entry pointing to the SKILL.md file. Update all SKILL.md frontmatter across all skills to ensure consistent formatting. Add vercel-config.mjs improvements for key-aware skill routing. Add tests/build-skill-map.test.ts for skill map construction. Add golden fixture JSON files (golden-bash-cap-collision, golden-bash-next-dev, golden-edit-middleware, golden-read-env-local, golden-read-vercel-json, golden-vercel-json-crons, golden-vercel-json-rewrites) for deterministic match engine validation. Add tests/fuzz-glob.test.ts and tests/fuzz-yaml.test.ts for property-based testing of the glob-to-regex and YAML parsers. Update snapshot files for injection output. Update validate.ts and validate.test.ts with command convention checks. Update HOOKS.md documentation. Update pretooluse-skill-inject.mjs, skill-map-frontmatter.mjs, and vercel-config.mjs hook modules. Bump plugin.json version from 0.2.0 to 0.3.0.
105 lines
3.3 KiB
TypeScript
105 lines
3.3 KiB
TypeScript
import { describe, expect, it } from "bun:test";
|
|
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
import { tmpdir } from "node:os";
|
|
import { join } from "node:path";
|
|
import { buildSkillMap, extractFrontmatter, parseSkillFrontmatter, scanSkillsDir } from "./skill-map-frontmatter.mjs";
|
|
|
|
describe("skill-map frontmatter regression coverage", () => {
|
|
it("extractFrontmatter should strip BOM and split frontmatter/body", () => {
|
|
const markdown = "\uFEFF---\nname: ai-sdk\ndescription: AI SDK skill\n---\n# Markdown body\n";
|
|
|
|
const { yaml, body } = extractFrontmatter(markdown);
|
|
|
|
expect(yaml).toBe("name: ai-sdk\ndescription: AI SDK skill");
|
|
expect(body).toBe("# Markdown body\n");
|
|
});
|
|
|
|
it("parseSkillFrontmatter should parse nested metadata with block-list arrays", () => {
|
|
const yaml = [
|
|
"name: ai-sdk",
|
|
"description: AI SDK skill",
|
|
"metadata:",
|
|
" priority: 8",
|
|
" pathPatterns:",
|
|
" - app/api/chat/**",
|
|
" - src/lib/ai/**",
|
|
].join("\n");
|
|
|
|
const parsed = parseSkillFrontmatter(yaml);
|
|
|
|
expect(parsed.name).toBe("ai-sdk");
|
|
expect(parsed.metadata.priority).toBe(8);
|
|
expect(typeof parsed.metadata.priority).toBe("number");
|
|
expect(parsed.metadata.pathPatterns).toEqual(["app/api/chat/**", "src/lib/ai/**"]);
|
|
expect(parsed.metadata.pathPatterns?.every((item: string) => typeof item === "string")).toBe(true);
|
|
});
|
|
|
|
it("parseSkillFrontmatter should parse inline arrays and preserve regex-like strings", () => {
|
|
const yaml = [
|
|
"name: ai-sdk",
|
|
"metadata:",
|
|
" bashPatterns: ['^bun test$', '^pnpm lint$']",
|
|
].join("\n");
|
|
|
|
const parsed = parseSkillFrontmatter(yaml);
|
|
|
|
expect(parsed.metadata.bashPatterns).toEqual(["^bun test$", "^pnpm lint$"]);
|
|
});
|
|
|
|
it("buildSkillMap should accept deprecated filePattern with deprecation warning", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "vercel-plugin-skillmap-"));
|
|
|
|
try {
|
|
const fooDir = join(root, "foo");
|
|
mkdirSync(fooDir, { recursive: true });
|
|
writeFileSync(
|
|
join(fooDir, "SKILL.md"),
|
|
[
|
|
"---",
|
|
"name: foo",
|
|
"metadata:",
|
|
" filePattern: app/api/chat/**",
|
|
"---",
|
|
"# Foo",
|
|
].join("\n"),
|
|
);
|
|
|
|
const result = buildSkillMap(root);
|
|
|
|
expect(result.skills.foo.pathPatterns).toEqual(["app/api/chat/**"]);
|
|
expect(
|
|
result.warnings.some((warning: string) => warning.includes("filePattern is deprecated")),
|
|
).toBe(true);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
|
|
it("scanSkillsDir should report invalid YAML via diagnostics instead of throwing", () => {
|
|
const root = mkdtempSync(join(tmpdir(), "vercel-plugin-skillmap-"));
|
|
|
|
try {
|
|
const brokenDir = join(root, "broken");
|
|
mkdirSync(brokenDir, { recursive: true });
|
|
writeFileSync(
|
|
join(brokenDir, "SKILL.md"),
|
|
[
|
|
"---",
|
|
"name: broken",
|
|
"metadata:",
|
|
"\tpriority: 3",
|
|
"---",
|
|
"# Broken",
|
|
].join("\n"),
|
|
);
|
|
|
|
const result = scanSkillsDir(root);
|
|
|
|
expect(result.diagnostics.length).toBe(1);
|
|
expect(result.skills.length).toBe(0);
|
|
} finally {
|
|
rmSync(root, { recursive: true, force: true });
|
|
}
|
|
});
|
|
});
|