The Problem of Repeating Everything
Have you ever had to explain the same thing to someone twenty times? Now imagine that, but with a robot that also loses its memory every few hours.
“No, Claude, the commit has to pass tests first.”
“Claude, I already told you to use the type: description format.”
“Don’t add emojis, damn it!”
This was my daily routine until I discovered Skills. In plain English: they’re instructions you write once and Claude follows forever. Like training a dog, but without the treats.
What Skills Are
Since version 2.1.3, Claude Code merged the old slash commands with something more powerful: Skills. They’re Markdown files with instructions that Claude can execute in two ways:
- Manually: when you type
/my-skill - Automatically: when Claude detects it should use it
That second point is the magic. You no longer have to remember to invoke the command. If you have a skill that says “use when the user finishes a task and has uncommitted changes,” Claude will do it automatically.
It’s like having a butler who knows when to clear the table without being asked.
Where They Live
~/.claude/skills/ # Personal (all your projects)
.claude/skills/ # Project-specific (shared with team)
~/.claude/commands/ # Legacy, still works
.claude/commands/ # Legacy, still works
If you want only you to use the skill, put it in your home. If you want the whole team to have it, commit it to the repo. That simple.
Anatomy of a Skill
A skill is a Markdown file with YAML frontmatter and then the content:
| |
That’s the minimum. But the frontmatter has quite a few more options worth knowing.
Required Fields
name
The skill identifier. Only lowercase, numbers and hyphens (max 64 characters). Must match the file or directory name.
| |
description
This is the most important field. Claude uses it for two things:
- Deciding when to auto-invoke the skill
- Understanding what it should do
Maximum 1024 characters. Include keywords that the user would naturally say.
| |
Optional Fields
model
Forces a specific model for this skill. Useful for tasks requiring more capability.
| |
If not specified, uses the current conversation’s model.
allowed-tools
Restricts which tools Claude can use. Critical for read-only or secure skills.
| |
Practical example: an analysis skill that should NOT touch anything:
| |
context: fork
Runs the skill in an isolated sub-agent with its own context. The main conversation history doesn’t get contaminated.
| |
Useful for complex multi-step operations where you don’t want to fill the chat with noise.
agent
Only works with context: fork. Defines what type of agent executes the skill.
| |
user-invocable
Controls whether it appears in the / (slash commands) menu. Defaults to true.
| |
Useful for internal skills that should only trigger automatically.
disable-model-invocation
Blocks Claude from invoking the skill on its own. Only you can activate it with /name.
| |
Useful for destructive or expensive operations that require explicit human decision.
hooks
Defines hooks that run during the skill lifecycle. Supports PreToolUse, PostToolUse and Stop.
| |
Substitution Variables
Within the skill content you can use:
| Variable | Contains |
|---|---|
$ARGUMENTS | Arguments passed when invoking /skill arg1 arg2 |
${CLAUDE_SESSION_ID} | Current session ID (useful for logs) |
Summary Table
| Field | Required | Purpose |
|---|---|---|
name | ✓ | Skill identifier |
description | ✓ | When and what to use it for |
model | Force specific model | |
allowed-tools | Restrict tools | |
context | fork for isolated sub-agent | |
agent | Agent type (with context: fork) | |
user-invocable | Show/hide in / menu | |
disable-model-invocation | Block auto-invocation | |
hooks | Lifecycle hooks |
Complete Example
| |
For the complete reference, check the official Agent Skills documentation.
Free Text or Deterministic Code?
This is the million-dollar question: if skills are Markdown, does it mean Claude always “interprets” what you write? Can I do something truly predictable?
The short answer: skills are as deterministic as you write them.
Think of a spectrum:
Vague/Flexible ──────────────────────────► Deterministic
"review the code" "execute these 3 commands in order"
Flexible skill (Claude decides)
| |
Here Claude has total freedom. Can look at whatever it wants, suggest whatever seems right. Useful for exploration, dangerous for critical processes.
Deterministic skill (disguised script)
| |
This is basically a 3-line script. Claude has no room to be creative. Execute, report, period.
Skill with conditional logic
| |
- If NOT
main→ ABORT with “Only from main”
Step 2: Clean state
| |
- If there’s output → ABORT with “Uncommitted changes”
Step 3: Bump + push
| |
There's branch logic here, but it's still deterministic: the conditions are explicit.
### Skill that invokes a real script
If you need truly complex logic (loops, parsing, APIs), put the code in a script and have the skill just execute it:
.claude/skills/deploy/ ├── SKILL.md └── deploy.sh
**SKILL.md:**
```markdown
---
name: deploy
description: Deploys to production
---
Execute:
```bash
bash .claude/skills/deploy/deploy.sh
Report the result. DO NOT modify the script.
**deploy.sh:**
```bash
#!/bin/bash
set -e
uv run pytest || exit 1
hugo --minify
rsync -avz public/ user@server:/var/www/
Best of both worlds: complex logic lives in Bash/Python where it belongs, and the skill is just the trigger.
When to use each approach
| Need | Approach |
|---|---|
| Fixed commands, always the same | Deterministic skill |
| Complex logic with many branches | External script |
| Analysis requiring judgment | Flexible skill with guardrails |
| Dangerous operations | Restrictive allowed-tools |
Real Example: the commit skill
This is the one I use most. Before I had to remember: “okay, run tests, then linter, then type-check, and only then commit”. Now I simply say “commit” and Claude does everything automatically.
| |
Phase 2: MANDATORY verifications
| |
If any fails, DO NOT continue.
Phase 3: Create commit
git add -A- Analyze changes
- Generate message:
type: description git commit
See the "When to Use" section? That's what enables auto-invocation. Claude reads that and thinks: "ah, the user just said 'done', there are pending changes, I should use this skill".
## Another example: task archiving
If you use a `TASKS.md` file to track what you do (I did before Beads), this skill automatically cleans completed tasks:
```markdown
---
name: archive-tasks
description: Archives completed tasks from TASKS.md to TASKS-DONE.md.
Use automatically when TASKS.md has many completed tasks
or exceeds 20K tokens.
---
# Archive Tasks
## When to Use (Automatic)
- TASKS.md has more than 50 completed tasks `[x]`
- TASKS.md exceeds 20,000 tokens
- User mentions TASKS.md is too big
## Process
1. Read `docs/llm/TASKS.md`
2. Identify completed tasks (`[x]`)
3. Move to `docs/llm/TASKS-DONE.md` with date
4. Remove from TASKS.md
5. Report how many were archived
## Rules
- **DO NOT delete** pending tasks `[ ]`
- **PRESERVE** context (parent section)
- **ADD** archival date
The beauty is you don’t have to remember. Claude sees that TASKS.md is huge and acts.
Tips for Writing Good Skills
1. Specific descriptions
| |
2. Define when to apply
| |
3. Be explicit about prohibitions
Claude tends to want to be polite and ask for confirmation. If you don’t want that, say so clearly:
| |
4. Use model: opus for important things
If the skill does something critical (security audit, complex refactoring), force the most capable model:
| |
5. Restrict tools if needed
Sometimes you want a skill that only reads, without modifying anything:
| |
Simple vs Complex Skills
A simple skill is a single file:
.claude/skills/review.md
A complex skill is a directory with resources:
.claude/skills/deploy/
├── SKILL.md # Instructions
├── templates/
│ └── k8s-deployment.yaml
└── scripts/
└── healthcheck.sh
Claude can read the directory files as additional context.
What I Use
| Skill | For what | Auto-invocation |
|---|---|---|
commit | Commit with verifications | When I say “commit” or finish something |
check-diagnostics | Check types and lint | Before commits |
owasp | Security audit | Manual (it’s expensive) |
archive-tasks | Clean up TASKS.md | When it’s too big |
The Difference from Legacy Commands
| Aspect | Skills | Commands |
|---|---|---|
| Auto-invocation | Yes | No |
| Structure | Directory or file | File only |
| Recommendation | Use for everything new | Legacy |
Commands still work, but skills are strictly better. If you have old commands, no need to migrate them, but for new things use skills.
Conclusion
Skills are basically programming, but in natural language. You define what you want to happen, when, and with what restrictions. Claude does the rest.
The best part is they’re versioned with your code. If you work on a team, everyone has the same skills. If you change something, it’s in git history.
Is it worth the effort to write them? If you repeat the same thing more than three times, absolutely. Every skill you write is a conversation you’ll never have to have again.
Now if you’ll excuse me, I need to go teach Claude that “refactor” doesn’t mean “rewrite everything from scratch”.
TL;DR: Skills are Markdown instructions that Claude Code executes manually or automatically. They can be as flexible or deterministic as you need: from “analyze this” to scripts disguised as prose. If you need complex logic, invoke external scripts. They live in .claude/skills/ and are versioned with your code.