agentleFS
podopodo/design-agent-skills/CLAUDE.md

design-agent-skills

CLAUDE.md32 starsChanged 6 months agoMITView on GitHub

This file is the authoritative reference for Claude Code when working on this repository. Follow it exactly — the test suite enforces most of these rules automatically. A two-tier catalogue of design skills for Claude Code, Cursor, Codex, OpenCode, and 30+ other AI agents. Skills install via: npx skills add podo/design-agent-skills [-g] This fetches directly from GitHub — npm publish is only needed for changes to bin/cli.mjs. Do not close a bug fix without a test. The test is proof…

  • Pipes a download into a shell
  • Reads credentials
  • Commits and pushes

The file

Show its rules and text

We split this CLAUDE.md into the rules it gives an agent

26 rules. About 0 are common, 8 are distinctive, and 18 are very specific to this project.

  • ≈0

    Common · estimate

    Many files like this have a rule of this kind.

  • ≈8

    Distinctive · estimate

    This author's own choices. Worth a look.

  • 18

    Very specific

    Tied to their project's names, files or tools. Probably skip.

  • Common
  • Distinctive
  • Very specific
  1. Distinctive:

    Follow it exactly — the test suite enforces most of these rules automatically.

    line 4
  2. Distinctive:

    Write "What it does" / "When NOT to use" by hand (no hardcoded counts)

    line 53
  3. Very specific:

    Update skills/design-catalogue/SKILL.md — add to the right section + routing guide (npm test fails on a stub missing from it)

    line 54
  4. Very specific:

    Update the relevant domain catalogue SKILL.md

    line 55
  5. Very specific:

    Run npm test — must pass 100%

    line 57
  6. Distinctive:

    Commit the fix and the test together

    line 72
  7. Distinctive:

    Do not close a bug fix without a test.

    line 74
  8. Very specific:

    Not a SKILL.md — MCPs, web tools, VS Code extensions, pip packages go elsewhere

    line 102
  9. Very specific:

    Every other stub must have a rank field (test enforces this).

    line 126
  10. Very specific:

    When in doubt: new skills default to rank: 3.

    line 127
  11. Very specific:

    Every skill directory must have a stub.yaml.

    line 133
  12. Very specific:

    Every skill directory must have a SKILL.md.

    line 253
  13. Distinctive:

    No two skills in the catalogue can share the same trigger (test enforces this)

    line 277
  14. Very specific:

    Use longer, specific phrases for impl skills ("UX writing skill", "microcopy pattern")

    line 278
  15. Very specific:

    Use shorter, generic phrases for routers ("UX writing skills", "microcopy")

    line 279
  16. Very specific:

    Check for conflicts before adding: grep -r "your trigger" skills/*/SKILL.md

    line 280
  17. Very specific:

    Do NOT use curl/cp into the pointer's own directory.

    line 328
  18. Distinctive:

    When adding a new skill, update both:

    line 334
  19. Very specific:

    Add a row to the right section in the ## Catalogue index

    line 338
  20. Very specific:

    Add 1–3 rows to ## Routing guide at the bottom

    line 339
  21. Distinctive:

    If it's a content/copy skill, also update the domain fast-path table description

    line 340
  22. Very specific:

    Add a row to the right section in ## Skills

    line 354
  23. Very specific:

    Update supply chain community/official/experimental counts if tier changes

    line 356
  24. Very specific:

    Use these exact strings in stub.yaml and das: blocks:

    line 362
  25. Distinctive:

    Update these three files atomically:

    line 401
  26. Very specific:

    Do not run gh release create manually — it creates a release before the zip is ready, and GitHub marks that release immutable (no further …

    line 438

≈ “Common” is an estimate: at least 1 in 5 of the 26,980 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

design-agent-skills — Contributor Guide

This file is the authoritative reference for Claude Code when working on this repository. Follow it exactly — the test suite enforces most of these rules automatically.


What this repo is

A two-tier catalogue of design skills for Claude Code, Cursor, Codex, OpenCode, and 30+ other AI agents.

Tier 1 — Routing layer (6 routers, permanent)
  design-catalogue → domain catalogues → implementation skills

Tier 2 — Implementation pointers (160 stubs)
  Lightweight stubs that tell an agent what a skill does and how to fetch the real one.
  On first use the agent upgrades the pointer to the full skill in-place.

Skills install via: npx skills add podo/design-agent-skills [-g] This fetches directly from GitHub — npm publish is only needed for changes to bin/cli.mjs.


Repository layout

skills/<name>/
  stub.yaml     — machine-readable metadata (type, tier, upstream)
  SKILL.md      — human/agent-readable pointer with frontmatter + install instructions

test/
  stubs.test.js — run with: npm test
  cli.test.js   — bin entry tests

bin/cli.mjs     — thin wrapper around `npx skills add`
VERSION         — semver, single line
package.json    — version field must match VERSION
CHANGELOG.md    — keep-a-changelog format
README.md       — skills table, router table, supply chain section

Adding a skill — checklist

  1. Evaluate the skill (see criteria below)
  2. Find the real install: npx skills add owner/repo --list shows the upstream skill names (what --skill and installed_as use)
  3. node scripts/add-skill.mjs — writes stub.yaml (rank 3 by default) and a SKILL.md with a generated install section; or copy a migrated stub
  4. Fill installed_as, install_default (and requires/license/size_mb when relevant) in stub.yaml, then npm run generate
  5. Prove it installs: node scripts/check-installs.mjs --real --only <name> (the landed directories must equal installed_as)
  6. Write "What it does" / "When NOT to use" by hand (no hardcoded counts)
  7. Update skills/design-catalogue/SKILL.md — add to the right section + routing guide (npm test fails on a stub missing from it)
  8. Update the relevant domain catalogue SKILL.md
  9. Regenerate the README table: node scripts/generate-readme-table.mjs --write, then update the counts (lines 4, the profile table and the tier line; npm test checks them)
  10. Run npm test — must pass 100%
  11. Bump version (see versioning section)
  12. Open a PR (main is protected); push the version tag after it merges — CI creates the release with the skills zip and publishes to npm automatically

Fixing bugs — checklist

  1. Reproduce the bug and identify the root cause
  2. Fix it
  3. Add a regression test that would have caught the bug before the fix:
    • Data bugs (missing fields, wrong values in stub.yaml / SKILL.md) → test/stubs.test.js
    • CLI behaviour bugs (wrong counts, wrong filtering, wrong commands) → test/cli.test.js
  4. Run npm test — must pass 100%
  5. Bump the patch version (see versioning section)
  6. Commit the fix and the test together

Do not close a bug fix without a test. The test is proof the bug is fixed and stays fixed.


Evaluation criteria — what makes a good skill

Accept if all of these are true

SignalWhy it matters
Has a SKILL.md at the upstream repo root (or known path)The pointer's upgrade target must exist
Installable via npx skills add owner/repo OR has a clear alternative installAgents must be able to fetch the real skill
Covers a gap not already in the catalogueNo point in two near-identical skills
Addresses a real design/engineering use caseScope must fit: UI, motion, a11y, Figma, data viz, content, etc.

Strong positive signals

  • 50+ GitHub stars (traction, maintenance signal)
  • Official org account (Anthropic, Vercel, Google, Expo, GSAP, LottieFiles…) → tier: official
  • Active commits in the last 6 months
  • Multiple sub-skills or slash commands (high density of capability)
  • Unique angle not covered elsewhere in the catalogue

Skip if any of these are true

  • Archived repository
  • curl | bash only install, no npx skills add pathway, and no reasonable type: package alternative
  • Heavy overlap with an existing catalogue entry and no meaningful differentiation
  • Not a SKILL.md — MCPs, web tools, VS Code extensions, pip packages go elsewhere
  • Lives in a completely different ecosystem (pip/skilz, VS Code marketplace, etc.)
  • 0 stars and fewer than 5 commits (too immature / likely abandoned)

Tier assignment

TierWhen to use
officialPublished by the company/project that owns the upstream (Anthropic, Figma, Vercel, Expo, GSAP, LottieFiles, shadcn, remotion…)
communityPublished by an individual or third party; 1+ stars; actively maintained
experimentalVery new, 0 stars, or unverified upstream — excluded from default installs

Rank assignment

rank controls which install profile includes this skill (npx design-agent-skills --picks / --essentials / --all).

RankProfileCriteria
1PicksBest-in-class for its category — one winner per domain. High stars, official when available, no redundancy.
2EssentialsAdds real coverage without duplicating rank-1. Covers sub-niches, secondary tools, solid community picks.
3ExtendedNiche, experimental, heavy overlap with rank-1/2, platform-specific, or low star count.

Rules:

  • Routers (type: router) are exempt from rank — always installed.
  • Every other stub must have a rank field (test enforces this).
  • When in doubt: new skills default to rank: 3. Promote to rank: 2 once quality is confirmed; rank: 1 only when it's the clear winner for its category.

stub.yaml — format and rules

Every skill directory must have a stub.yaml. The test suite enforces all rules below.

Fields

type: skill           # REQUIRED — one of: router | skill | package | platform
tier: community       # REQUIRED — one of: official | community | experimental
rank: 2               # REQUIRED (non-routers) — one of: 1 | 2 | 3 (see Rank assignment below)
category: tui-terminal                    # REQUIRED — must match a VALID_CATEGORIES entry
upstream: https://github.com/owner/repo   # REQUIRED for type:skill; forbidden for type:router
upstream_path: SKILL.md                   # optional — path within repo to the SKILL.md
version: latest       # always "latest"

# ── install contract (REQUIRED on every non-router stub; see "Install fields" below) ──
installed_as:                             # the upstream skill NAMES this stub installs (what --skill takes)
  - skill-a
  - skill-b
install_default: npx skills add owner/repo --skill skill-a skill-b   # the primary command (no -g, no -y)

# ── optional ──
install_extra:                            # more commands that complete the install (same rules as install_default)
  - npx skills add owner/repo/path/to/skill
install_claude:                           # Claude Code plugin route, the full two steps
  - /plugin marketplace add owner/repo
  - /plugin install name@marketplace
install_mode: npx                         # npx (default) | manual | builtin — see below
requires:                                 # shown to users: account, API key, MCP server, runtime, companion skill
  - FIGMA_API_KEY and the Figma MCP server
license: MIT                              # SPDX id (from `gh api repos/o/r --jq .license.spdx_id`) or a short note
size_mb: 150                              # repo size in MB; >= 100 adds a clone-timeout warning
conflicts_with:                           # stubs that write the same directory, incl. this stub's own pointer directory (must be mutual)
  - other-stub
note: "..."                               # maintainer annotation; never shown to agents

Install fields

stub.yaml is the single source of truth for how a pointer installs. The install section of each SKILL.md (between <!-- das:install:start --> and <!-- das:install:end -->) is generated from it: edit stub.yaml, run npm run generate, and never hand-edit inside the markers. The hand-written parts of a pointer are the frontmatter, the title line, ## Invoke after install, ## What it does and ## When NOT to use.

FieldRule
installed_asExact upstream skill names (frontmatter name, which is what --skill matches). The CLI installs each into .agents/skills/<sanitised name> (lowercase, runs of characters outside [a-z0-9._] become -; stitch::x → stitch-x). This list must equal what the install really lands — npm run verify:installs checks it exactly.
install_defaultnpx skills add <owner/repo or owner/repo/subpath or raw SKILL.md URL> [--skill a b c]. Must target the stub's own upstream repo. No -g, -y, --yes or --all (the pointer adds -g -y itself). Prefer a targeted --skill a b c over a bare repo install so installed_as is exact.
install_mode: manualFor skills the CLI cannot install (no/unparseable frontmatter, shared tooling like gstack, a repo too big to clone). Needs manual_reason, install_steps (shell lines) and optionally install_check: a file or directory whose existence means "installed" (~/…, absolute, or relative to the scope root such as .agents/skills/x/SKILL.md, which the pointer probes under both ~ and the project; the pointer's own SKILL.md never counts). Steps carry no -g, -y or --all, and an npx skills add step must be the last command of its step: the pointer renders Global (-g -y) and Project (-y) variants itself. npm run verify:installs replays only the safe steps (git clone, git -C … sparse-checkout, npx skills add <clone path>, never with -g) in a scratch dir, requires the landed directories to equal installed_as and a relative install_check to exist, and never runs ./setup, cp, curl or pip.
install_mode: builtinThe skill is a feature of an app, nothing to install (e.g. Nimbalyst). Needs manual_reason.
install_claudeThe complete plugin route: a /plugin marketplace add … is only valid together with a /plugin install name@marketplace. Must start with /plugin or claude plugin.
conflicts_withTwo different upstreams that write the same directory overwrite each other silently. That includes a stub's own pointer directory (skills/<name>): if another stub installs a skill with that name, installing it replaces your pointer. Declare it on both stubs; the pointer then warns. The same upstream listed twice is fine.

Banned in any install field or pointer (lint-enforced): claude skills add and claude plugin add (neither is a Claude Code command), --skill <placeholder>, the legacy ~/.design-agent-skills path, and hardcoded skill/agent/command counts in the description or prose, such as "31 skills" or "71 design systems" (they go stale; see design-with-claude).

Quote YAML values that contain : or # (lint-enforced). The repo reads stub.yaml with small line readers (scripts/lib/stub.mjs, install.sh), but a real YAML parser reads - Network access: x as a mapping and drops everything after #. Write - "Network access: x". Nothing may follow a closing quote. A value that is wrapped in quotes loses only that wrapping pair, so install_default: npx skills add o/r --skill "Two Words" keeps its inner quotes.

Offline checks for one stub, without running the whole suite:

node scripts/lint-stubs.mjs --only my-stub            # install schema, banned commands, pointer in sync
node scripts/generate-pointers.mjs --only my-stub     # rewrite the generated install section
SKILLS_BIN=/path/to/skills node scripts/check-installs.mjs --real --only my-stub   # REAL install, exact match

Type selection

TypeUse whenUpgrade path
routerDomain navigator (never add new routers — exactly 6 exist)Never — always from this repo
skillOne upstream skill installable via npx skills add owner/repo --skill <upstream-name>install_default
packageSeveral skills from one upstream (--skill a b c), or a skill with a custom installinstall_default, or install_mode: manual
platformA platform/app the skills CLI cannot installinstall_mode: manual / builtin

Rule: type: router must NOT have an upstream field. type: skill MUST have one.

Examples

# type: skill — one upstream skill whose name differs from our directory (ux-writing-skill -> ux-writing)
type: skill
tier: community
rank: 1
category: content-design
upstream: https://github.com/content-designer/ux-writing-skill
upstream_path: SKILL.md
version: latest
install_default: npx skills add content-designer/ux-writing-skill --skill ux-writing
installed_as: ux-writing
# type: package — several skills from one upstream, with heads-up facts
type: package
tier: official
rank: 1
category: official-suites
upstream: https://github.com/anthropics/skills
version: latest
install_default: npx skills add anthropics/skills --skill frontend-design pptx
installed_as:
  - frontend-design
  - pptx
license: pptx is proprietary; the others are Apache-2.0 or per-skill
# type: router — no upstream, permanent
type: router
tier: official
note: "Meta-navigator for the design-agent-skills catalogue"

SKILL.md — format and rules

Every skill directory must have a SKILL.md. The test suite validates the frontmatter.

Frontmatter rules

---
name: <skill-name>         # REQUIRED — must exactly match the directory name
description: |             # REQUIRED — multiline OK; used by agents for discovery
  One or two sentences.
  What it does, not marketing copy.
triggers:                  # REQUIRED — at least one; no duplicates across the whole catalogue
  - "phrase that activates this skill"
  - "another phrase"
das:                       # optional block — can hold type/category for tooling
  type: skill
  category: content-design
  upstream: "https://github.com/owner/repo"
  version: latest
---

Trigger rules

  • Triggers are matched exactly against user messages — keep them natural phrases
  • No two skills in the catalogue can share the same trigger (test enforces this)
  • Use longer, specific phrases for impl skills ("UX writing skill", "microcopy pattern")
  • Use shorter, generic phrases for routers ("UX writing skills", "microcopy")
  • Check for conflicts before adding: grep -r "your trigger" skills/*/SKILL.md

