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:

  1. Reads the SKILL.md file
  2. Pulls the instructions in as additional context
  3. Runs the task following those instructions
  4. 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:

LocationPathApplies to
EnterpriseVia managed settingsEvery user in the organization
Personal~/.claude/skills/<name>/SKILL.mdAll of your projects
Project.claude/skills/<name>/SKILL.mdThis project only
Plugin<plugin>/skills/<name>/SKILL.mdWherever 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

FieldDescription
nameThe skill's name (default: the directory name). Lowercase, hyphens only.
descriptionWhat it does and when to use it. Claude uses this to decide when to invoke the skill automatically. Max 1,536 chars.
when_to_useExtra context about when to invoke it (trigger phrases, examples).
argument-hintHint for autocomplete. Example: [issue-number]
disable-model-invocationtrue = only you can invoke it, not Claude. Default: false.
user-invocablefalse = only Claude can invoke it (hidden from the / menu). Default: true.
allowed-toolsTools Claude can use without asking permission while the skill is active.
modelA specific model for this skill.
effortEffort level while the skill is active (low/medium/high/xhigh/max).
contextfork = runs in an isolated subagent.
agentWhich kind of subagent to use when context: fork.
hooksHooks scoped to this skill's lifecycle.
pathsGlob patterns that limit when the skill activates.

Invocation control: who can invoke a Skill

FrontmatterYou can invokeClaude can invokeWhen it loads
(default)YesYesDescription in context, full skill on invocation
disable-model-invocation: trueYesNoDescription NOT in context, loads on invocation
user-invocable: falseNoYesDescription 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. A legacy-system-context skill explains how an old system works — useful for Claude, not something you'd invoke.

Skill vs CLAUDE.md

This distinction is fundamental:

AspectCLAUDE.mdSkill
When it loadsAlways, at session startDescription at start, content only on invocation
PurposeGeneral project contextA specific task or specialized knowledge
Ideal size< 200 lines20-500 lines in SKILL.md
Example"This project uses TypeScript with ESLint""To create a component, follow these 5 steps"
Context costAlways in contextZero 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:

  1. Load the contents of SKILL.md
  2. Apply the instructions (analogy, diagram, walk-through, gotcha)
  3. 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

VariableDescription
$ARGUMENTSAll arguments as one complete string
$NThe 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:

SkillWhat it does
/simplifyReviews modified code: reuse, quality, efficiency
/batchRuns multiple related operations in a batch
/debugHelps debug a specific problem
/loopRuns a prompt in a loop on an interval
/claude-apiHelps 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:

  1. Check the description: It should include keywords users would naturally say
  2. Confirm it's listed: Ask Claude "what skills do you have available?"
  3. Rephrase your request: Make it line up more closely with the description
  4. Invoke it manually: /skill-name to force the invocation

The skill activates too often

If Claude invokes the skill when you don't want it to:

  1. Make the description more specific: Fewer generic keywords
  2. Add disable-model-invocation: true if 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_BUDGET variable

The skill doesn't show up after you create it

If you just created a skill and it isn't showing:

  1. Check the path: .claude/skills/<name>/SKILL.md (with the intermediate directory)
  2. Check the frontmatter: The --- at the start and end are required
  3. 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:

  1. Stages all changes
  2. Generates a commit message following Conventional Commits
  3. Runs the commit
  4. 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:

  1. Takes a component name as an argument
  2. Creates the component's full structure
  3. 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:

  1. Has a lightweight SKILL.md with an overview
  2. Has a checklist.md with the detailed review checklist
  3. References checklist.md from SKILL.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:

  1. "We use strict TypeScript across the whole project"
  2. "When you create an endpoint, validate inputs with Zod"
  3. "The backend lives in apps/api, the frontend in apps/web"
  4. "To deploy, run ./scripts/deploy.sh and wait for confirmation"
  5. "Tests with Vitest, not Jest"
See solution
ItemWhereWhy
1. Strict TypeScriptCLAUDE.mdApplies to everything Claude writes
2. Endpoint with ZodSkill /new-endpointOnly applies when you create endpoints
3. Repo structureCLAUDE.mdGeneral project context
4. Deploy with a scriptSkill /deploy with disable-model-invocation: trueSpecific task, side effects
5. Vitest not JestCLAUDE.mdApplies 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
  • description in the frontmatter lets Claude invoke the skill automatically when it's relevant
  • disable-model-invocation: true → only you invoke it (deploy, commit, actions with side effects)
  • user-invocable: false → only Claude invokes it (background knowledge)
  • allowed-tools pre-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.md under 500 lines — push detail into supporting files
  • Live change detection: adding/editing skills takes effect without a restart

Additional resources

  1. Claude Code Skills — Official documentation — Complete, up-to-date reference
  2. Agent Skills Standard — The open standard Claude Code implements
  3. Claude Code Commands Reference — List of bundled skills and built-in commands
  4. Subagents — Delegating skills to specialized agents (context: fork)
  5. Plugins — Packaging and distributing skills with plugins
  6. Hooks — Automating workflows around tool events