If you use Claude Code or claude.ai, you've probably caught yourself giving the same instructions over and over. "Use conventional commits." "Run the linter before committing." "Check for breaking changes." Every new conversation, you start from scratch.
Agent Skills fix this. You package your instructions, conventions, and scripts into a reusable module that Claude picks up automatically.
What are Agent Skills?
Skills are directories containing a SKILL.md file with instructions, optional reference files, and optional scripts. Think of them as onboarding docs for Claude — reusable and automatically triggered when your request matches.
Anthropic ships pre-built Skills (PowerPoint, Excel, Word, PDF). The real power is custom Skills: your expertise, your conventions, your patterns.
Where they work:
-
Claude Code — drop into
.claude/skills/(project) or~/.claude/skills/(global). Auto-discovered. - claude.ai — upload as zip via Settings > Features (Pro/Max/Team/Enterprise).
Skills don't sync across surfaces — manage them separately.
How the loading works
Skills use progressive disclosure — they load in three stages, not all at once:
-
Metadata (always loaded) — Claude sees only
nameanddescriptionfrom YAML frontmatter. ~100 tokens per Skill. -
Instructions (when triggered) — Claude reads
SKILL.mdwhen your request matches. Instructions enter the context window. - Resources (as needed) — Additional files and scripts are loaded only when referenced. Scripts are executed via bash — their code never enters context, only the output.
This matters because every token in your Skill competes with conversation history. Understanding this mechanism is key to writing efficient Skills.
The practices that matter most
Nail the description
The description determines whether Claude picks up your Skill at all. Write in third person, be specific, include trigger words:
# Good — Claude knows exactly when to use this
description: >
Reviews code changes, generates commit messages, and validates
diffs before committing. Use when the user asks to review code,
prepare a commit, write a commit message, or open a pull request.
# Bad — too vague to match reliably
description: Helps with git stuff.
Constraints: name is lowercase + hyphens only (max 64 chars), description max 1024 chars, no XML tags in either.
Write for Claude, not for humans
Claude already knows what commits and pull requests are. Don't explain basics — focus on your specific conventions. If you wouldn't explain it to a senior dev joining your team, don't explain it to Claude.
Structure for progressive disclosure
Keep SKILL.md under 500 lines. Split detailed content into separate files referenced from the main file:
## Commit messages
Short version: `type(scope): description`
For detailed rules and examples, see [COMMIT_CONVENTIONS.md](COMMIT_CONVENTIONS.md)
Claude loads COMMIT_CONVENTIONS.md only when dealing with commits. Keep references one level deep — nested chains (A → B → C) get partially read.
Use scripts for deterministic tasks
Anything that should run the same way every time belongs in a script. They're more reliable than generated code, and more token-efficient since only the output enters context:
Run `bash scripts/check_diff.sh` to get a diff summary.
Build feedback loops
For quality-critical tasks, add validation:
1. Run `bash scripts/check_diff.sh`
2. If warnings found, fix them
3. Run again — only proceed when clean
Control the thinking effort
You can set how much reasoning Claude puts into your Skill via the effort field in the frontmatter:
---
name: reviewing-code
description: Reviews code changes and generates commit messages.
effort: max
---
Values: low, medium, high, max. Use max (opus only) for very complex tasks only because it consumes a lot of tokens. Use low for straightforward, mechanical operations like formatting or file renaming. This directly controls Claude's thinking depth when executing the Skill.
Delegate to a subagent
For complex Skills, you can have Claude spawn a subagent to handle the Skill in isolation. This keeps the main conversation's context clean and lets the Skill run with its own dedicated context window.
Configure it in the frontmatter:
---
name: reviewing-code
description: Reviews code changes and generates commit messages.
effort: high
model: claude-sonnet-4-6
allowed-tools: Read, Bash, Grep
---
When model and allowed-tools are specified, Claude delegates the task to a subagent running that model with only those tools. The main conversation receives the result without carrying the Skill's full execution context.
Anti-patterns to avoid
- Time-sensitive info — "Before August 2025, use the old API" will rot. Use "current" and "legacy" sections.
- Too many options — Pick a default tool/library, mention one alternative for edge cases.
- Assuming packages exist — List install commands explicitly.
- Deep reference chains — Keep everything one link from SKILL.md.
Complete example: a code review Skill
Here's a full Skill applying all the above practices.
Structure
code-review/
├── SKILL.md
├── COMMIT_CONVENTIONS.md
└── scripts/
└── check_diff.sh
SKILL.md
---
name: reviewing-code
description: >
Reviews code changes, generates commit messages, and validates
diffs before committing. Use when the user asks to review code,
prepare a commit, write a commit message, check a diff, or
open a pull request.
effort: high
---
Below the frontmatter, the markdown body:
# Code Review Assistant
## Review process
1. Run `bash scripts/check_diff.sh` to get an overview
2. Analyze changes by category: breaking changes, new features,
bug fixes, refactoring
3. For each file, check: unrelated changes mixed in? Error handling?
Edge cases? Docs on public methods?
## Commit messages
Conventional Commits: `type(scope): description`
Types: feat, fix, refactor, docs, test, chore, ci.
For detailed rules, see [COMMIT_CONVENTIONS.md](COMMIT_CONVENTIONS.md).
## PR preparation
1. Run `bash scripts/check_diff.sh` and fix any issues
2. One logical change per commit
3. PR description explains the "why", not the "what"
COMMIT_CONVENTIONS.md
Loaded only when Claude needs to write or review commit messages:
# Commit Message Conventions
## Format
type(scope): short description
[optional body]
[optional footer]
## Types
- feat: New user-visible feature
- fix: User-visible bug fix
- refactor: No behavior change
- docs: Documentation only
- test: Adding/updating tests
- chore: Build, tooling, dependencies
- ci: CI/CD changes
## Example
feat(auth): add JWT token refresh endpoint
Tokens auto-refresh 5 minutes before expiry.
Closes #142
scripts/check_diff.sh
Executed by Claude — only the output enters context:
#!/bin/bash
echo "=== Staged changes summary ==="
git diff --cached --stat 2>/dev/null || git diff --stat
echo ""
echo "=== Looking for common issues ==="
if git diff --cached -G "(console\.log|debugger|print\()" --name-only 2>/dev/null | grep -q .; then
echo "WARNING: Debug statements found in:"
git diff --cached -G "(console\.log|debugger|print\()" --name-only
fi
git diff --cached --stat 2>/dev/null | awk '$3 > 500 {print "WARNING: Large change - " $0}'
echo "=== Done ==="
Getting started
- Create a directory with a
SKILL.md - Add YAML frontmatter (
name,description, optionallyeffort) - Write concise instructions in the body
- Drop into
.claude/skills/or upload as zip on claude.ai
Start with a real problem you face repeatedly. A focused Skill that does one thing well beats a sprawling one every time.
The official documentation covers the full spec, and the best practices guide goes deeper on advanced patterns.
If you found this helpful, you can buy me a coffee ☕.
Top comments (0)