How to Write SKILL.md for Claude Code, Codex, and 19+ AI Tools
What is SKILL.md?
SKILL.md is a cross-platform instruction file for AI coding agents. You write one file, and it works across Claude Code, Codex CLI, Gemini CLI, Kiro CLI, Cursor, and 19+ other tools.
Iâve written 30+ skills over the past few months for deployment, testing, code review, and content workflows. The pattern is simple once you internalize it, but the first few attempts usually fail for the same reasons. This guide covers what actually works.
How does it work?
The loading mechanism has two phases:
- Index phase â On startup, the tool reads only the YAML frontmatter (
nameanddescription) from every SKILL.md file. This builds a lightweight index. - Match phase â When a user sends a message, the tool compares it against all descriptions. If thereâs a match, the full markdown body loads into context.
This means your description field is everything. If it doesnât match, the skill never fires.
Where do SKILL.md files live?
Paths vary by tool, but the format is the same:
| Tool | Personal skills path | Project skills path |
|---|---|---|
| Claude Code | ~/.claude/skills/ | .claude/skills/ |
| Codex CLI | ~/.codex/skills/ or ~/.agents/skills/ | .codex/skills/ |
| VS Code Copilot | ~/.github/skills/ | .github/skills/ |
| Gemini CLI | ~/.gemini/skills/ | .gemini/skills/ |
| Antigravity CLI | ~/.agy/skills/ | .agy/skills/ |
| Kiro CLI | ~/.kiro/skills/ | .kiro/skills/ |
The directory name becomes the invocation command. In Claude Code, type /skill-name. In Codex, type $skill-name. The format inside SKILL.md is identical across all platforms.
The anatomy of a SKILL.md
---
name: deploy-to-staging
description: "Use when the user asks to deploy, push to staging, or ship to test environment. Runs the deployment pipeline with safety checks."
---
# Deploy to Staging
## When to use
- User says "deploy", "push to staging", "ship it"
- After tests pass and user wants to see changes live
## Instructions
1. Run `pnpm test` and confirm all tests pass
2. Run `pnpm build` to verify build succeeds
3. Execute `./scripts/deploy-staging.sh`
4. Report the deployment URL back to user
## Rules
- Never deploy if tests are failing
- Always confirm with user before running deploy script
- If deploy fails, show the error log and suggest fixes
How to write a description that actually triggers
The description is the make-or-break field. Hereâs what works and what doesnât:
| Bad description | Why it fails | Good description |
|---|---|---|
| âHelps with deploymentsâ | Too vague, wonât match specific requests | âUse when the user asks to deploy, push to staging, or ship to test" |
| "Code review toolâ | Doesnât describe when to trigger | âUse when reviewing PRs, checking code quality, or before merging branches" |
| "Database utilitiesâ | No trigger words | âUse when the user asks to run migrations, seed data, or query the databaseâ |
Rules for descriptions:
- Start with âUse whenâ â forces you to describe the trigger scenario
- Include the actual words users say â âdeployâ, âpushâ, âshipâ, not âinitiate deployment sequenceâ
- List 2-3 trigger scenarios â covers variations of the same intent
- Keep it under 200 characters â tools truncate long descriptions
Five mistakes that break skills
1. The mega-skill
One SKILL.md that does everything: deploys, tests, reviews, writes docs, makes coffee.
Fix: One skill, one job. If youâre using âandâ more than once in the description, split it.
2. Documentation-style body
Writing prose that explains the concept instead of giving commands.
<!-- Bad: reads like documentation -->
The deployment process involves several stages including
building the application, running tests, and pushing to
the staging environment...
<!-- Good: imperative instructions -->
1. Run `pnpm build`
2. Run `pnpm test`
3. Execute `./scripts/deploy.sh`
4. Report the URL from stdout
3. No examples
The agent doesnât know what good output looks like without examples.
## Example
Input: "deploy the auth service to staging"
Output:
- Ran tests: 47 passed, 0 failed
- Built successfully (2.3s)
- Deployed to: https://staging.example.com/auth
- Health check: â
200 OK
4. Conflicting skills
Two skills with overlapping descriptions. The tool picks one semi-randomly.
Fix: Make descriptions mutually exclusive. âUse when deploying to stagingâ vs âUse when deploying to productionâ â not âUse when deployingâ.
5. Body exceeds 500 lines
Large bodies eat context window and slow down responses.
Fix: Move reference material to a references/ subdirectory:
my-skill/
âââ SKILL.md # Core instructions (under 500 lines)
âââ references/
âââ api-schema.md # Loaded only when referenced
âââ examples.md
Complete template you can copy
---
name: skill-name-here
description: "Use when [exact trigger phrase 1], [trigger phrase 2], or [trigger phrase 3]. Does [what the skill produces]."
---
# Skill Title
## When to use
- [Specific scenario 1]
- [Specific scenario 2]
- [Specific scenario 3]
## Instructions
1. [First step â be specific about inputs]
2. [Second step â include exact commands]
3. [Third step â describe expected output]
## Example
Input: "[realistic user message]"
Output:
[what the agent should produce]
## Rules
- [Hard constraint 1]
- [Hard constraint 2]
- [What to never do]
How to test your skill
Change the description, then test if it triggers:
# In Claude Code / Codex / Kiro CLI:
> deploy to staging
# Did the skill activate? Check the context panel.
# If not, tweak the description and try again.
Iterate on the description until the trigger is reliable. This is the only testing that matters.
SKILL.md vs other config files
| File | Loads when | Use for |
|---|---|---|
| SKILL.md | On demand (description match) | Specialized workflows |
| CLAUDE.md / AGENTS.md | Every conversation | Project-wide rules, style guides |
| .cursorrules | Every conversation (Cursor) | Editor-specific preferences |
| .env | Always | Secrets, API keys |
Advanced: Frontmatter fields beyond name and description
The spec has evolved. Here are fields youâll encounter across platforms:
| Field | Platform | Purpose |
|---|---|---|
disable-model-invocation | Claude Code, VS Code Copilot | Set to true to prevent auto-triggering. Skill only fires via /skill-name. Use for workflows with side effects. |
context: fork | VS Code Copilot (experimental) | Runs the skill in a dedicated subagent context instead of inline. |
dependencies | Claude Code | Declares packages (e.g., python>=3.8, pandas>=1.5.0). Claude installs them when loading. |
user-invocable | Claude Code | Set to false to make a skill model-only (no slash command). |
For Codex specifically, implicit invocation control lives in a separate agents/openai.yaml file with policy.allow_implicit_invocation: false â not in the SKILL.md frontmatter.
Naming rules (per the agentskills.io spec):
- Maximum 64 characters
- Lowercase letters, numbers, and hyphens only
- Must match the directory name
- Cannot contain reserved words (âanthropicâ, âclaudeâ)
- Description maximum: 1,024 characters (truncated at 1,536 combined with
when_to_use)
Security considerations
Skills are text instructions, not sandboxed code. Treat them like dependencies:
- Read a skillâs SKILL.md before installing it â itâs a text file, takes 30 seconds
- Prefer skills from known repos (
github.com/anthropics/skills,github.com/openai/skills) - Be cautious with skills that include executable scripts in
scripts/ - Use project-level skills (
.claude/skills/) over global ones (~/.claude/skills/) to limit blast radius - The spec doesnât yet have skill signing or trust levels â read what you install