Skip to main content

Hooks system: PreToolUse / PostToolUse / Stop in Claude Code

Cluster 14 of the Claude Code Workflow (Platform). The three hook types of Claude Code (PreToolUse, PostToolUse, Stop) allow intercepting tool calls and session events. When writing them, when they are overhead, real examples from the portfolio.

What do you think about this article?

Hooks system: PreToolUse / PostToolUse / Stop in Claude Code
Claude Code WorkflowPhotos:PixabayPexels

At a Glance

Cluster 14 of the Claude Code Workflow (Platform). The three hook types of Claude Code (PreToolUse, PostToolUse, Stop) allow intercepting tool calls and session events. When writing them, when they are overhead, real examples from the portfolio.

Direct answer

Hooks are useful for enforcing strict constraints or auditing purposes, but can be excessive when simple instructions in a configuration file suffice. They should be fast and not block the workflow, ideally executing heavy operations out-of-band or only on Stop events to avoid friction and maintain productivity.

The hooks by Claude Code allow intercepting tool invocations and session events with custom shell commands. They are powerful and overkill at the same time: useful when you have enforceable constraints, excessive when just instructions in CLAUDE.md are sufficient. This article explains when to use the candle.

Independence Disclaimer: I am not affiliated with Anthropic. I use 2 hooks active on the portfolio: one for auto-formatting posts after Edit/Write, one for audit logs on Stop. That's all.

The three types of hooks

  • Pre-Tool Use - executed BEFORE Claude Code invokes a tool. Receives in stdin payload with tool name and parameters. It can: Modify parameters (replacement output), block execution (non-zero exit code), Permit it (exit code zero).
  • I don't see any Italian text provided. Please provide the Italian text you'd like me to translate, and I'll be happy to assist you. Executed AFTER invocation of a tool successful. Receives payload with tool name and result. Typically used for auto-formatting, audit logs, notifications.
  • Stop Executed when the session ends (Claude Code CLI quit). Used for session summary, usage tracking, temporary cleanup.

Configuration

I don't see any Italian text to translate. Please provide the text you'd like me to translate, and I'll be happy to assist you. ~/.claude/settings.json Hello .claude/settings.json A project. Illustrative example:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "/usr/local/bin/auto-format-hook.sh"
      }
    ],
    "Stop": [
      {
        "command": "/usr/local/bin/session-summary.sh"
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "/usr/local/bin/bash-safety-check.sh"
      }
    ]
  }
}

matcher It's a regular expression on the tool name (it was called "all tools"). command Is this a path executable by a script or binary?

Example: auto-format hook

One of the two hooks I use. The script received via stdin with a JSON payload and file path updated just now:

#!/usr/bin/env bash
# /usr/local/bin/auto-format-hook.sh
# Esegue prettier su file .ts / .tsx / .json appena modificati

PAYLOAD=$(cat)
FILE=$(echo "$PAYLOAD" | jq -r '.params.file_path // empty')

if [ -z "$FILE" ]; then exit 0; fi
case "$FILE" in
  *.ts|*.tsx|*.json) npx --no-install prettier --write "$FILE" 2>/dev/null ;;
  *) exit 0 ;;
esac

Effect: every Edit / Write on the TS/JSON file is automatically formatted. Saved commit "chore: format" clean and diff accumulated.

Example: Bash Safety Check

Hook PreToolUse on Bash to block destructive commands without explicit confirmation: Note: I've kept the token XPHX0XPHX and XPHX1XPHX exactly as-is, as per your request.

#!/usr/bin/env bash
# /usr/local/bin/bash-safety-check.sh
# Blocca rm -rf / e simili senza confirm utente in shell

PAYLOAD=$(cat)
CMD=$(echo "$PAYLOAD" | jq -r '.params.command')

if echo "$CMD" | grep -qE 'rm\s+-rf\s+/|drop\s+database|truncate\s+table'; then
  echo "BLOCKED: destructive command detected. Require explicit user confirm in chat." >&2
  exit 1
fi
exit 0

Output does not block execution of the tool. The message in stderr arrives at the model, which reacts (typically asks for user confirmation before repeating).

Example: Stop hook for usage tracking

Standard Hook Stop tip: adds a line to personal log usage.

#!/usr/bin/env bash
# /usr/local/bin/session-summary.sh

LOG="$HOME/.claude/usage-log.csv"
DATE=$(date -u +%Y-%m-%dT%H:%M:%SZ)
SESSION_ID="${CLAUDE_SESSION_ID:-unknown}"

echo "$DATE,$SESSION_ID,session_end" >> "$LOG"

Effect: log usage for personal audits. Use if you want to track actual time of Claude Code's activity on project.

When NOT writing a hook

  1. When an education CLAUDE.md is enough. If the rule is "do not modify / below without confirmation", instruction in CLAUDE.md is more straightforward than a PreToolUse hook that blocks it.
  2. When the bond is soft. Hooks are strict enforcement. For rule indications ("preferably use async/await"), a comment in CLAUDE.md or a code-reviewer skill is enough.
  3. When the setup costs more than its value. Hooks require a working script + debugging + maintenance. For one-shot rules, it's not worth it.

Overly invasive hook pattern

A PostToolUse hook that executes the full test suite after every Edit is a zealous enforcer that paralyzes the workflow. An Edit requires 30s → with hook test suite 2 minutes = every small change costs 2.5 minutes.

Mitigation: hooks must be fast (under 5s). If heavy operations are needed, execute them out-of-band (background job) or only on Stop, not PostToolUse.

Debugging hook remains unchanged as it is a technical term.

The hook turns around quietly in a subprocess. The output of stderr arrives at the model (and to you if you have verbose mode). For debugging:

  1. Add set -x At the top of the script for tracing.
  2. Redirect standard error to a dedicated file (2>> ~/.claude/hook-debug.log).
  3. Test the script manually with a fake payload via stdin.

What I learned writing this article

Hooks are like decorators in Python: useful in few cases, strategic, but harmful if used everywhere. On my portfolio I use 2 hooks (auto-format + Stop log) and that's the right number. Tried with 5+ and had continuous friction.

According to insight: the majority of rules that I thought required hook resolve better in CLAUDE.md instructions + code-reviewer skill. Hook remain for the few cases of "this MUST never happen regardless of context".

Avoided pattern

  • Hook as substitute for CLAUDE.md instructionsSoft instructions go to CLAUDE.md. True hook for enforcement.
  • Post-Tool Use with Heavy OperationAuto-format is OK, full test suite has failed.

A story worth sharing

Placeholder Section If you have interesting custom hooks, write to me federico@federicocalo.dev.


Previous cluster: MCP server development Next cluster: IDE/web/CLI Claude Code environments →

Frequently asked questions

What are the hooks by Claude Code used for?

The hooks allow intercepting tool invocations and session events with custom shell commands.

How do I know if a hook is necessary or excessive in my workflow?

A hook is useful when you have enforceable constraints, but excessive when just instructions in CLAUDE.md are sufficient.

What happens to the output of stderr from a hook?

The output of stderr arrives at the model (and to you if you have verbose mode).

How can I avoid overusing hooks and causing friction in my workflow?

Hooks must be fast (under 5s) and heavy operations should be executed out-of-band or only on Stop, not PostToolUse.

Sources and further reading

Commenti

Caricamento commenti...

Sign in to comment

Discuss this article in the community

Join the Telegram group to discuss with other developers, ask questions and share your experiences.

Did you like this article?

Explore other content on the blog or discover my projects