Module 5: Skills and Hooks: automating your workflow
Creating Skills: extending Claude Code with reusable instructions
Creating Skills: extending Claude Code with reusable instructions
Overview
Skills are how you extend Claude Code with reusable capabilities. They're directories with a SKILL.md file (plus optional supporting files) that live in .claude/skills/. Claude Code detects them automatically — and they can be invoked two ways: by you with a slash command (/skill-name), or by Claude itself when it detects the skill is relevant to what you asked for.
This capsule covers the current Skills format: the directory structure, the YAML frontmatter that configures behavior, the difference between manual and automatic invocation, how Skills relate to CLAUDE.md, and the bundled Skills that ship with Claude Code. The Skills format follows the open Agent Skills standard, which works across multiple AI tools.
The gap between someone who repeats the same instructions in every prompt and someone who has Skills configured is the gap between typing code by hand and using snippets — with the extra twist that Claude can invoke the Skill on its own when it judges that to be the right move.
What Skills are
A Skill is a directory in .claude/skills/ with at least a SKILL.md file. When you or Claude invoke a Skill, Claude:
- Reads the
SKILL.mdfile - Pulls the instructions in as additional context
- Runs the task following those instructions
- Optionally reaches for supporting files inside the skill's directory (templates, scripts, examples)
SKILL.md has two parts: YAML frontmatter between --- that configures the skill, and markdown content with the instructions Claude follows.
A note on the format change
Custom commands (
.claude/commands/name.md) were merged into Skills. The old format still works, but Skills are the recommended path. Skills add: a directory for supporting files, YAML frontmatter to control invocation, and the ability for Claude to load them automatically when they're relevant.
Where Skills live (4 scope levels)
Where you store a Skill determines who can use it:
| Location | Path | Applies to |
|---|---|---|
| Enterprise | Via managed settings | Every user in the organization |
| Personal | ~/.claude/skills/<name>/SKILL.md | All of your projects |
| Project | .claude/skills/<name>/SKILL.md | This project only |
| Plugin | <plugin>/skills/<name>/SKILL.md | Wherever the plugin is active |
Precedence when names collide: enterprise > personal > project. Plugins use a plugin-name:skill-name namespace, so they never clash.
When to use each level
Personal (~/.claude/skills/):
→ Skills you use across all your projects
→ Example: /commit (your commit workflow)
→ Example: /review (your code review style)
Project (.claude/skills/):
→ Skills specific to one project
→ Committable to the repo — your team shares them
→ Example: /deploy-staging (deploy to your infra)
→ Example: /new-component (your React component pattern)
Plugin:
→ Skills distributed via the plugin marketplace
→ You don't write these — you install them
Enterprise:
→ Configured by administrators
→ Apply across the whole organization
Anatomy of a Skill
A Skill is a directory, not a file. SKILL.md is the entrypoint, but you can include additional files:
my-skill/
├── SKILL.md # Main instructions (required)
├── template.md # Template for Claude to fill in
├── examples/
│ └── sample.md # Example of the expected output
└── scripts/
└── validate.sh # Script Claude can run
SKILL.md is required. The rest are optional and give you more power: templates, examples, executable scripts, detailed reference documentation. Reference these files from SKILL.md so Claude knows what they contain and when to load them.
Key rule: Keep SKILL.md under 500 lines. Move detailed reference material into separate files.
YAML frontmatter: configuring the Skill's behavior
The YAML frontmatter at the top of SKILL.md controls how the Skill works:
---
name: my-skill
description: What this skill does and when to use it
disable-model-invocation: true
allowed-tools: Read Grep
---
Markdown instructions go here...
Every field is optional. Only description is recommended, so Claude knows when to use the skill.
Frontmatter fields
| Field | Description |
|---|---|
name | The skill's name (default: the directory name). Lowercase, hyphens only. |
description | What it does and when to use it. Claude uses this to decide when to invoke the skill automatically. Max 1,536 chars. |
when_to_use | Extra context about when to invoke it (trigger phrases, examples). |
argument-hint | Hint for autocomplete. Example: [issue-number] |
disable-model-invocation | true = only you can invoke it, not Claude. Default: false. |
user-invocable | false = only Claude can invoke it (hidden from the / menu). Default: true. |
allowed-tools | Tools Claude can use without asking permission while the skill is active. |
model | A specific model for this skill. |
effort | Effort level while the skill is active (low/medium/high/xhigh/max). |
context | fork = runs in an isolated subagent. |
agent | Which kind of subagent to use when context: fork. |
hooks | Hooks scoped to this skill's lifecycle. |
paths | Glob patterns that limit when the skill activates. |
Invocation control: who can invoke a Skill
| Frontmatter | You can invoke | Claude can invoke | When it loads |
|---|---|---|---|
| (default) | Yes | Yes | Description in context, full skill on invocation |
disable-model-invocation: true | Yes | No | Description NOT in context, loads on invocation |
user-invocable: false | No | Yes | Description in context, loads on invocation |
When to use each:
- Default: Skills with safe effects that could be useful proactively
disable-model-invocation: true: Workflows with side effects or critical timing (/commit,/deploy,/send-slack-message). You don't want Claude deciding to deploy because the code looks ready.user-invocable: false: Background knowledge that isn't actionable as a command. Alegacy-system-contextskill explains how an old system works — useful for Claude, not something you'd invoke.
Skill vs CLAUDE.md
This distinction is fundamental:
| Aspect | CLAUDE.md | Skill |
|---|---|---|
| When it loads | Always, at session start | Description at start, content only on invocation |
| Purpose | General project context | A specific task or specialized knowledge |
| Ideal size | < 200 lines | 20-500 lines in SKILL.md |
| Example | "This project uses TypeScript with ESLint" | "To create a component, follow these 5 steps" |
| Context cost | Always in context | Zero until invoked |
Rule of thumb: If the instruction applies to EVERYTHING Claude does in your project → CLAUDE.md. If it applies only to one specific task, or it's long reference material → Skill.
The distinction in practice
CLAUDE.md:
## Code conventions
- Strict TypeScript
- CSS Modules for styling
- Tests with Vitest
.claude/skills/create-component/SKILL.md:
---
name: create-component
description: Create a new React component following the project's conventions. Use when the user asks to create a new component.
---
When creating a component:
1. Create the file at src/components/[Name]/[Name].tsx
2. Use TypeScript with an interface for props
3. Create [Name].module.css
4. Create __tests__/[Name].test.tsx
5. Create index.ts with a barrel export
CLAUDE.md says "we use CSS Modules". The skill says "when you create a component, here are the exact steps".
Creating your first Skill, step by step
Let's build a simple skill: explain-code, which explains how code works using analogies and ASCII diagrams.
Step 1: Create the skill's directory
mkdir -p ~/.claude/skills/explain-code
We use ~/.claude/skills/ (personal) so it's available across every project. If you want it scoped to the current project only, use .claude/skills/explain-code inside the project.
Step 2: Create the SKILL.md file
touch ~/.claude/skills/explain-code/SKILL.md
Step 3: Write the frontmatter and the instructions
Open SKILL.md and write:
---
name: explain-code
description: Explains how code works with visual diagrams and analogies. Use when someone asks "how does this work?", when they're learning a codebase, or when teaching technical concepts.
---
When explaining code, always include:
1. **Start with an analogy:** Compare the code to something from everyday life
2. **Draw a diagram:** Use ASCII art to show flow, structure, or relationships
3. **Step-by-step walk-through:** Explain what happens on each important line
4. **One common gotcha:** A frequent mistake or misunderstanding
Keep explanations conversational. For complex concepts, use multiple analogies.
Step 4: Try the Skill both ways
Manual invocation with the slash command:
> /explain-code src/auth/login.ts
Automatic invocation — Claude spots that it's relevant from the description:
> how does this code work?
In both cases, Claude includes an analogy and an ASCII diagram in its explanation.
Step 5: Confirm it works
When it runs, Claude should:
- Load the contents of SKILL.md
- Apply the instructions (analogy, diagram, walk-through, gotcha)
- Answer following that pattern
Arguments: passing parameters to a Skill
Both you and Claude can pass arguments when invoking a Skill. Arguments are available via $ARGUMENTS, or by index with $0, $1, $2.
Example: a skill that fixes a GitHub issue
---
name: fix-issue
description: Fixes a GitHub issue
disable-model-invocation: true
---
Fix GitHub issue $ARGUMENTS following our coding standards.
1. Read the issue description
2. Understand the requirements
3. Implement the fix
4. Write tests
5. Create a commit
When you run /fix-issue 123, Claude receives: "Fix GitHub issue 123 following our coding standards..."
Positional arguments with $0, $1, $2
---
name: migrate-component
description: Migrates a component from one framework to another
---
Migrate the $0 component from $1 to $2.
Preserve all existing behavior and tests.
Running /migrate-component SearchBar React Vue:
$0→SearchBar$1→React$2→Vue
Note: Use quotes for values with spaces: /migrate-component "Search Bar" React Vue makes $0 equal Search Bar.
Other available substitutions
| Variable | Description |
|---|---|
$ARGUMENTS | All arguments as one complete string |
$N | The argument at position N (0-indexed) |
${CLAUDE_SESSION_ID} | The current session's ID |
${CLAUDE_SKILL_DIR} | The directory of the current SKILL.md |
Supporting files
Skills can include multiple files in their directory. This keeps SKILL.md focused on the essentials while Claude reaches for detailed reference material only when it needs it.
api-patterns/
├── SKILL.md # Overview and navigation
├── reference.md # Detailed API docs
├── examples.md # Usage examples
└── scripts/
└── helper.py # Executable script
Reference the supporting files from SKILL.md:
## Additional resources
- For full API details, see [reference.md](reference.md)
- For usage examples, see [examples.md](examples.md)
- To validate configuration, run [scripts/helper.py](scripts/helper.py)
Tip: Keep SKILL.md under 500 lines. Move long material into separate files.
Bundled Skills: the ones that ship with Claude Code
Claude Code includes pre-configured Skills available in every session:
| Skill | What it does |
|---|---|
/simplify | Reviews modified code: reuse, quality, efficiency |
/batch | Runs multiple related operations in a batch |
/debug | Helps debug a specific problem |
/loop | Runs a prompt in a loop on an interval |
/claude-api | Helps with the Claude API / Anthropic SDK |
You invoke them like any other skill: / + name. The full list is in the commands reference.
Pre-approving tools with allowed-tools
The frontmatter's allowed-tools field grants permission for the listed tools while the skill is active — Claude can use them without asking for approval each time.
---
name: commit
description: Stage and commit the current changes
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *)
---
With this skill active, Claude runs git add, git commit, and git status without asking for approval. It doesn't restrict which tools are available — it just pre-approves the ones listed.
Live change detection
Claude Code watches the skills directories for live changes. Adding, editing, or deleting a skill in ~/.claude/skills/ or .claude/skills/ takes effect in the current session, no restart needed.
Exception: Creating a new .claude/skills/ directory that didn't exist when the session started requires restarting Claude Code so it begins watching it.
Troubleshooting
The skill doesn't activate automatically
If Claude doesn't use your skill when you expect it to:
- Check the description: It should include keywords users would naturally say
- Confirm it's listed: Ask Claude "what skills do you have available?"
- Rephrase your request: Make it line up more closely with the description
- Invoke it manually:
/skill-nameto force the invocation
The skill activates too often
If Claude invokes the skill when you don't want it to:
- Make the description more specific: Fewer generic keywords
- Add
disable-model-invocation: trueif you only want manual invocation
Descriptions get truncated
Skill descriptions load into context under a character budget. If you have many skills, descriptions get shortened. The fix:
- Put the key information at the start of the description (front-loaded)
- Cut unnecessary text
- Raise the budget with the
SLASH_COMMAND_TOOL_CHAR_BUDGETvariable
The skill doesn't show up after you create it
If you just created a skill and it isn't showing:
- Check the path:
.claude/skills/<name>/SKILL.md(with the intermediate directory) - Check the frontmatter: The
---at the start and end are required - Restart Claude Code: If you created a
.claude/skills/directory that didn't exist before
Exercises
Exercise 1: Your first personal skill
Create a personal skill (~/.claude/skills/) called commit that:
- Stages all changes
- Generates a commit message following Conventional Commits
- Runs the commit
- Includes
disable-model-invocation: true(you don't want Claude committing on its own)
See solution
Create ~/.claude/skills/commit/SKILL.md:
---
name: commit
description: Stage and commit the changes with a message following Conventional Commits
disable-model-invocation: true
allowed-tools: Bash(git add *) Bash(git commit *) Bash(git status *) Bash(git diff *)
---
Create a commit from the current changes:
1. Run `git status` to see what changed
2. Run `git diff --staged` and `git diff` to understand the changes
3. Stage the relevant files with `git add`
4. Generate a commit message following Conventional Commits:
- `feat:` new functionality
- `fix:` bug fix
- `refactor:` code change with no behavior change
- `docs:` documentation only
- `test:` tests
- `chore:` maintenance
5. Run the commit
The message should be clear and explain the "why" behind the change when that applies.
Why it works: With disable-model-invocation: true, Claude won't commit automatically when it finishes writing code. With allowed-tools, it stops asking permission for every git command.
Exercise 2: A project skill with arguments
Create a project skill (.claude/skills/) called new-component that:
- Takes a component name as an argument
- Creates the component's full structure
- Gets invoked with
/new-component UserProfile
See solution
Create .claude/skills/new-component/SKILL.md:
---
name: new-component
description: Create a new React component with the project's standard structure
argument-hint: [ComponentName]
---
Create a new React component named $0 with this structure:
1. Create the directory: `src/components/$0/`
2. Create `src/components/$0/$0.tsx`:
- Functional component with TypeScript
- `$0Props` interface for the props
- Default export
3. Create `src/components/$0/$0.module.css` with a base `.container` class
4. Create `src/components/$0/__tests__/$0.test.tsx` with:
- A basic render test
- A props test
5. Create `src/components/$0/index.ts` with a barrel export
Don't use `any`, don't use `React.FC`. Use a `function` declaration, not an arrow function.
Why it works: $0 is replaced with the first argument. With /new-component UserProfile, the skill creates src/components/UserProfile/UserProfile.tsx, and so on.
Exercise 3: A skill with supporting files
Create a pr-review skill that:
- Has a lightweight
SKILL.mdwith an overview - Has a
checklist.mdwith the detailed review checklist - References
checklist.mdfromSKILL.md
See solution
.claude/skills/pr-review/SKILL.md:
---
name: pr-review
description: Systematic review of a Pull Request following the team's checklist
---
Review this Pull Request following the full checklist in [checklist.md](checklist.md).
Review format:
- Start with a 2-3 line summary
- Organize feedback by category (per the checklist)
- Separate "must fix" from "nice to have"
- At the end, give a recommendation: approve / request changes / comment
.claude/skills/pr-review/checklist.md:
# PR Review Checklist
## Functionality
- [ ] The code does what the PR says it does
- [ ] Edge cases considered
- [ ] Appropriate error handling
## Code quality
- [ ] Clear names
- [ ] No dead code
- [ ] Reasonable complexity
## Tests
- [ ] New tests for new functionality
- [ ] Existing tests pass
- [ ] Edge cases covered
## Documentation
- [ ] README updated if it applies
- [ ] Comments where the "why" isn't obvious
Why it works: SKILL.md stays short and focused. checklist.md loads only when Claude needs it. As this scales, you'd add an examples/ directory with well-reviewed PRs as reference.
Exercise 4: Deciding what goes in a Skill vs CLAUDE.md
For each item, decide whether it belongs in CLAUDE.md or in a Skill:
- "We use strict TypeScript across the whole project"
- "When you create an endpoint, validate inputs with Zod"
- "The backend lives in apps/api, the frontend in apps/web"
- "To deploy, run ./scripts/deploy.sh and wait for confirmation"
- "Tests with Vitest, not Jest"
See solution
| Item | Where | Why |
|---|---|---|
| 1. Strict TypeScript | CLAUDE.md | Applies to everything Claude writes |
| 2. Endpoint with Zod | Skill /new-endpoint | Only applies when you create endpoints |
| 3. Repo structure | CLAUDE.md | General project context |
| 4. Deploy with a script | Skill /deploy with disable-model-invocation: true | Specific task, side effects |
| 5. Vitest not Jest | CLAUDE.md | Applies to every test you write |
The rule: CLAUDE.md = the what. Skills = the how, for specific tasks.
Summary
- Skills are directories with
SKILL.md+ YAML frontmatter (the current format, replacing flat commands) - 4 scope levels: enterprise > personal (~/.claude/skills/) > project (.claude/skills/) > plugin
descriptionin the frontmatter lets Claude invoke the skill automatically when it's relevantdisable-model-invocation: true→ only you invoke it (deploy, commit, actions with side effects)user-invocable: false→ only Claude invokes it (background knowledge)allowed-toolspre-approves tools while the skill is active- Arguments:
$ARGUMENTS,$0,$1, etc. - Bundled skills:
/simplify,/batch,/debug,/loop,/claude-api - Skills vs CLAUDE.md: CLAUDE.md is permanent context; Skills are instructions loaded on demand
- Keep
SKILL.mdunder 500 lines — push detail into supporting files - Live change detection: adding/editing skills takes effect without a restart
Additional resources
- Claude Code Skills — Official documentation — Complete, up-to-date reference
- Agent Skills Standard — The open standard Claude Code implements
- Claude Code Commands Reference — List of bundled skills and built-in commands
- Subagents — Delegating skills to specialized agents (
context: fork) - Plugins — Packaging and distributing skills with plugins
- Hooks — Automating workflows around tool events