Skip to content

Claude Code skills, hooks, and subagents: what each is for

By SunnyKumar Jonwal 10 min read

Claude Code out of the box is already useful. It gets much better once you shape it to your project, and the tool offers several ways to do that. The trouble is that they overlap: you could encode "always format files after editing" in a CLAUDE.md line, a skill, a hook, or a subagent prompt, and only one of those actually guarantees it.

This post sorts out what each mechanism is, what it's good at, and how to pick. The short version is that they differ in when they load and who decides to use them, and those two questions settle most choices.

The map

Here's the landscape at a glance.

Mechanism What it is Who triggers it Loaded when
CLAUDE.md Standing instructions Always in force Every session
Slash command A saved prompt You, by typing it When invoked
Skill Instructions and files for a task The model, when relevant Description always; body on demand
Hook A shell command on an event The harness, automatically Configured; runs deterministically
Subagent A helper with its own context The model delegates When spawned
MCP server External tools and data The model calls tools Tool definitions always

A helpful way to remember it: instructions and skills advise, hooks enforce, subagents isolate, and MCP servers connect.

CLAUDE.md: the briefing

CLAUDE.md is loaded into context at the start of every session and holds knowledge that applies most of the time: build commands, conventions, gotchas. It's the right home for anything the agent needs on nearly every task. Its weakness is that everything in it costs tokens every time, and that instructions are advisory. The model usually follows them and sometimes doesn't. There's a full guide to writing a good CLAUDE.md.

Slash commands: saved prompts

A custom slash command is a Markdown file containing a prompt, stored in a commands folder inside .claude/, that you run by typing its name. It's the simplest extension: a shortcut for a prompt you use often, such as "review the staged changes for security issues" or "write a changelog entry for the last commit." Commands can take arguments. You trigger them, so they cost nothing until used, and they don't run on their own.

Use one when you catch yourself pasting the same paragraph repeatedly.

Skills: know-how loaded on demand

A skill is a folder containing a SKILL.md file with a short name and description in its frontmatter, plus instructions, and optionally scripts, templates, and reference files. The clever part is how it loads. Claude sees only each skill's name and description up front, which is cheap. When a task matches a description, it loads the full instructions, and pulls in supporting files only as needed. This is often called progressive disclosure, and it lets you have dozens of skills without bloating every session.

Here's a small example for release notes:

---
name: release-notes
description: Write release notes for a new version. Use when the user asks for a changelog, release notes, or a summary of changes since the last tag.
---

# Writing release notes

1. Find the previous tag with `git describe --tags --abbrev=0`.
2. List commits since then with `git log <tag>..HEAD --oneline`.
3. Group changes under Added, Changed, Fixed, and Security.
4. Write one plain-English line per change, aimed at users, not developers.
5. Leave out merge commits and dependency bumps unless they affect users.

See `template.md` in this folder for the exact format.

The description is the trigger, so write it like a job posting for the skill: what it does and when to reach for it. A vague description means it fires at the wrong times or never. Keep the body focused on procedure and judgment, and put long reference material in separate files the skill points to.

Skills are best for repeatable know-how that isn't needed in every session: your release process, how to add a new API endpoint in your framework, a data-cleaning routine, your house style for documentation. Personal skills live in your home directory, project skills live in the repository so the team shares them, and plugins can bundle them.

Hooks: guarantees, not suggestions

A hook is a shell command that Claude Code runs automatically at a defined point in its lifecycle: before a tool call, after one, when you submit a prompt, when the agent finishes, and so on. Because the harness runs it, not the model, it happens every single time. That's the crucial difference from instructions.

Say you want files formatted after every edit. Telling the agent to do it is a request. A hook makes it a fact. A config for that might look like this in your settings file:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs ./vendor/bin/pint"
          }
        ]
      }
    ]
  }
}

The command receives details about the event as JSON on standard input, which is why the example uses jq to pull out the file path. Check the current documentation for the exact event names and payload shape, since they evolve.

Good uses for hooks include running a formatter or linter after edits, blocking edits to protected files or dangerous commands before they run, logging every tool call for audit, sending a notification when a long task finishes, and injecting fresh context, such as the current branch or ticket, when a prompt is submitted.

Two cautions. Hooks run with your permissions, so a careless one can do real damage, and a hook copied from an untrusted source is code execution. And slow hooks slow the whole loop, so keep them fast and quiet. Where a hook blocks an action, have it print a clear reason, because the agent reads that message and adjusts.

Hooks also pair well with security controls: a pre-tool hook that denies writes outside the project folder is a guardrail that doesn't depend on the model behaving.

Subagents: helpers with their own context

