← All posts Claude Code · Part 5 of 7

Automating Your Workflow: Skills, Slash Commands, and Hooks

By the time a team has used an agentic coding tool in earnest for a few weeks, a pattern emerges: the same instructions get typed again and again. "Review this change for security issues." "Generate release notes from the commits since the last tag." "Run the formatter and the tests before you tell me you're done." Each of these is a small ritual, and rituals are what we should be automating. Whether to automate them is the easy part. The harder question is at which layer, because the layers behave very differently and reaching for the wrong one is the most common mistake I see.

Claude Code gives you three distinct mechanisms for turning repeatable work into repeatable automation: slash commands, skills, and hooks. They look similar on the surface (all three are ways of "telling the agent to do a thing routinely"), but they sit at different points on a spectrum that matters a great deal in practice: the spectrum from guidance the model may follow to guarantees the harness enforces.

This article is about that spectrum, and about building the instinct for which tool to reach for. It follows directly from the two earlier pieces on context engineering and on permissions, because automation does not exist in a vacuum: it amplifies whatever foundation you have already laid.

Three layers, one spectrum

Start with what each mechanism actually is, because the names invite confusion.

A slash command is a saved prompt. Nothing more exotic than that: a markdown file containing a prompt you found yourself writing repeatedly, parked in a known location so you can invoke it by name with /my-command. When you call it, its text is injected into the conversation as if you had typed it. It is convenience at the prompt layer, a macro for your own keystrokes, and it remains entirely model-driven. The model reads the prompt and decides how to act on it, exactly as it would with anything you type by hand.

A skill is a reusable, model-invoked capability. It packages a set of instructions, optionally with supporting resources such as scripts, reference files, or templates, behind a name and a description. The important word is model-invoked: you do not call a skill explicitly. The agent reads the available descriptions and, when a task in the conversation matches, decides to load and apply that skill. Skills are the right home for domain expertise you want applied consistently and in context: the knowledge a senior engineer would bring to a particular kind of task, applied without you having to remember to paste it in every time.

A hook is something else entirely. A hook is a deterministic shell command that the harness runs in response to an event in the agent's lifecycle: before a tool call, after a tool call, when the agent stops, and so on. Everything else in this article hangs on one distinction. The harness executes hooks, not the model. A hook does not ask the model nicely; it runs, full stop, and its exit status can block, allow, or feed information back into the loop. Hooks are how you turn "please remember to run the tests" into a fact of the environment rather than a hope.

Slash commands and skills are guidance the model may choose to follow. Hooks are guarantees the harness will always enforce. If the difference between "usually" and "always" matters for a given behaviour (and for anything safety- or correctness-critical, it always does), then you want a hook.

A decision rule worth memorising

Faced with a repeatable behaviour, ask one question first: do I need a guarantee, or do I need a convenience? The answer routes you almost immediately.

  • Need a guarantee? Reach for a hook. Formatting that must happen on every edit, a test suite that must pass before the agent claims success, a gate that must reject commits touching a forbidden path. These are non-negotiable outcomes, and only the harness-enforced layer can deliver them. The model cannot "forget" a hook because the model is not the one running it.
  • Need a repeatable prompt? Reach for a slash command. A standardised review you run by hand, a release-notes routine you trigger at the end of a sprint, a scaffolding prompt you use whenever you start a new module. You decide when it fires, and the model carries it out.
  • Need reusable expertise the model should apply when relevant? Reach for a skill. The house style for API error handling, the way your organisation writes migration scripts, the checklist a reviewer applies to a public-facing endpoint. You want the agent to recognise the situation and bring the right know-how unprompted.

The failure mode I most often correct is people encoding a guarantee as guidance: writing "always run the linter before finishing" into a project instruction file and being surprised when, one run in twenty, it does not happen. Instructions are read by a probabilistic model; nineteen-in-twenty compliance is roughly what you should expect from guidance. If the twentieth case is a production incident, you did not need an instruction. You needed a hook.

Worked example: a slash command for consistent review

Consider code review. Most teams have an implicit checklist, and most of the time it lives in someone's head, which means the review you get depends on who is reviewing and how tired they are. A slash command makes the checklist explicit and repeatable. It is a markdown file dropped into the commands directory (typically .claude/commands/ for a project), and the file name becomes the command name.

# .claude/commands/review.md

Review the current staged diff with the rigour of a senior engineer.
Work through these areas in order and report findings grouped by severity:

1. Correctness & edge cases — off-by-one, null/empty inputs,
   error paths that are swallowed or mishandled.
