This repo publishes reusable agent skills — Claude Code, Codex, Cursor, OpenCode and friends. Skills live at skills/<category>/<skill>/ — categories are repo/, work/, docs/, meta/ — each self-contained: a SKILL.md (YAML frontmatter + body) plus optional assets. No runtime code ships except one file: scripts/resolve-config.sh, mirrored into every skill that reads the config. That is what test/ covers — the skill prose has no test/ suite, and none is wanted there. Skill quality is measured out of band instead, by the evals/ convention (skills/README.md): per-skill, with/without-baseline fixtures that are inert dev-only data a development tool runs, never test/, and stripped by packaging and skills:link.
Every file named below is the source of truth for what it configures. Read it rather than trusting a summary here — this file carries only what no file states outright.
CLAUDE.md and AGENTS.md are kept byte-identical. CLAUDE.md is what Claude Code reads; AGENTS.md is what vendor-neutral agent tools read — Codex, OpenCode, Cursor, Copilot, and whatever follows them. Two real files, deliberately not a symlink: not every tool resolves one.
After editing either file, copy it over the other — don't repeat the edit by hand:
cp CLAUDE.md AGENTS.md # or the reverse, whichever you just editedRetyping a change is exactly how the two drift; one reflowed line or reworded clause is enough. diff CLAUDE.md AGENTS.md must print nothing — pnpm test fails when it does. Fix it by letting one file win wholesale, never by merging them.
package.json has the full list. The ones with non-obvious behaviour:
| Command | Why it needs saying |
|---|---|
pnpm verify |
The repo's gate — check + skills:check + typecheck + test, in CI's order, and the root verify key. CI runs the same commands as one step each, so one run reports every failure; test/ci-gate.test.ts fails if the two lists drift. |
pnpm check |
Lint + format check only — a subset of the gate, not the gate. pnpm check:fix applies both fixers. |
pnpm skills:sync |
Regenerates eleven artifacts from the skill folders. Run after touching any skill. |
pnpm skills:check |
The CI guard for the above. Fails if any of them drifted. |
pnpm typecheck |
tsc --noEmit. erasableSyntaxOnly is on, so an enum fails here, not at runtime. |
pnpm test |
node --test over test/ — the resolver, the schema, skill self-containment, the CI-gate guard, the conformance-gate guard, the link destinations, and the CLAUDE.md/AGENTS.md mirror. |
pnpm skills:link |
Symlinks every skill into both ~/.claude/skills/ (Claude Code) and ~/.agents/skills/ (Codex, Cursor, OpenCode, Gemini CLI) for live local testing — whole-folder, except a skill carrying a dev-artifact dir (evals/) links entry by entry to leave the fixture out, per destination. pnpm skills:unlink clears both; the destination list lives once, in scripts/skills-lib.sh (ADR-0016). |
pnpm skills:conformance |
Validates every skill against the Agent Skills spec with skills-ref, pinned and run in Docker. Not in verify — the gate stays pnpm-only; this needs Docker. Its own workflow. |
Eleven artifacts are generated — never hand-edit them: the root README.md skills table, each skills/<category>/README.md, .claude-plugin/plugin.json, skills.sh.json's groupings, the <skills-config> block mirrored into each skill that hosts it — with templates/resolve-config.sh copied into each skill that names it, which is every host plus the two queue skills that delegate the prose and still run the script (source: scripts/config-block.md and scripts/resolve-config.sh), the <skills-authority> / <skills-authority-reduced> author-authority block mirrored into each skill that reads third-party text (source: scripts/authority-block.md), the <skills-verify> / <skills-verify-isolated> check-command block mirrored into each skill that runs the repo's gate (source: scripts/verify-block.md), the <skills-worklock> single-flight-lock spec mirrored into the two work-loop unit skills — the two *-queue skills name their worker's REFERENCE for it instead, which is what lets them skip both this block and the config one (source: scripts/worklock-block.md, roster in test/isolation.test.ts), the <skills-plan> plan-presentation rule mirrored into each skill that puts a plan in front of a human (source: scripts/plan-block.md, roster in test/isolation.test.ts), the <skills-tldr> report-lead rule mirrored into each skill whose run ends in a report — a strict subset of the plan roster, since a skill leads with a result only where it closes by reporting one (source: scripts/tldr-block.md, roster in test/isolation.test.ts), and the <skills-forge> forge/host resolution rule mirrored into each skill that drives a forge — the same two *-queue skills name their worker's REFERENCE for this one too (source: scripts/forge-block.md, roster in test/isolation.test.ts). A new category also needs an entry in CATEGORIES in scripts/gen-skills.ts, or the sync fails loudly.
- pnpm is mandatory —
packageManagerStrict: trueplus thepackageManagerpin. npm/yarn are rejected outright. pnpm-workspace.yamlholds every pnpm setting. pnpm 10+ reads only auth/registry from.npmrc; anything else placed there is silently ignored. There is no.npmrc.- A release-age gate is active (
minimumReleaseAgeinpnpm-workspace.yaml). Packages published inside that window will not install, andpnpm tazereports them as "up to date" rather than as withheld — so "up to date" never proves nothing newer exists. - oxfmt formats markdown, JSON and YAML too, not just JS — see
lint-staged.config.js. Markdown is the point here; JS is incidental. oxlintandoxfmtare pinned exactly (no^), which drops them out of taze's default scope entirely — absent from its table, not reported as current.-lbrings them back in and composes with the mode (taze minor -w -l). Don't reach forlatestto move a pin: it spans majors and will happily propose a 0.x major or a prerelease.- Husky runs lint-staged and commitlint on every commit. Don't
--no-verifyunless asked. - Conventional Commits are enforced. Scope by skill name when changing one skill:
feat(write-readme): …. - release-please cuts releases from
mainwithbump-minor-pre-major, so a pre-1.0 breaking change bumps the minor. Onlyfeat/fix/breaking reach the changelog — typing a user-visible change aschoreorrefactorsilently drops it from the release.
.github/workflows/ is authoritative. Four behaviours that mislead if unknown:
- CI runs
on: pull_requestonly, so a commit pushed straight todevis never checked by CI. The AI work loop is configuredbranch:dev— it commits to the shared branch with no PR — which makespnpm verify, run locally before the push, the only automated gate between such a change and the release branch. That is why the rootverifykey is the full gate and not just lint plus format: nothing downstream would catch a broken test, a type error, or a drifted generated artifact until the rollup PR. - Every PR workflow skips drafts, and that is now load-bearing rather than incidental — a draft's check list is not a pass.
work-implementopens its PRs as drafts on purpose andwork-reviewmarks one ready only once it would accept, so CI is spent once per finished instead of once per push (The draft gate inwork-implement's REFERENCE). Each of the three therefore needs both halves: the draft gate andready_for_reviewin its triggertypes, or the un-draft fires nothing. All three gate on the job, so a draft reports oneskippingrow per gated job rather than an empty list — which is why anything reading those checks must discardskippingoutright instead of treating a non-failing list as green. - CodeQL runs on every PR to
main/dev, markdown-only ones included —codeql.ymlcarries nopaths:filter, so both matrix legs (actions,javascript-typescript) analyse every pull request; only the draft gate above holds it back. It is not the cheap no-op a path-scoped workflow would be, and the work loop's CI read depends on knowing which workflows a head actually triggers, so this is worth having right. - The skill conformance check is a separate workflow, scoped to
skills/**and its own script — so it does not run on a PR that touches neither, and, like everything else here, never on a push straight todev. It re-tiers the deliberatedisallowed-tools(ADR-0007) instead of failing on it;test/conformance-gate.test.tspins that list tovalidate-skills' prose. Running it locally needs Docker, which is whypnpm verifydoes not.
Create skills/<category>/<name>/SKILL.md, run pnpm skills:sync, commit as feat(<name>): add skill. The frontmatter contract and layout live in skills/README.md; the full workflow in CONTRIBUTING.md.