A subagent is a specialized assistant defined in a Markdown file with frontmatter, kept in an agents folder in .claude/. It has its own instructions, and optionally its own restricted set of tools. The main agent can delegate a task to it, and it does the work in a separate context window and returns a summary.

---
name: code-reviewer
description: Reviews diffs for bugs, missing tests, and security problems. Use after making changes and before committing.
tools: Read, Grep, Glob, Bash
---

You are a careful code reviewer. Review the current git diff.
Report only real problems, ordered by severity, each with the file, line,
and a concrete suggested fix. Check for missing tests, unhandled errors,
and anything touching auth or input handling. Do not edit files.

Why bother? Three reasons. First, context isolation: a subagent can read fifty files while researching and give you back three sentences, so your main window stays clean. Second, specialization: a reviewer with strict instructions gives more consistent reviews than the general agent improvising. Third, restricted tools: the reviewer above can't edit anything, which is a useful guarantee.

There are built-in subagents for tasks like exploring a codebase and planning, and you can add your own. The cost is extra tokens, since each subagent runs its own loop, and less shared context, so the brief you give it has to be complete. It doesn't see your conversation, only what you pass along. The tradeoffs are covered more broadly in multi-agent systems.

MCP servers: new capabilities

MCP servers give the agent new tools and data sources: your issue tracker, a database, a browser, a design tool. They're the right answer when the agent needs to reach something it can't already. Each connected server adds its tool definitions to the context, so be selective. MCP explained covers how they work, and building an MCP server shows how to write one.

The line between a skill and an MCP server is worth keeping clear. A skill teaches the agent how to do something with capabilities it has. An MCP server gives it access to something new. A "deploy checklist" skill might tell the agent to call your deployment server's tools in the right order.

Plugins: sharing the bundle

Once you've built a useful set of commands, skills, subagents, and hooks, you'll want to share it with teammates or reuse it across projects. Plugins package these pieces so they can be installed together, and marketplaces collect them. It's a distribution mechanism, not a new kind of behavior. Start without one, and package things up when there's something worth sharing.

Choosing: a short decision guide

Work through these questions.

Does it need to happen every time, without exception? Use a hook. Formatting, blocking dangerous edits, and audit logs belong here.

Is it knowledge the agent needs in nearly every session? Put it in CLAUDE.md.

Is it a procedure needed occasionally, with steps, scripts, or templates? Make a skill, so it stays out of context until it matters.

Is it a prompt you like to trigger yourself? Save a slash command.

Is it noisy exploration or a task that benefits from a separate perspective or restricted tools? Use a subagent.

Does the agent need to touch a system it can't reach now? Connect an MCP server.

Is it a real security boundary? Use permissions and sandboxing, and don't lean on any prompt-level mechanism.

Putting them together

A realistic setup for a web project might look like this. CLAUDE.md has the commands and three key conventions. A pint hook formats PHP files after each edit. A release-notes skill handles changelogs. A code-reviewer subagent, with read-only tools, reviews diffs before commits. An MCP server connects the issue tracker. A /fix-issue slash command takes a ticket number and kicks off the whole flow: read the ticket, plan, implement, test, review.

Each piece does one job, and none duplicates another. That's the point of knowing the differences.

When something doesn't fire

Most confusion comes from expecting a mechanism to behave like a different one, so debug by type.

If a skill never triggers, read its description as if you were the model choosing among twenty. Does it say when to use it in the words a user would actually type? Descriptions that talk about the skill's internals instead of the situations it serves rarely match. Try asking for the task in plain language and see whether it loads, then adjust the wording.

If a hook doesn't run, check the event name, the matcher, and that the command works when you run it by hand with sample input piped in. A hook that errors silently is easy to mistake for one that never ran, so log to a file while you're developing it.

If a subagent never gets chosen, its description probably doesn't say when to use it. You can also name it directly in your request and see whether it behaves as designed. If it does, the trouble is in how the description advertises it.

And if the agent ignores a line in CLAUDE.md, remember that's the one mechanism on the list that can be ignored. When the behavior is mandatory, promote it to a hook or a permission rule.

Pitfalls to avoid

Skills with overlapping descriptions compete, and the model may pick the wrong one. Give each a distinct trigger. Too many always-loaded items, whether long CLAUDE.md files or many MCP servers, crowd the context; trim what doesn't earn its keep. Hooks that surprise people cause confusion, so document them in the repository. Subagents given thin briefs return thin results, so write the delegation like a proper task description. And when something isn't working, check which mechanism you're relying on: an instruction the model ignored needs a hook if the behavior is mandatory.

The workflow guide shows how these fit into day-to-day use. Start with the smallest thing that solves your problem, and add mechanisms as real needs appear.