2. Security — injection, missing authz checks, secrets in code,
   unvalidated input crossing a trust boundary.
3. Concurrency — shared mutable state, race conditions, ordering
   assumptions that the code does not actually guarantee.
4. Tests — is the new behaviour covered? Are the tests meaningful
   or merely present?
5. Clarity — names, dead code, comments that explain "why" vs "what".

For each finding give: file & line, the problem, and a concrete fix.
Do not approve work that has no tests for new behaviour.
End with a one-line verdict: APPROVE, APPROVE WITH NITS, or REQUEST CHANGES.

Now /review produces the same disciplined pass every time, regardless of who runs it. Note what this is and is not. It is a reliable way to ask for a thorough review in your house style. It is not a guarantee that nothing unreviewed reaches the main branch. The model still decides how literally to apply each point, and a human still decides whether to act on the verdict. I think that is right: a review is a judgement, and judgement is the kind of work you want a capable model doing rather than a rigid script.

Worked example: a hook that actually enforces quality

Now contrast that with a behaviour you genuinely cannot leave to chance: formatting and tests after the agent edits code. You do not want this requested; you want it enforced, and you want a failing test to block the agent from sailing on as if everything were fine. That is hook territory, specifically a post-edit hook using the PostToolUse event, which fires after a tool call completes.

Hooks are configured in settings.json. The shape below is illustrative rather than something to paste in as is. Treat the matcher and command as the parts you adapt to your stack:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "sh -c 'set -e; npm run format --silent && npm test --silent'"
          }
        ]
      }
    ]
  }
}

The mechanics are what make this work. After any Edit or Write, the harness runs the command. Because of set -e, the moment the formatter or the test run exits non-zero, the whole command fails. A non-zero exit from a hook is a signal the harness surfaces back into the agent's loop rather than silently discarding. The agent is told, deterministically, that its last edit left the tree in a failing state, and it cannot truthfully report success while the gate is red. There is no version of this run, however the conversation drifts, where the tests are quietly skipped, because skipping them was never an option the model had.

The same pattern generalises to anything that must be true rather than merely hoped for: a PreToolUse hook that inspects a proposed command and blocks edits to a generated file or a vendored directory; a Stop hook that runs a final verification before the agent is permitted to conclude the session. The events differ; the principle does not. If correctness depends on it, encode it where the model cannot route around it.

Composing the layers

These mechanisms work best stacked. A mature setup tends to look like this. A skill carries the durable expertise (how your team writes a database migration, complete with the conventions and the gotchas), and the agent applies it whenever a migration is in scope, no prompting required. A slash command sits on top for the rituals you trigger deliberately, such as /review or /release-notes, giving you a consistent, named way to invoke a complex prompt. And underneath both, hooks form the safety net: whatever the skill advised and whatever the command requested, the post-edit hook still runs the tests, and the pre-commit gate still refuses to let a forbidden change through.

Read top to bottom, that is a gradient from soft to hard. Skills shape how the agent works when it recognises a context. Commands let you summon a behaviour on demand. Hooks guarantee the floor beneath everything. A team that uses all three deliberately gets the model bringing the right expertise unasked, one-word rituals for the things it does often, and the boring, critical checks happening every time without anyone having to remember them.

Automation amplifies your guardrails

There is a sting in the tail, and it ties this piece back to the earlier articles on context and on permissions. Automation does not operate on a blank slate. It amplifies whatever foundation you have already built, and it amplifies bad foundations just as faithfully as good ones.

A skill is only as sound as the expertise written into it; encode a sloppy convention and the agent will now apply that sloppiness consistently, which is worse than applying it occasionally. A hook that runs an arbitrary shell command is running with whatever reach you have granted; a thoughtless command in a hook is a thoughtless command executed automatically on every matching event, which is precisely why the permission model from the earlier article is not an optional companion to automation but a prerequisite for it. And a slash command that injects a confused or contradictory prompt will produce confused work faster and more repeatably than you ever could by hand.

This is the point that runs through the whole series: automation is neutral. The same machinery that makes a well-governed agent reliably excellent makes a poorly-governed one reliably dangerous. Good context gives the automation something true to act on. Good permissions give it a safe boundary to act within. Only once those are in place does layering commands, skills, and hooks on top start paying off, with repeatable work turned into automation and the guarantees sitting in the one layer that can actually keep them.

Get the layering right and you stop supervising the agent turn by turn. It brings its own expertise, you invoke rituals by name, and the enforced checks hold however the conversation goes. The next article builds on that, when a single well-automated agent becomes several working together.

Next: Orchestration & Subagents →