import assert from "node:assert/strict" import { existsSync, readFileSync, readdirSync } from "node:fs" import path from "node:path" import { test } from "node:test" import { fileURLToPath } from "node:url" import { parseFrontmatter } from "../scripts/frontmatter.ts" import { loadClassification } from "../scripts/generate.ts" const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..") const entries = loadClassification(path.join(root, ".opencode/state/classification.json")) const vendored = entries.filter((e) => e.artifacts.includes("skill:verbatim")) const agentFiles = readdirSync(path.join(root, "agents")).filter((f) => f.endsWith(".md")) // V2-native frontmatter only (AGENTS.md convention); legacy v1 keys must never appear. const V2_KEYS = new Set([ "name", "description", "mode", "model", "color", "permissions", "hidden", "steps", "disabled", "request", "system", ]) test("agent frontmatter uses V2-native keys only", () => { for (const file of agentFiles) { const { frontmatter } = parseFrontmatter(readFileSync(path.join(root, "agents", file), "utf8")) for (const key of Object.keys(frontmatter)) { assert.ok(V2_KEYS.has(key), `${file}: non-V2 frontmatter key '${key}'`) } assert.ok(frontmatter.mode, `${file}: mode required`) assert.ok(frontmatter.description, `${file}: description required`) } }) test("every skill an agent names still exists in the upstream snapshot", () => { const allNames = new Set(entries.map((e) => e.name)) for (const file of agentFiles) { const body = readFileSync(path.join(root, "agents", file), "utf8") const tokens = new Set( [...body.matchAll(/`([a-z][a-z0-9-]*)`/g)].map((m) => m[1]).filter((t) => allNames.has(t)), ) for (const name of tokens) { const entry = entries.find((e) => e.name === name)! const dir = path.join(root, "upstream/skills", entry.bucket, name) assert.ok( existsSync(path.join(dir, "SKILL.md")), `${file} names skill '${name}' which no longer exists upstream (renamed or removed?)`, ) assert.ok( entry.artifacts.includes("skill:verbatim"), `${file} names skill '${name}' which is excluded from this package`, ) } } }) test("worker may only load model-invoked vendored skills", () => { const worker = readFileSync(path.join(root, "agents/pocock-worker.md"), "utf8") const allowed = [...worker.matchAll(/action:\s*skill,\s*resource:\s*"([^"]+)",\s*effect:\s*allow/g)].map( (m) => m[1], ) assert.ok(allowed.length >= 3, "worker skill allow-list parsed") const userInvoked = new Set(entries.filter((e) => e.user_invoked).map((e) => e.name)) const vendoredNames = new Set(vendored.map((e) => e.name)) for (const name of allowed) { assert.ok(!userInvoked.has(name), `worker allows user-invoked skill '${name}' (needs a person)`) assert.ok(vendoredNames.has(name), `worker allows '${name}' which is not a vendored skill`) } for (const core of ["tdd", "codebase-design", "diagnosing-bugs"]) { assert.ok(allowed.includes(core), `worker missing core skill '${core}'`) } }) test("reviewer is read-only: edit/subagent denied, only code-review allowed", () => { const reviewer = readFileSync(path.join(root, "agents/pocock-reviewer.md"), "utf8") assert.match(reviewer, /action:\s*edit,\s*resource:\s*"\*",\s*effect:\s*deny/) assert.match(reviewer, /action:\s*subagent,\s*resource:\s*"\*",\s*effect:\s*deny/) const allowedSkills = [...reviewer.matchAll(/action:\s*skill,\s*resource:\s*"([^"]+)",\s*effect:\s*allow/g)].map( (m) => m[1], ) assert.deepEqual(allowedSkills, ["code-review"]) }) test("worker and reviewer cannot spawn subagents (no circular delegation)", () => { for (const file of ["pocock-worker.md", "pocock-reviewer.md"]) { const body = readFileSync(path.join(root, "agents", file), "utf8") assert.match(body, /action:\s*subagent,\s*resource:\s*"\*",\s*effect:\s*deny/, `${file}: subagent deny`) } }) test("classification is fully reviewed and matches the generated tree", () => { const needsReview = entries.filter((e) => e.status === "needs-review") assert.deepEqual( needsReview.map((e) => e.name), [], "needs-review classifications block the build until reviewed", ) const manifest = JSON.parse(readFileSync(path.join(root, "manifest.json"), "utf8")) assert.deepEqual( manifest.map((m: { id: string }) => m.id).sort(), vendored.map((e) => e.name).sort(), ) for (const e of vendored) { assert.ok(existsSync(path.join(root, "skills", e.name, "SKILL.md")), `skills/${e.name} generated`) } for (const e of entries.filter((x) => x.artifacts.includes("excluded"))) { assert.ok(!existsSync(path.join(root, "skills", e.name)), `excluded skill ${e.name} must not ship`) } })