Body sections (type:skill and type:package)

# <skill-name>

> Catalogue stub — full skill: [owner/repo](https://github.com/owner/repo)

<!-- das:install:start … -->     ← GENERATED from stub.yaml (decision tree, install commands, heads-up)
<!-- das:install:end -->

## Invoke after install          ← hand-written; every `Skill name:` must be one of installed_as

...trigger phrases or slash commands...

## What it does

...capability summary (no hardcoded counts; they go stale)...

## When NOT to use

...redirect to alternatives...

Install route rules

--skill matches the upstream SKILL.md frontmatter name, not our directory name. When the two differ (directory guizang-ppt, upstream skill guizang-ppt-skill) the full skill installs next to the pointer instead of replacing it; the generated "already installed?" check handles both cases by testing that the installed SKILL.md exists and no longer carries the pointer's das: marker.

Always verify before committing — a wrong name fails silently for users:

npx skills add owner/repo --list                 # the names listed are what --skill accepts
SKILLS_BIN=… node scripts/check-installs.mjs --real --only my-stub    # real install, exact match with installed_as
  • Nested or hidden skills (under a plugin path, or the repo has a root SKILL.md): install by subpath npx skills add owner/repo/path/to/skill, or use the CLI's --full-depth.
  • Huge repos (the CLI gives up cloning after 300 s; raise it with SKILLS_CLONE_TIMEOUT_MS): prefer a raw single-file URL (npx skills add https://raw.githubusercontent.com/o/r/main/skills/x/SKILL.md) or a manual sparse clone (git clone --depth 1 --filter=blob:none --sparse … + git -C … sparse-checkout set …).
  • Skills that call shared tooling elsewhere in their repo (gstack) must be installed as a whole: install_mode: manual with the upstream's real steps.
  • Upstream SKILL.md with no or unparseable YAML frontmatter is invisible to the CLI: document the upstream's manual install (install_mode: manual).
  • Do NOT use curl/cp into the pointer's own directory; it nests the skill inside the pointer.

Updating routers

When adding a new skill, update both:

1. skills/design-catalogue/SKILL.md (top-level router)

  • Add a row to the right section in the ## Catalogue index
  • Add 1–3 rows to ## Routing guide at the bottom
  • If it's a content/copy skill, also update the domain fast-path table description

2. Domain catalogue (pick one)

DomainFileAdd when
Motion, 3D, shaders, animationskills/motion-catalogue/SKILL.mdGSAP, Three.js, Lottie, shader, generative art, video
Figma, design-to-code, tokensskills/figma-catalogue/SKILL.mdFigma, Stitch, design tokens, design-to-code
Accessibility, WCAG, performanceskills/accessibility-catalogue/SKILL.mda11y audits, WCAG, Core Web Vitals, React quality
UI craft, visual, brand, mobileskills/design-engineering-catalogue/SKILL.mdTaste, color, typography, iOS/Android, brand, components
Slides, diagrams, data viz, PM, copyskills/content-catalogue/SKILL.mdPresentations, wireframes, charts, PM, UX writing, copywriting

3. README.md

  • Add a row to the right section in ## Skills
  • Bump skill counts in lines 4, 20, and the tier table (line ~70)
  • Update supply chain community/official/experimental counts if tier changes

Category values

Use these exact strings in stub.yaml and das: blocks:

CategoryUsed for
design-systemsUI taste, brand, design engineering, aesthetic anchors
creative-3dMotion, animation, 3D, shaders, generative art, video
interaction-polishUX polish, user journeys, interaction patterns, audits
visual-componentsColor, typography, components, mobile platform design
accessibility-qualitya11y, WCAG, performance, quality audits
design-reviewDesign review, critique, creative direction, UX strategy
figma-codeFigma, design-to-code, tokens, design system tooling
official-suitesOfficial org skill packages (Anthropic, Vercel, Expo…)
diagramsWireframes, excalidraw, flowcharts, architecture diagrams
data-visualizationCharts, D3, data analysis, BI
presentationsSlides, PowerPoint, Reveal.js, Slidev, MARP
product-pmPM frameworks, PRD, discovery, product strategy
content-designUX writing, copywriting, microcopy, AI text quality
email-designHTML email, MJML, email templates
tui-terminalTerminal UI, TUI frameworks (Textual, Ratatui, Bubble Tea, Ink)
metaCatalogue routers and meta-skills
motion-animationMotion design, animation principles, timing, easing
design-engineeringDesign-engineering crossover: bridging design and code
design-researchUser research, usability testing, discovery methods

Versioning

When to bump

ChangeVersion bump
New skills addedMinor (1.6.0 → 1.7.0)
Router/catalogue content updates onlyPatch (1.6.0 → 1.6.1)
Breaking changes to CLI or install formatMajor
README/CLAUDE.md onlyNo bump required

How to bump

Update these three files atomically:

# 1. VERSION file
echo "1.7.0" > VERSION

# 2. package.json
npm version 1.7.0 --no-git-tag-version

# 3. CHANGELOG.md — add entry at the top (after the header)

Creating a GitHub release

main is a protected branch — git push origin main is rejected by the branch protection hook. The version bump goes through a PR like any other change, and only then does the tag get pushed.

# 1. Bump VERSION + package.json + CHANGELOG on a branch
git checkout -b release-<version>
git commit -am "chore: release <version>"
git push origin release-<version>

# 2. PR it, wait for `validate`, merge
gh pr create --base main --title "chore: release <version>"
gh pr merge <n> --squash --delete-branch

# 3. Tag main — this is what triggers the release + npm publish
git checkout main && git pull --ff-only
git tag v<version>
git push origin v<version>

CI does the rest — it runs tests, builds the skills-only zip, creates the GitHub release with the zip attached, and publishes to npm.

Do not run gh release create manually — it creates a release before the zip is ready, and GitHub marks that release immutable (no further asset uploads allowed).

Rules:

  • Tag must match the VERSION file exactly (e.g. v1.7.0)
  • Push the tag after the version bump commit is merged to main
  • The CHANGELOG entry for the tagged version is extracted automatically for the release body
  • Auto-merge is disabled on this repo, so gh pr merge --auto fails; and branches must be up to date before merging, so two PRs touching the same file serialize (the second needs gh pr update-branch and a fresh CI run)

What GitHub Actions does automatically on tag push

The .github/workflows/publish.yml workflow fires on every git push origin v* and:

  1. Runs npm test — must pass before anything publishes
  2. Builds a skills-only zip (design-agent-skills-vX.Y.Z.zip) — contains only skills/, no source code
  3. Creates the GitHub release with the zip attached and notes from CHANGELOG.md
  4. Publishes to npm via Trusted Publishing (OIDC — no token secret required)

You do not need to run npm publish or gh release create manually. Pushing the tag is the only trigger needed.

Never npm publish by hand — it will conflict with the workflow if the version is already in flight, and it bypasses the test gate.

CHANGELOG format

## [1.7.0] — YYYY-MM-DD

### Added

- **`skill-name`** — One-line description. vX.Y.Z (`owner/repo`)

### Changed

- **`router-name` router** — What changed in the router.

Catalogue grows from N → M skills.

Running tests

npm run test:fast   # stubs + pointers + check-installs + cli — hermetic, a few seconds. Use while iterating.
npm test            # the above plus install.sh / add-skill shell integration tests
npm run lint:stubs  # the install lint on its own, readable output (add `-- --only name`)

test:fast covers stubs.test.js, pointers.test.js, check-installs.test.js and cli.test.js: thousands of assertions, no network. It enforces every rule in this file, so it is the gate that matters when adding or editing a skill. It is also what preversion runs. Anything that needs a real install (npm run verify:installs) is network-dependent and runs in CI instead.

The full npm test additionally drives install.sh end to end. Those tests fork one subprocess per skill in several loops (doctor alone forks ~130 awks), so they take tens of seconds on macOS and are the slow half of the suite. All must pass before committing.

What the tests check

SuiteChecks
file presenceEvery skill dir has both stub.yaml and SKILL.md
stub.yaml validitytype is one of router/skill/package/platform; tier is one of official/community/experimental; routers have no upstream; skills have upstream
SKILL.md frontmatterHas name, description, triggers (≥1); name matches directory name
type:skill upgrade commandsContains npx skills add; contains --skill <name>; no legacy ~/.design-agent-skills/ paths; no curl
catalogue-level invariantsExactly 6 routers; no duplicate trigger phrases across all skills
pointers.test.js (install schema)Every stub has installed_as and a valid install route; install commands target the stub's own upstream, with no -g/-y/--all/placeholders; every --skill name is declared; the generated install section matches stub.yaml; "Skill name" lines are installed skills; no banned commands or hardcoded counts; stub.yaml values a real YAML parser would read differently are quoted; manual steps carry no -g/-y; no two different upstreams write the same directory, including another stub's own pointer directory (unless both declare conflicts_with); every stub is in the top-level router
check-installs.test.jsThe real-install checker's pass/fail logic, offline, against a fake skills CLI and a local fixture repository: exact landed directories, manual routes (-g never reaches the CLI, install_check, unreadable clone URLs), clone timeout only for heavy stubs
install.test.jsinstall.sh commands against the real catalogue, status against a fixture catalogue (installed_as, install_check as file, directory or scope-relative path, dangling symlinks), and that its stub.yaml reader returns what the JS reader returns for every scalar key of every stub
add-skill.test.jsThe wizard's output is lint-clean; it refuses --type router, an existing name and a path-like name
README counts (stubs.test.js)The --all, --essentials, --picks and tier counts in README.md match the stubs

Diagnosing failures

# Check for duplicate triggers before adding
grep -h "  - " skills/*/SKILL.md | sort | uniq -d

# Verify a new skill's stub
cat skills/<name>/stub.yaml

# Verify SKILL.md frontmatter
head -20 skills/<name>/SKILL.md

Upstream freshness check

scripts/check-upstreams.mjs verifies that every non-router stub still points at a live, non-stale upstream repo:

  • Deleted / blocked upstream (HTTP 404 / 451) → hard failure. The pointer's upgrade target is gone; fix the URL or remove the skill.
  • Archived upstream → stale warning (fails only with --strict).
  • Missing upstream_path file → warning (only checked with --paths).

Where it runs:

TriggerScopeBlocking
Pull request (Test workflow)only skills changed in the PR (--only)yes — a PR can't add a dead pointer
Weekly cron + manual (Upstream URL check)whole catalogue (--paths)yes — alerts on rot anywhere

Set GITHUB_TOKEN/GH_TOKEN when running locally so the GitHub API allows 5000 req/hr instead of 60:

GH_TOKEN=$(gh auth token) node scripts/check-upstreams.mjs            # whole catalogue
GH_TOKEN=$(gh auth token) node scripts/check-upstreams.mjs --only my-skill,other-skill

PR CI is intentionally scoped to changed skills so a PR is gated only on what it touches — pre-existing rot elsewhere is caught by the weekly full-catalogue run, not by every unrelated PR.

Install command check

A live upstream does not prove the documented install works. scripts/check-installs.mjs uses the real skills CLI. It needs network and npx, so it is not part of npm test:

node scripts/check-installs.mjs                       # --list mode: every target resolves and lists its --skill names
node scripts/check-installs.mjs --real                # real install of every stub into a scratch dir
node scripts/check-installs.mjs --real --only a,b     # just these stubs
SKILLS_BIN=/path/to/skills node scripts/check-installs.mjs --real   # use a local CLI instead of npx

--real is the one that matters: for each stub it runs install_default (+ install_extra) with -y in an empty directory and requires the directories that land in .agents/skills/ to exactly equal installed_as (sanitised), each with a SKILL.md carrying name and description. Manual stubs have only their clone, sparse-checkout and npx skills add <clone path> steps replayed; ./setup, cp, curl and pip are never run, and -g is dropped so the developer's real skills directory is never touched. After the replay the landed directories must equal installed_as and a project-relative install_check must exist. Clone failures under load are retried; stubs with size_mb >= 100 run one at a time and are the only ones given a longer clone timeout (SKILLS_CLONE_TIMEOUT_MS=900000). Every other stub runs with the CLI's own 300 s default, so a repo that is too big to install for users but is not marked size_mb >= 100 fails here instead of passing on a generous limit. One stub that cannot be replayed does not abort the run; it is reported.

It runs on PRs (changed stubs only) and weekly (every stub). When it fails, fix installed_as / install_default in stub.yaml and run npm run generate.

Common mistakes

MistakeTest that catches itFix
name: in SKILL.md frontmatter doesn't match directory nameSKILL.md frontmatter → name matches directoryMake them identical
type:skill SKILL.md uses curl instead of npx skills addtype:skill upgrade commandsReplace with npx skills add owner/repo --skill <name>
type:skill pointer does not install the --skill names of stub.yamltype:skill upgrade commands → the pointer installs exactly the --skill namesEdit stub.yaml, then npm run generate (never hand-edit the install block)
Trigger phrase already used by another skillno duplicate trigger phrasesPick a more specific phrase; check with grep first
New router added (8th router)has exactly 6 router skillsRouters are permanent and fixed — never add a new router
type:router has upstream fieldrouter type has no upstreamRemove the upstream field
type:skill missing upstream in stub.yamlskill type has upstreamAdd upstream: https://github.com/owner/repo
Non-router stub missing rank fieldnon-router has rankAdd rank: 1, 2, or 3 to stub.yaml
Forgot to update both routersManual reviewAlways update design-catalogue + domain catalogue
Forgot to update README skill countManual reviewUpdate count on lines 4, 20, and tier table
--skill <directory-name> when the upstream skill is named differentlyinstall command validity → installs upstream skill (table of known names), pointers.test.jsAdd installed_as: <upstream name>, use it in --skill; confirm with npx skills add owner/repo --list
Per-skill shorthand like npx skills add owner/<skill> or owner/skill-namepointers.test.js → install fields are valid (target must be the stub's own upstream repo)Use npx skills add <upstream owner/repo> --skill <name>
- Some text: more text or a trailing # comment in stub.yamlpointers.test.js → values a real YAML parser would read differentlyWrap the value in double quotes
-g / -y in a manual install_steps linepointers.test.js → manual install_stepsRemove it; the pointer adds -g -y / -y to the npx skills add step
A skill installed by another stub is named like this stub's directorypointers.test.js → has no install-directory collisionsDeclare conflicts_with on both stubs
Legacy ~/.design-agent-skills/skills/... detection pathinstall command validity → no legacy pathUse ~/.agents/skills/<name> (global) or .agents/skills/<name> (project)

What NOT to add

  • MCP servers — different protocol, not SKILL.md skills
  • VS Code extensions — different ecosystem
  • pip/skilz ecosystem — different install toolchain
  • Archived repos — even if the skill was good
  • Skills that require account signup to install (platform type is OK but flag it)
  • A 7th router — the 6 routers are a hardcoded catalogue invariant

Worked example — adding a new skill

Say you want to add aperepel/textual-tui-skill, whose upstream skill is named textual-tui:

# 1. Find the real upstream skill name: it is what --skill and installed_as use, not our directory name
npx skills add aperepel/textual-tui-skill --list

# 2. Branch (main is protected), then write the stub and pointer
git checkout -b add-textual-tui-skill
node scripts/add-skill.mjs --name textual-tui-skill --type skill \
  --upstream https://github.com/aperepel/textual-tui-skill --upstream-path skill/SKILL.md \
  --category tui-terminal --tier community --rank 3 --installed-as textual-tui \
  --description "Deep-dive Textual (Python) TUI skill" --trigger "Textual TUI" --yes

That writes skills/textual-tui-skill/stub.yaml:

type: skill
tier: community
rank: 3
category: tui-terminal
upstream: https://github.com/aperepel/textual-tui-skill
upstream_path: skill/SKILL.md
version: latest
install_default: npx skills add aperepel/textual-tui-skill --skill textual-tui
installed_as: textual-tui

and a SKILL.md whose install section is generated from it. Then:

# 3. Add requires / license / size_mb when they apply, hand-write "What it does" and "When NOT to use"
#    (no hardcoded counts), then regenerate the install section
npm run generate

# 4. Prove it installs: the directories that land must equal installed_as
node scripts/check-installs.mjs --real --only textual-tui-skill

# 5. Routers and README: add the skill to skills/design-catalogue/SKILL.md (index row + routing guide)
#    and skills/content-catalogue/SKILL.md, then regenerate the table and update the counts
node scripts/generate-readme-table.mjs --write
npm test

# 6. CHANGELOG entry + version bump (a new skill is a minor bump), commit, and open a PR
git add skills/textual-tui-skill skills/design-catalogue skills/content-catalogue README.md CHANGELOG.md VERSION package.json
git commit -m "feat: add textual-tui-skill (Textual Python TUI, deep-dive)"
git push -u origin add-textual-tui-skill && gh pr create --base main

Download now (ZIP)

Does it work?

Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.

No reviews yet

People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.

  • It worked
  • Partly
  • It didn't
What did you use it for? e.g. Reviewing pull requests in a Rails app

Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.

Your trail

Files you open will collect here, so you can find them again.

Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.

Now do this for your own.

Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.