If you have been using Claude Code for a while you have definitely run into three terms: skill, subagent and hook. All three live under “customizing Claude Code” and at first glance they look like they do the same job.
They don’t. Each solves a different problem, and picking the wrong one means either needless complexity or a setup that never fires 🛠️
The Short Answer
- Skill: when you keep writing the same instructions over and over
- Subagent: when a job produces so much output it floods your main conversation
- Hook: when you need a rule to hold without exception
Slash Commands Are Skills Now
The biggest change first, because most older guides miss it: custom slash commands have been merged into skills.
A file at .claude/commands/deploy.md and a folder at .claude/skills/deploy/SKILL.md do the same thing, both create /deploy. Your old files keep working, nothing broke.
What skills add on top:
- A folder for supporting files (templates, scripts, reference material)
- Control over who can invoke the command
- The ability for Claude to load it on its own when relevant
If both share a name, the skill wins.
What Is a Skill and How Do You Write One?
A skill is a folder containing a SKILL.md file. You write instructions, Claude adds it to its toolkit.
When should you write one? When you keep pasting the same instructions, checklist or multistep procedure into chat. Or when a section of your CLAUDE.md has stopped being a fact and become a procedure.
CLAUDE.md loads into context in full, on every session. A skill’s body loads only when it is used.
So a long reference document as a skill costs you almost nothing until you need it. The same text in CLAUDE.md costs you on every single launch.
Where Does the File Go?
| Location | Path | Who gets it |
|---|---|---|
| Personal | ~/.claude/skills/<name>/SKILL.md | All your projects |
| Project | .claude/skills/<name>/SKILL.md | That project, shared with your team |
The command name comes from the folder name. A file at .claude/skills/deploy-staging/SKILL.md creates /deploy-staging.
Check project skills into version control so your team can use and improve them.
An Example Skill
---
name: deploy
description: Deploy the application to production
disable-model-invocation: true
---
Deploy $ARGUMENTS to production:
1. Run the test suite
2. Build the application
3. Push to the deployment target
4. Verify the deployment succeeded
The fields worth knowing:
| Field | What it does |
|---|---|
description | What the skill does. Claude decides when to use it from this, the most important field |
disable-model-invocation | true stops Claude running it on its own, only you can invoke |
user-invocable | false hides it from the menu, only Claude uses it |
allowed-tools | Tools usable without a permission prompt while the skill runs |
model | Model to use while the skill is active |
paths | Only activate the skill when working with matching files |
Claude decides when to use a skill purely from the description field. Write it vaguely and it either never triggers or triggers in the wrong places.
Put the main use case first, and skip long lists.
Passing Arguments
The $ARGUMENTS placeholder is replaced with whatever follows the skill name:
---
name: fix-issue
description: Fix a GitHub issue
disable-model-invocation: true
---
Fix GitHub issue $ARGUMENTS following our coding standards.
Running /fix-issue 123 gives Claude “Fix GitHub issue 123…”.
Stop Claude Running It on Its Own
By default both you and Claude can invoke any skill. That is not always what you want.
Setting disable-model-invocation: true means only you can invoke it. Essential for anything with side effects: /deploy, /commit, /send-slack-message. You do not want Claude deciding to deploy because your code looks ready.
The reverse exists too: user-invocable: false hides the skill from the menu so only Claude uses it. That makes sense for background knowledge, like a note explaining how a legacy system works, since invoking it as a command would be meaningless.
What Is a Subagent and When Do You Use One?
A subagent is a separate helper running in its own context window, with its own system prompt and its own tool permissions.
What is it for? When a side task would fill your main conversation with search results, logs or file contents you will never look at again, the subagent does that work in its own context and returns only the summary.
What you get:
- Preserved context: the noise stays out of your main conversation
- Enforced limits: you decide which tools it can use
- Lower cost: route the job to a fast, cheap model like Haiku
An Example Subagent
The file goes in .claude/agents/:
---
name: code-reviewer
description: Reviews code for quality and best practices
tools: Read, Glob, Grep
model: sonnet
---
You are a code reviewer. When invoked, analyze the code and give
concrete feedback on quality, security and best practices.
The frontmatter is configuration, the body becomes the system prompt.
A subagent gets only its own system prompt plus basic details like the working directory. It sees nothing of what you discussed in the main conversation.
So the brief you give it has to stand alone. “Review the file I mentioned earlier” will not work.
Subagent or Main Conversation?
Stay in the main conversation when:
- The work needs frequent back-and-forth or iterative refinement
- Planning, implementation and testing share the same context
- You are making a quick, small change
Open a subagent when:
- The job produces long output you will never revisit
- You want to restrict tool access
- The work is self-contained and can return a summary
What Is a Hook and What Is It For?
Hooks are scripts that run when something happens in Claude Code. The distinction that matters: prompts depend on the model’s interpretation, hooks run code and cannot hallucinate.
If you need a rule to hold without exception, do not write it in a prompt, write a hook.
The events you will use most:
| Event | When it runs | Can it block? |
|---|---|---|
PreToolUse | Before a tool runs | Yes |
PostToolUse | After a tool has run | No, it already happened |
UserPromptSubmit | When you send a message | Yes |
SessionStart | When a session begins | No |
Stop | When Claude finishes responding | Yes, it can keep it going |
Example: Blocking a Destructive Command
Hooks are defined in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh"
}
]
}
]
}
}
And the script itself:
#!/bin/bash
COMMAND=$(jq -r '.tool_input.command')
if echo "$COMMAND" | grep -q 'rm -rf'; then
echo "Destructive command blocked" >&2
exit 2
fi
exit 0
The rule is simple: exit code 2 means block. Whatever you write to stderr goes back to Claude as an error message. Exit code 0 and everything carries on as normal.
You cannot block anything with PostToolUse, because the tool has already run. There you can only show a warning.
Anything you want to stop needs PreToolUse.
Which One When?
| What you need | Use | Why |
|---|---|---|
| I keep pasting the same prompt | Skill | Move it to a file, invoke with /name |
| The procedure ships with templates or scripts | Skill | Everything lives in one folder |
| Long reference material I rarely need | Skill | Loads only when invoked |
| The job floods my conversation with output | Subagent | Own context, returns a summary |
| I want to route work to a cheaper model | Subagent | model: haiku cuts cost |
| A rule must hold without exception | Hook | Code runs, not model judgment |
| Tests must run before every commit | Hook | PreToolUse can block |
| Claude should not do this on its own | Skill + disable-model-invocation | Only you can invoke it |
Common Mistakes
1. Dumping everything into CLAUDE.md. It loads in full every session. Once a section turns into a procedure, move it to a skill.
2. Leaving side-effecting commands unguarded. Without disable-model-invocation: true, Claude may run your /deploy skill whenever it judges the moment right.
3. Assuming the subagent knows the conversation. It does not. Write a complete brief.
4. Trying to block with PostToolUse. The tool already ran. You need PreToolUse.
5. Opening a subagent for everything. Work that needs back-and-forth finishes faster in the main conversation, since a subagent starts from zero.
Frequently Asked Questions
What is a Claude Code skill? A skill is a folder containing a SKILL.md file. You write your instructions inside and Claude adds it to its toolkit. Unlike CLAUDE.md content, a skill’s body loads into context only when used, which makes long reference material almost free.
Were Claude Code slash commands removed? They were not removed, they were merged into skills. A file at .claude/commands/deploy.md and a folder at .claude/skills/deploy/SKILL.md both create /deploy. Your old files keep working, and if both share a name the skill takes precedence.
What is the difference between a skill and a subagent? A skill runs in the main conversation’s context and adds instructions there. A subagent runs in its own context window with its own system prompt and tool permissions, returning only a summary when done. Use a subagent for jobs with long output and a skill for repeated procedures.
What is a Claude Code hook? A hook is a script that runs when a specific event happens in Claude Code. Unlike prompts it runs code and cannot hallucinate, which makes it the right tool for rules that must hold without exception. Hooks are defined in .claude/settings.json.
How does a hook block a command? The script returning exit code 2 means block, and whatever it writes to stderr goes back to Claude as an error message. Blocking only works on events that run before the action, like PreToolUse. PostToolUse cannot block because the tool has already run.
How do I stop Claude from running a skill on its own? Add disable-model-invocation: true to the frontmatter of your SKILL.md. Only you can then invoke the skill by command. It is recommended for anything with side effects, such as deploying, committing or sending messages.
Where do Claude Code skill files go? Personal skills go in ~/.claude/skills/<name>/SKILL.md and project skills in .claude/skills/<name>/SKILL.md. The command name comes from the folder name, so a deploy-staging folder creates /deploy-staging.
Can a subagent see the main conversation? No. A subagent receives only its own system prompt and basic details like the working directory, and sees nothing of what was discussed in the main conversation. The brief you give it has to make sense on its own.
Summary
The three are not alternatives, they complement each other:
- Skills move repeated procedures into files and cut context cost
- Subagents take noisy work out of the main conversation and restrict tool access
- Hooks enforce rules in code rather than leaving them to the model
The most common working combination: make the procedure a skill, push the heavy exploration into a subagent, and guard the irreversible steps with a hook.
For the thinking behavior of the models underneath Claude Code see the extended thinking versus adaptive thinking post, and for adding tools see the MCP guide 👀
