Table of Contents
flowchart LR
T["Use this template"] --> E["Edit SKILL.md"]
E --> P["Push to GitHub"]
P --> V{"CI validates"}
V -->|pass| I["npx skills add\nuser/skill"]
V -->|fail| E
- Click Use this template to create your repo
- Edit
SKILL.md— setname:to match your repo name, write yourdescription: - Push and test:
npx skills add YOUR_USERNAME/your-skill-name --all
Post-clone checklist
- Set
name:inSKILL.mdto your repo's kebab-case name - Write a real
description:in third person with trigger phrases - Replace this README's header, badge URLs, and description
- Update
LICENSEcopyright (or swap license entirely) - Delete directories you don't need (
scripts/,assets/,references/) - Write skill instructions in the
SKILL.mdbody - Test locally:
npx skills add ./ --list
| File | Purpose | Required |
|---|---|---|
SKILL.md |
Skill definition — frontmatter metadata + markdown body | yes |
references/ |
On-demand docs loaded only when agents follow a link | no |
scripts/ |
Executable code the skill invokes at runtime | no |
assets/ |
Static resources — templates, data files, images | no |
LICENSE |
License file (copied to consumers) | recommended |
.github/workflows/validate.yml |
CI pipeline — frontmatter validation + markdown lint | recommended |
_config/.markdownlint.yaml |
Lint config (excluded from installation via _ prefix) |
no |
Note
Only SKILL.md is required. Everything else is scaffolding you can keep or delete.
The CI pipeline catches common mistakes before they reach consumers.
npx skills add YOUR_USERNAME/my-skill-nameSee CLI documentation for all install methods.
Tip
README.md and _-prefixed paths are automatically excluded from installation.
See exclusion rules below.
my-skill-name/
│
│ # ── Installed by `npx skills add` ─────────────────
├── SKILL.md # Skill definition — frontmatter + body (required)
├── references/ # On-demand docs linked from SKILL.md body
│ └── REFERENCE.md # loaded only when the agent follows the link
├── scripts/ # Executable code the skill invokes
├── assets/ # Static resources (templates, data files, images)
├── LICENSE # License file
├── .gitignore
├── .github/
│ └── workflows/
│ └── validate.yml # CI: frontmatter + markdown lint
│
│ # ── Excluded from installation ────────────────────
├── README.md # Repo docs (hardcoded exclusion)
└── _config/ # Build/lint config (excluded via _ prefix)
└── .markdownlint.yaml
Important
The CLI copies everything except these four exclusions
(source):
README.md, metadata.json, _-prefixed paths, .git/
Everything else — including LICENSE, .gitignore, .github/, and all subdirectories — is copied into the consumer's skills directory.
Skills load in three stages1:
flowchart LR
A["**Discovery**\n`name` + `description`\n~100 tokens"]
B["**Activation**\nSKILL.md body\n< 5k tokens"]
C["**On-demand**\nreferences/ scripts/ assets/\nas needed"]
A --> B -.-> C
Keep SKILL.md lean. Move detailed docs to references/ and link from the body.
The skill definition uses YAML frontmatter plus a markdown body.
All 6 frontmatter fields
| Field | Required | Constraints | Spec |
|---|---|---|---|
name |
yes | 1–64 chars, lowercase alphanumeric + hyphens, no leading/trailing/consecutive -, must match parent dir, NFKC-normalized |
spec |
description |
yes | 1–1024 chars. Primary trigger — pack use cases, trigger phrases, and negative triggers here | spec |
license |
no | License name or path to bundled file (e.g. MIT, Proprietary. LICENSE.txt has complete terms) |
spec |
compatibility |
no | 1–500 chars. Runtime/environment requirements (e.g. Requires Node.js 18+) |
spec |
metadata |
no | key: value map (values coerced to strings). Common keys: author, version |
spec |
allowed-tools |
no | Space-delimited pre-approved tools. Experimental — support varies by agent | spec |
Only these six fields are accepted by the reference validator — unknown fields cause validation errors.
Writing an effective description
The description field is the single most important line in your skill. It controls whether agents discover and activate your skill2.
Before — vague, no triggers, second person:
description: A skill that helps with testing thingsAfter — specific, trigger-rich, third person:
description: >-
Generates unit tests for Python functions using pytest. Triggers on
"write tests for", "add test coverage", "generate pytest". Supports
fixtures, parametrize, and mock patterns. Do NOT activate for
JavaScript testing or manual test plans.Rules:
- Write in third person — "Generates..." not "I generate..."
- Front-load trigger phrases — what users say when they need this skill
- Include negative triggers — "Do NOT activate for X" prevents false matches
- Stay under 1024 characters — but use every character that adds signal
From the skill-creator meta-skill and top-performing skills on skills.sh:
Tip
The description field is the primary trigger for skill activation — the body
is only loaded after the description matches. Put all "when to use" information
in the description, not the body.
- Imperative voice — tell the agent what to do, not what the skill is
- Examples over explanations — concrete input/output pairs in code blocks
- One level deep — link to
references/andscripts/directly fromSKILL.md; no nested chains - Challenge every line — "the context window is a public good" (skill-creator)
Body patterns used by top skills
| Pattern | Example | When to use |
|---|---|---|
| Quick-reference table + sections | pdf, docx | Tool/library skills with multiple workflows |
| Rule catalog (index + rule files) | react-best-practices | Codified knowledge with many discrete rules |
| Phased workflow | mcp-builder | Multi-step processes |
| Decision tree | webapp-testing | Approach depends on context |
| Router to references | internal-comms | Variant-specific content in reference files |
| Pure guidelines | frontend-design | Creative/aesthetic skills |
# Quick check — name + description present
FM=$(sed -n '2,/^---$/p' SKILL.md | head -n -1)
echo "$FM" | grep -q '^name:' && echo "$FM" | grep -q '^description:' && \
echo "valid" || echo "invalid: missing name or description"Note
CI runs a fuller validation on every push and PR: kebab-case name format, placeholder detection, body line count, and broken reference links.
npx markdownlint-cli2 "**/*.md" --config _config/.markdownlint.yamlnpx skills add ./ --list- Push to GitHub
- Verify:
npx skills add YOUR_USERNAME/my-skill-name --all - Check the skills.sh leaderboard once installs accumulate
Warning
If name: in SKILL.md does not match your repository directory name, consumers
may get installation errors. The CI pipeline validates kebab-case format but cannot
check the directory match — verify this manually.
To ship multiple skills from one repo:
my-repo/
├── skills/
│ ├── skill-one/
│ │ └── SKILL.md
│ └── skill-two/
│ ├── SKILL.md
│ ├── scripts/
│ └── references/
└── .claude-plugin/
└── marketplace.json # optional — groups skills into named collections
The CLI searches these directories in priority order: repo root, skills/, skills/.curated/, skills/.experimental/, then agent-specific paths (.claude/skills/, .cursor/skills/, etc.).
marketplace.json schema
Optional plugin manifest for grouping skills (example):
{
"name": "my-skill-collection",
"metadata": { "version": "1.0.0" },
"plugins": [
{
"name": "core-skills",
"source": "./",
"skills": ["./skills/skill-one", "./skills/skill-two"]
}
]
}My skill isn't being discovered — what's wrong?
Check the description field in SKILL.md. Discovery depends entirely on the description matching user intent. Common fixes:
- Add concrete trigger phrases ("create a widget", "debug the frobnitz")
- Add negative triggers ("Do NOT activate for X")
- Write in third person ("Generates..." not "I generate...")
- Make sure
name:matches your directory name exactly
How do I test changes without publishing?
Install from your local directory:
npx skills add ./ --allThis copies your skill files into the local agent's skill directory, identical to a remote install. Use --list instead of --all to preview what would be copied.
What's the difference between description and the body?
description (frontmatter) |
Body (markdown) | |
|---|---|---|
| When loaded | Always — during discovery | Only after description matches |
| Purpose | Trigger activation | Guide execution |
| Token budget | ~100 tokens | < 5k tokens |
| Voice | Third person ("Generates...") | Imperative ("Generate...") |
Put all "when to use" information in the description. The body tells the agent how to execute.
How many agents support skills?
The skills specification is supported by Claude Code, Cursor, Windsurf, Cline, Amp, and many more. Any agent that reads SKILL.md frontmatter follows the same discovery and activation flow.
Can I use a different license?
Yes. Replace the LICENSE file and update license: in SKILL.md frontmatter. The license field accepts either a name (MIT, Apache-2.0) or a reference to a bundled file (Proprietary. LICENSE.txt has complete terms).
Specification · What Are Skills · CLI · Anthropic Skills · Vercel Skills · Leaderboard
Built with skill-starter
Footnotes
-
Progressive disclosure model — skills load incrementally to minimize context window usage. ↩
-
The description is injected into the agent's system prompt during discovery. Write it as if the agent will read it verbatim — because it will. ↩