Aulafy

Hooks

Hooks are shell scripts that run automatically in response to Claude Code events. They give you full control over the tool's behavior.

What are hooks?

Hooks let you intercept and react to Claude Code lifecycle events: before running a tool, after doing so, when Claude finishes a response, and so on.

Common use cases:

  • Automatically format code after each edit.
  • Log all Claude operations.
  • Block dangerous operations with custom logic.
  • Send notifications when Claude finishes a long task.
  • Run tests automatically after each change.

Hook types

Configuring hooks

Hooks are configured in .claude/settings.json (project) or ~/.claude/settings.json (global):

TerminalCode
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "prettier --write $CLAUDE_FILE_PATH"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification "Claude terminó" with title "Claude Code"'"
          }
        ]
      }
    ]
  }
}

Environment variables available in hooks

The matcher

The matcher field is a regular expression matched against the tool name. If not specified, the hook applies to all tools.

TerminalCode
# Solo edición de archivos TypeScript
"matcher": "Edit"

# Herramientas de bash y edición
"matcher": "Bash|Edit|Write"

# Cualquier herramienta de lectura
"matcher": "^Read"

Command-type hooks

The most common type. Runs a shell command. The hook can read event information via environment variables or via stdin (JSON):

TerminalCode
{
  "type": "command",
  "command": "bash /ruta/a/mi-hook.sh",
  "timeout": 30
}

Example: hook that reads event data from stdin

TerminalCode
#!/bin/bash
# mi-hook.sh — recibe el evento completo como JSON por stdin
EVENT=$(cat)
TOOL=$(echo $EVENT | jq -r '.tool_name')
FILE=$(echo $EVENT | jq -r '.tool_input.path // ""')

echo "Herramienta: $TOOL, Archivo: $FILE" >> ~/.claude/hook.log

Blocking operations with PreToolUse

A PreToolUse hook can return a non-zero exit code to block the operation:

TerminalCode
#!/bin/bash
# Bloquear rm -rf
TOOL=$(cat | jq -r '.tool_name')
CMD=$(cat | jq -r '.tool_input.command // ""')

if [[ "$CMD" == *"rm -rf"* ]]; then
  echo "BLOQUEADO: rm -rf no permitido"
  exit 1  # exit 1 = bloquear la operación
fi

exit 0  # exit 0 = permitir

Configure it like this in settings.json:

TerminalCode
{
  "hooks": {
    "PreToolUse": [{
      "matcher": "Bash",
      "hooks": [{
        "type": "command",
        "command": "bash ~/.claude/hooks/bloquear-rm.sh"
      }]
    }]
  }
}

Practical examples

Format with Prettier after editing

TerminalCode
{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Edit|Write",
      "hooks": [{
        "type": "command",
        "command": "npx prettier --write "$CLAUDE_FILE_PATH" 2>/dev/null || true"
      }]
    }]
  }
}

Run tests after changes

TerminalCode
{
  "hooks": {
    "Stop": [{
      "hooks": [{
        "type": "command",
        "command": "npm test --watchAll=false 2>&1 | tail -20"
      }]
    }]
  }
}

Log all operations

TerminalCode
{
  "hooks": {
    "PostToolUse": [{
      "hooks": [{
        "type": "command",
        "command": "echo "$(date) - $CLAUDE_TOOL_NAME: $CLAUDE_FILE_PATH" >> ~/.claude/audit.log"
      }]
    }]
  }
}

Managing hooks from the CLI

TerminalCode
# Ver hooks configurados (dentro de Claude Code)
/hooks

# O abre settings.json directamente:
claude config