TLDR; Claude Code skills, hooks, subagents and commands each do a different job. Commands are prompt shortcuts you usually trigger yourself. Skills load expertise when it is relevant. Hooks run automatically when an event happens, so a rule holds without Claude remembering it. Subagents hand a job to a separate helper. In my experience, picking the wrong one is the most common mistake: match the tool to the job, and learn them in the order of the steps below.
When to use each of the four
- Retyping the same steps? Write a command.
- Repeating a multi-step workflow that needs knowledge? Write a skill.
- A rule that must hold every time? Write a hook, not a line in
CLAUDE.md. - A job that would flood your conversation with detail? Hand it to an agent.
- Skip all four for a one-liner. “Always use conventional commits” belongs in CLAUDE.md, the file Claude reads at the start of every session. A prompt you use once, you just type.
| Tool | Use it when | Triggered by | Runs in |
|---|---|---|---|
| Command | You retype the same steps | You type /name (Claude can too, unless you turn that off) |
The main conversation |
| Skill | A repeated workflow needs knowledge and supporting files | Claude’s judgement, or you type /name |
The main conversation |
| Hook | A rule or check must run every time | An event (automatic) | Outside the conversation |
| Agent | A job needs its own working space | Claude’s judgement, or a skill that calls for one | Its own separate context |
Commands and skills add to what Claude knows. Hooks and agents add to what Claude does on its own.
How Claude Code skills, hooks, agents and commands differ
Claude Code can only hold so much text at once. That space is the context window, and everything in it costs tokens, the word-fragments Claude bills by. Each of the four tools decides what enters that space, and when.
- Skills load on demand. Claude reads each skill’s short description at startup so it can decide when to use it. The full skill and its supporting files stay out of context until Claude uses it.
- Hooks run outside the model. Claude Code itself runs a hook, so Claude cannot skip it.
CLAUDE.mdinstructions are suggestions that Claude follows well but not perfectly. A hook that blocks a dangerous command runs every time, whatever Claude remembers; how well it protects you depends on what it checks. - Agents keep detail out of your conversation. An agent does its reading in a separate context and returns a summary: 1,000 to 2,000 tokens in my use.
- Commands are plain templates. You usually trigger them yourself; add
disable-model-invocation: trueto the frontmatter (the settings block at the top of the file) so Claude never does.
My rails-developer skill runs to about 8,000 tokens when fully loaded. Inside CLAUDE.md I would pay for it in every session, Swift and marketing included. As a skill, across a typical week of 50+ sessions, I pay for it only in the Rails ones.
How to build each Claude Code extension
Work in this order. Each step builds on the one before, and most people I work with need only the first two.
1. Write a command for steps you retype
A command is a Markdown file in ~/.claude/commands/, run with a slash. This one is next-task.md:
---
allowed-tools: Bash(git branch *), Bash(git status *), Bash(head *)
---
Pick up the next available task from the current task list.
## Current State
!`git branch --show-current`
!`git status --short | head -10`
## Steps
1. Run TaskList to see all tasks and their statuses
2. Find the lowest-ID task that is pending with no unresolved blockers
3. Create the feature branch and start work
The !`backtick` lines run before the prompt reaches Claude, so the branch name and git status are already filled in. Claude Code runs them itself, so Claude spends no tool call on them. The allowed-tools line lets those commands run; without it, a command you haven’t allowed stops the run. I type /next-task and Claude picks up where I left off. My /review-session command works the same way: it runs one review at the end of every session.
Check: typing /next-task shows the filled-in state and Claude starts on the right task.
2. Write a skill when the workflow needs knowledge
A skill is a directory with a SKILL.md file, plus optional references/ and examples/ folders for the heavy material. The file opens with frontmatter, then the instructions:
---
name: tech-lead
description: Use when asked to 'plan a feature', 'write a PRD',
or 'implement a feature'.
---
# Tech Lead Skill
Research, plan, and execute features end-to-end...
The description is the line Claude reads to decide whether to load the skill. When you say “plan this feature”, Claude scans the descriptions of your personal skills (~/.claude/skills/), project skills (.claude/skills/) and plugin skills (from installed plugins, step 6), then loads the match. The references/ files stay unread until the skill asks for them, so a large skill costs little until it is needed.
A skill and a command are both Markdown, and both answer to /name. A command is a single flat file. A skill is a directory, with room for supporting files. If the job is “do this sequence”, a command is enough. If it needs domain knowledge and reference material, write a skill.
My tech-lead skill classifies a request as a small fix, a change or a full feature. It then runs only the steps that size needs, such as research, planning, delegation and review. I would not type that as a prompt each time.
A skill can also wrap a local tool. The /transcribe skill runs speech recognition on your Mac and hands a speaker-labelled transcript back to Claude.
Check: a plain request such as “plan this feature” loads the skill without you typing its name.
3. Add a hook for a rule that must hold
Hooks live in settings.json (Claude Code’s settings file), under an event name. A hook can also carry a matcher, which filters the event: for tool events, it names the tool to watch. The most useful pattern is PreToolUse with a matcher on Bash, which checks each shell command before it runs:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/check-dangerous-commands.sh"
}
]
}
]
}
}
For a simple PreToolUse script like this one, exit code 2 blocks the tool call and feeds the error message back to Claude. Exit 0 raises no objection, and the normal permission rules still apply. If the hook itself crashes, the call goes ahead unless you set "onFailure": "block" (Claude Code v2.1.295 and later). This is how you enforce hard rules without relying on Claude to remember them: no force pushes, no rm -rf on certain paths, no committing .env files. A hook on Bash can stop Claude committing a .env file, but not reading it with the Read tool. The guide to Claude Code data privacy covers how to keep secrets and client records away from the model.
Hook exits with code 2?
yes
BlockedThe tool doesn’t run; the error message goes back to Claude
Otherwise
AllowedExit 0: the tool runs
Hooks fire on 30-plus events. These are the ones I use most:
- PreToolUse / PostToolUse: before and after Claude uses a tool such as Bash, Edit or Write
- Stop: when Claude finishes responding
- SubagentStop: when a helper agent finishes
- SessionStart / SessionEnd: the start and end of a session
- UserPromptSubmit: before your message reaches Claude
A hook runs one of five types of action:
- Command runs a shell script. In my use it covers 90% of cases.
- HTTP sends a request to a web address, for outside systems.
- Prompt sends a one-turn question to a model for a yes or no.
- Agent starts a helper agent for a harder check.
- MCP tool calls a tool on an already-connected MCP server (a connection that gives Claude another program’s tools), with no wrapper script.
If you catch yourself writing “NEVER do X” in CLAUDE.md, write a hook instead. The hamster plugin automates that move: it turns a mistake Claude keeps repeating into a hook rule that must pass its own tests.
Check: ask Claude to run the forbidden command. It is blocked, and Claude sees your error message.
4. Run your tests automatically with a hook
The same mechanism checks Claude’s work without you asking. This hook fires every time a helper agent finishes. If git shows unstaged changes to tracked files, the hook runs RuboCop (a Ruby style checker) in auto-fix mode, then the test suite:
{
"hooks": {
"SubagentStop": [
{
"hooks": [
{
"type": "command",
"command": "git diff --quiet || (bin/rubocop -a; bin/rails test); exit 0",
"statusMessage": "Running linters and tests after agent completed"
}
]
}
]
}
}
Swap in your own linter and test command. The ; exit 0 means this hook runs the checks but never blocks: a failing test doesn’t stop anything. A PostToolUse hook with "matcher": "Edit|Write" does the same after each edit. The quality gates guide builds on this mechanism for automated review and fact-checking. The hamster guide has more Claude Code hook examples.
Check: let a helper agent change a file. The linter and tests run when it finishes, with no prompt from you.
5. Hand a job to an agent
An agent is a separate Claude session with its own context window. Claude Code ships several; these three do most of the work:
- Explore: read-only codebase research. It searches and reads but cannot edit.
- Plan: read-only research Claude does in Plan Mode (where it plans but writes no code).
- general-purpose: full access to Claude Code’s tools, for jobs that build or change things.
For your own, write a Markdown file with frontmatter that sets what the agent may do:
---
name: knowledge-searcher
description: Deep multi-entry vault searches when /recall isn't sufficient.
model: sonnet
maxTurns: 20
tools: Bash, Read, Glob, Grep
---
Search the knowledge vault for entries matching the query.
Break complex queries into multiple search terms and
synthesise findings from multiple entries.
The tools field lists the only tools the agent may use. This agent gets Bash, Read, Glob and Grep, and no Edit, Write or Agent tool, so it cannot start agents of its own. Bash can still change files: drop it, or block writes with a hook or a deny rule in settings. You decide how much it can break. Save the file in .claude/agents/ (one project), ~/.claude/agents/ (all your projects) or a plugin’s agents/ folder.
The most common agent mistake is under-briefing. An agent sees only the task you send, not the conversation so far. The exception is a fork, an agent that starts with a copy of the current conversation. Custom agents load your CLAUDE.md files but not your auto memory (the notes Claude keeps for itself). The built-in Explore and Plan agents start without CLAUDE.md. Brief every agent like a colleague who knows the project but just walked in:
- Bad: “Fix the failing test.”
- Good: “The test in
test/models/user_test.rb:45fails withNoMethodError: undefined method 'full_name'. TheUsermodel atapp/models/user.rbwas refactored in the last commit to renamefull_nametodisplay_name. Update the test to use the new method name and verify it passes.”
Agents Claude starts run in the background by default, so the conversation carries on while they work. A custom agent can set background: true in its frontmatter to always run in the background. They can also work in isolated git worktrees. A worktree is a separate copy of your code, so an agent’s changes stay out of your own copy until you merge them. That is how I run parallel jobs: three agents in worktrees, each on its own branch, and I review the results when they finish.
Check: the agent’s report names the files it read and what it found, and your main conversation holds only that summary.
6. Bundle what works into a plugin
Once a set of skills, hooks, agents and commands works together, a plugin packages it as one installable unit. Its plugin.json file gives the plugin a name, a version and a description, and the plugin folder holds a subfolder for each kind: skills/, agents/, commands/, hooks/. Most pieces work as they would on their own. A plugin agent ignores some frontmatter fields (hooks, mcpServers, permissionMode), so put its hooks and MCP servers in the plugin’s own configuration. The plugin adds packaging and a name prefix: a plugin skill runs as /memory-vault:knowledge-retrieval, so two plugins cannot clash on a name.
A plugin can be a local folder or come from a marketplace, a git repository with a marketplace.json list. When a skill moves from ~/.claude/skills/ into a plugin, delete the loose copy. Otherwise both load, as /name and /plugin-name:name, and Claude may pick either.
Check: open a second project. A plugin installed at user scope is already enabled there, and /plugin-name:skill-name runs the same skill.
Pitfalls
| Symptom | Cause | Fix |
|---|---|---|
| Claude ignores a “NEVER” rule in CLAUDE.md | An instruction is a suggestion | Write a PreToolUse hook that exits with code 2 |
| Every session starts slow and costly | Domain knowledge sits in CLAUDE.md | Move it into a skill with references/ |
| A skill never loads | Its description does not match how you ask |
Rewrite the description with the phrases you use |
| An agent’s result misses the point | It got a one-line task and none of your context | Brief it with file paths, the error and the goal |
A PreToolUse hook blocks nothing |
The script exits 0 or 1, or the matcher names the wrong tool | Exit 2 to block (exit 1 only warns unless the hook sets "onFailure": "block"); match the tool name |
| The same skill loads twice | A loose copy and a plugin copy both load, as /name and /plugin-name:name |
Delete the loose copy |
In practice
My setup gives each tool one job:
CLAUDE.mdfor universal rules, under 200 lines- Skills for domain expertise, loaded on demand
- Hooks for automated quality gates, scripted checks where possible and model checks where useful
- Agents for delegated subtasks, in an isolated context
- Commands for personal shortcuts
Two of the examples above are my own open-source work: the /transcribe skill and the hamster plugin. My memory vault plugin combines all four. A Stop hook runs a Python script that nudges me to save knowledge at the end of a session. Three agents handle deep search, saving a session’s findings and periodic clean-up. Eight commands (recall, remember, related, browse, curate, gaps, graph, status) are the shortcuts, and one skill supplies the retrieval context. Together they give me a knowledge store that works across every session.
Agents I build for Applied AI clients run with limited permissions, and their hard limits sit in code the model cannot skip.
Related reading
- How to write a CLAUDE.md file for Claude Code, the rules a hook should replace
- 10 ways to reduce Claude Code token usage, how skills and agents reduce token costs
- How I use Claude Code to manage six client projects, the full workflow these primitives support
- Claude Code hook examples: turning mistakes into tested rules, hooks and skills packaged as an open-source plugin
- Local audio transcription with speaker labels in Claude Code, a skill that wraps a local tool
- Claude Code for the full stack: marketing, DevOps, and beyond code, skills and hooks for marketing and DevOps work
Common questions
What is the difference between skills, hooks, agents and commands in Claude Code?
Skills are reusable expertise Claude loads when it is relevant. Hooks are automatic checks that Claude Code itself runs when something happens, such as before a command or when a helper finishes. Agents are separate Claude helpers that do a scoped job. Commands are shortcuts you trigger by typing a slash and a name. Commands and skills add to what Claude knows; hooks and agents add to what Claude does on its own.
When should you use a hook instead of a skill?
Use a hook when something must happen every time, because Claude Code runs hooks and Claude cannot skip them. Use a skill when you want expertise available for Claude to apply when it is relevant.
How do you use hooks in Claude Code?
Add them to Claude Code’s settings.json under an event name, such as PreToolUse (before Claude uses a tool) or SubagentStop (when a helper agent finishes). Each entry takes an optional matcher, which filters the event (a tool name for tool events, an agent type for SubagentStop), and a shell command to run. A PreToolUse hook on Bash (Claude’s shell tool) checks each command before it runs. Exit code 2 blocks it and sends the error back to Claude; exit code 0 raises no objection, so the normal permission rules decide.
In what order should you learn these four?
In my experience, most people discover them in the wrong order and over-build. I start with commands and skills for everyday work, add hooks when a rule must be enforced automatically, and reach for agents when a job needs its own helper. Plugins bundle the set once it settles.