← Alle Beitrage

Claude Code Hooks Tutorial: 5 produktionsreife Hooks von Grund auf

From the guide: Claude Code Comprehensive Guide

Claude Code führt in etwa 95 % der Fälle die richtige Aktion aus. Die verbleibenden 5 % umfassen Force-Pushes auf main, das Überspringen Ihres Formatters und das Committen von Code, der beim Linting durchfällt. Hooks eliminieren diese 5 %, indem sie deterministische Gates an 17 Lifecycle-Punkten in Claudes Workflow hinzufügen. Sie feuern jedes Mal, ohne Ausnahme, unabhängig von der Formulierung des Prompts oder dem Verhalten des Modells.

Kurzfassung: Hooks sind Shell-Befehle, die durch Claude Code-Lifecycle-Events ausgelöst werden. PreToolUse-Hooks prüfen und blockieren Aktionen (Exit-Code 2 = blockieren, Exit 0 = erlauben). PostToolUse-Hooks validieren und formatieren im Nachhinein. Konfigurieren Sie sie in .claude/settings.json mit einem matcher-Regex und einem verschachtelten hooks-Array. Dieses Tutorial erstellt fünf produktionsreife Hooks: Auto-Formatter, Sicherheits-Gate, Test-Runner, Benachrichtigungs-Alert und Pre-Commit-Qualitätsprüfung.

Wichtigste Erkenntnisse

  • Solo-Entwickler: Beginnen Sie mit dem Auto-Formatter (Hook 1) und dem Sicherheits-Gate (Hook 2). Diese beiden Hooks verhindern die häufigsten Claude Code-Fehler ohne laufenden Wartungsaufwand.
  • Teamleiter: Committen Sie Hooks in .claude/settings.json in Ihrem Repository. Jedes Teammitglied erhält automatisch dieselben Sicherheits-Gates und Qualitätsprüfungen.
  • Sicherheitsingenieure: Exit-Code 2 blockiert die Aktion. Exit-Code 1 gibt nur eine Warnung aus. Jeder PreToolUse-Sicherheits-Hook muss exit 2 verwenden, andernfalls bietet er keine Durchsetzung.

Was sind Hooks?

Hooks sind Shell-Befehle, die zu bestimmten Lifecycle-Events während einer Claude Code-Sitzung ausgeführt werden. Sie laufen außerhalb des LLM als einfache Skripte, die durch Claudes Aktionen ausgelöst werden – nicht als Prompts, die vom Modell interpretiert werden.

Vier Hauptkategorien decken die häufigsten Anwendungsfälle ab (Claude Code unterstützt insgesamt 17 Event-Typen):

  • Sitzungs-Events: SessionStart und Stop feuern beim Beginn oder Ende einer Sitzung. Verwenden Sie diese für Setup, Teardown und Benachrichtigungen.
  • Tool-Events: PreToolUse und PostToolUse feuern vor und nach der Verwendung eines Tools durch Claude (eine Datei schreiben, einen Bash-Befehl ausführen oder Code durchsuchen). Dies sind die mächtigsten Hooks, da sie bestimmte Aktionen prüfen und blockieren können.
  • Benachrichtigungs-Events: Notification feuert, wenn Claude eine Benachrichtigung generiert. Nützlich zum Weiterleiten von Alerts an Slack, Desktop-Benachrichtigungen oder Logging-Systeme.
  • Subagent-Events: SubagentStop feuert, wenn ein Subagent (gestartet über das Agent-Tool) abgeschlossen wird. Hooks feuern auch für Subagent-Aktionen, sodass Ihre Sicherheits-Gates rekursiv greifen.

Exit-Code-Semantik ist entscheidend. Exit 0 bedeutet Erfolg (fortfahren). Exit 2 bedeutet Aktion blockieren. Exit 1 bedeutet ein nicht-blockierender Hook-Fehler, bei dem die Aktion trotzdem fortgesetzt wird. Jeder sicherheitskritische Hook muss exit 2 verwenden, um sein Gate tatsächlich durchzusetzen.


Grundlagen der Hook-Konfiguration

Hooks befinden sich in Ihren Einstellungsdateien:

  • Projektebene: .claude/settings.json im Wurzelverzeichnis Ihres Repositorys (mit Ihrem Team geteilt)
  • Benutzerebene: ~/.claude/settings.json (Ihre persönlichen Hooks, global angewendet)

Die JSON-Struktur:

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/your/script.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/another-script.sh"
          }
        ]
      }
    ]
  }
}

Jeder Eintrag hat einen matcher (Regex, der auf Tool-Namen wie Bash, Write, Edit, Read, Glob, Grep oder Agent passt) und ein hooks-Array mit Hook-Definitionen. Jeder Hook gibt einen type ("command" für Shell-Befehle) und den auszuführenden command an. Der Matcher Write|Edit passt auf beide Tool-Typen.

Sie können Hooks auch interaktiv mit dem Slash-Befehl /hooks innerhalb einer Claude Code-Sitzung verwalten.

Wenn ein Hook feuert, übergibt Claude Code den Kontext über Umgebungsvariablen ($FILE_PATH für Dateioperationen) und stdin (ein JSON-Objekt mit dem Tool-Namen, Parametern und Sitzungs-Metadaten). Ihr Skript liest diese Informationen, um Entscheidungen zu treffen.


5 praktische Hooks

Jeder der folgenden Hooks löst ein reales Problem, auf das ich bei der Verwendung von Claude Code als primärem Entwicklungswerkzeug gestoßen bin. Alle Beispiele verwenden das korrekte verschachtelte Hook-Schema.

1. Auto-Formatierung bei Dateibearbeitung

Claude schreibt funktional korrekten Code, der gelegentlich die Formatierungsregeln Ihres Projekts verletzt. Anstatt Claude um eine Neuformatierung zu bitten, führen Sie Ihren Formatter automatisch nach jedem Dateischreibvorgang aus.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]]; then black --quiet \"$FILE_PATH\" 2>/dev/null; elif [[ \"$FILE_PATH\" == *.js ]] || [[ \"$FILE_PATH\" == *.ts ]]; then npx prettier --write \"$FILE_PATH\" 2>/dev/null; fi'"
          }
        ]
      }
    ]
  }
}

Claude Code setzt $FILE_PATH auf die geänderte Datei. Der Hook prüft die Dateiendung und führt den passenden Formatter aus. Python-Dateien erhalten black, JavaScript- und TypeScript-Dateien erhalten prettier. Das 2>/dev/null unterdrückt überflüssige Ausgaben, sodass Sie nur tatsächliche Fehler sehen.

Für größere Projekte sollten Sie den Inline-Befehl in ein eigenständiges Skript auslagern, um die Lesbarkeit zu verbessern.

2. Sicherheits-Gate für gefährliche Befehle

PreToolUse-Hooks auf dem Bash-Tool prüfen den Befehl, den Claude ausführen will, und blockieren ihn, wenn er einem gefährlichen Muster entspricht. Ich habe diesen Hook geschrieben, nachdem Claude während einer Refactoring-Sitzung einen Force-Push auf main ausgeführt hatte.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"rm\\s+-rf\\s+/|git\\s+push\\s+(-f|--force)\\s+(origin\\s+)?main|git\\s+reset\\s+--hard|DROP\\s+TABLE|:(){ :|:& };:\"; then echo \"BLOCKED: Dangerous command detected: $CMD\" >&2; exit 2; fi'"
          }
        ]
      }
    ]
  }
}

Wenn dieser Hook mit Code 2 beendet wird, bricht Claude Code den ausstehenden Befehl ab. Die Fehlermeldung wird sowohl in Ihrem Terminal als auch in Claudes Kontext angezeigt, sodass das Modell versteht, warum die Aktion fehlgeschlagen ist, und eine sicherere Alternative vorschlägt.

Blockierte Muster: - rm -rf / (rekursives Löschen vom Stammverzeichnis) - git push --force main und git push -f main (Force-Push auf den Main-Branch) - git reset --hard (Vernichtung nicht committeter Arbeit) - DROP TABLE (versehentliche Datenbankzerstörung) - Fork-Bomben

Passen Sie diese Liste an Ihre Umgebung an. Produktionsdatenbanken benötigen destruktive SQL-Muster. CLI-basierte Deployments benötigen Deployment-Befehlsschutz.

3. Test-Runner nach Änderungen

Wenn Claude eine Python-Datei bearbeitet, führen Sie automatisch die relevanten Tests aus. So werden Regressionen sofort erkannt, anstatt sie erst drei Dateibearbeitungen später zu entdecken.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]] && [[ \"$FILE_PATH\" != *test_* ]]; then TEST_FILE=\"tests/test_$(basename \"$FILE_PATH\")\"; if [[ -f \"$TEST_FILE\" ]]; then python -m pytest \"$TEST_FILE\" -x --tb=short 2>&1 | tail -20; fi; fi'"
          }
        ]
      }
    ]
  }
}

Der Hook prüft, ob die bearbeitete Datei eine Python-Quelldatei ist (nicht die Testdatei selbst), sucht nach einer entsprechenden Testdatei mit der test_-Prefix-Namenskonvention und führt sie aus, falls vorhanden. Das -x-Flag stoppt beim ersten Fehler, und tail -20 hält die Ausgabe kompakt.

Hinweis: Dieser Hook setzt ein flaches tests/-Verzeichnis mit test_-Prefix-Benennung voraus. Passen Sie die Pfadkonstruktion für verschachtelte Testverzeichnisse oder andere Namenskonventionen an.

Dieser Hook ist besonders wertvoll bei Refactoring-Sitzungen, in denen Claude mehrere Dateien bearbeitet. Die unmittelbare Rückmeldungsschleife verhindert die Kaskade von „eines repariert, drei andere kaputt gemacht”, die entsteht, wenn Tests erst am Ende ausgeführt werden.

4. Benachrichtigung bei Sitzungsende

Lang laufende Claude Code-Sitzungen können Minuten dauern. Anstatt das Terminal zu beobachten, lassen Sie sich benachrichtigen, wenn die Sitzung abgeschlossen ist.

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code session ended\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Dieses macOS-Beispiel verwendet osascript, um eine native Benachrichtigung auszulösen. Für Linux ersetzen Sie die osascript-Zeile durch notify-send "Claude Code" "Session ended". Für Slack-Benachrichtigungen verwenden Sie einen Webhook:

curl -s -X POST "$SLACK_WEBHOOK_URL" \
  -H 'Content-type: application/json' \
  -d '{"text": "Claude Code session ended"}'

Ich verwende die Slack-Variante für Hintergrundaufgaben, die mit & <task> (Claude Codes Hintergrundmodus) gestartet werden. Die Desktop-Benachrichtigung verwende ich für interaktive Sitzungen.

5. Qualitätsprüfung vor dem Commit

Bevor Claude git commit ausführt, validieren Sie, dass der Code das Linting besteht. Dies fängt Probleme auf, die allein durch Formatierung nicht erkannt werden: unbenutzte Imports, undefinierte Variablen, Typfehler.

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"^git\\s+commit\"; then if ! LINT_OUTPUT=$(ruff check . --select E,F,W 2>&1); then echo \"LINT FAILED -- fix before committing:\" >&2; echo \"$LINT_OUTPUT\" >&2; exit 2; fi; fi'"
          }
        ]
      }
    ]
  }
}

Dieser Hook wird nur aktiviert, wenn der Bash-Befehl mit git commit beginnt. Er führt ruff (einen schnellen Python-Linter) mit Error-, Pyflakes- und Warning-Regeln aus. Wenn Probleme bestehen, wird der Commit blockiert (Exit 2) und Claude sieht die Lint-Ausgabe, was typischerweise dazu führt, dass es die Probleme behebt und einen erneuten Versuch startet.

Sie können mehrere Qualitätsprüfungen schichten: mypy für Typprüfung, bandit für Sicherheitsscans oder die benutzerdefinierten Validierungsskripte Ihres Projekts. PreToolUse-Hooks auf Bash-Befehle geben Ihnen ein programmierbares Gate vor jeder Shell-Aktion.


Tipps zum Debugging von Hooks

Hooks scheitern häufiger lautlos, als man erwarten würde. Fünf Techniken, die ich zum Debuggen verwende:

  1. Testen Sie Skripte zuerst unabhängig. Leiten Sie Beispiel-JSON manuell in Ihr Skript: echo '{"tool_input":{"command":"git commit -m test"}}' | bash your-hook.sh. Wenn es außerhalb von Claude Code fehlschlägt, schlägt es auch innerhalb fehl.
  2. Verwenden Sie stderr für Debug-Ausgaben. Alles, was Ihr Hook nach stderr schreibt, erscheint in Claudes Kontext. Schreiben Sie echo "DEBUG: matched $CMD" >&2 während der Entwicklung und entfernen Sie es, sobald der Hook stabil läuft.
  3. Achten Sie auf jq-Fehler. Wenn Ihr JSON-Pfad falsch ist, gibt jq stillschweigend null zurück und Ihre Bedingungen greifen nicht. Testen Sie Ihre jq-Ausdrücke mit echtem Tool-Input.
  4. Überprüfen Sie Exit-Codes. Exit 2 blockiert Aktionen. Exit 1 gibt nur eine Warnung aus. Ein PreToolUse-Hook, der versehentlich exit 1 verwendet, bietet keinerlei Durchsetzung, obwohl er funktionierend erscheint. Beginnen Sie permissiv (standardmäßig Exit 0) und verwenden Sie exit 2 nur für bestimmte blockierte Muster.
  5. Halten Sie Hooks schnell. Hooks laufen synchron. Ein Hook, der 5 Sekunden benötigt, fügt jeder passenden Tool-Verwendung 5 Sekunden hinzu. Ich halte alle meine Hooks unter 2 Sekunden, idealerweise unter 500 Millisekunden.

Nächste Schritte

Diese fünf Hooks decken die Grundlagen ab: Formatierung, Sicherheit, Tests, Benachrichtigungen und Qualitäts-Gates. Sobald Sie mit diesen Mustern vertraut sind, können Sie Hooks für Kontextinjektion (projektspezifische Anweisungen beim Sitzungsstart hinzufügen), Rekursionsschutz (Verhinderung unendlicher Subagent-Schleifen) und Workflow-Orchestrierung (Verkettung mehrstufiger Prozesse) erstellen.

Für die Hook-Architektur, alle 17 Lifecycle-Events und fortgeschrittene Muster siehe den Hooks-Abschnitt meines vollständigen Guides: Claude Code Guide: How Do Hooks Work?

Ich habe auch über die Entstehungsgeschichten hinter meinen 95 produktionsreifen Hooks geschrieben in Claude Code Hooks: Why Each of My 95 Hooks Exists, wo ich die Vorfälle beschreibe, die jeden einzelnen motiviert haben.


FAQ

Können Hooks Claude Code daran hindern, einen Befehl auszuführen?

Ja. PreToolUse-Hooks blockieren jede Tool-Aktion durch Beenden mit Code 2. Claude Code bricht die ausstehende Aktion ab und zeigt die stderr-Ausgabe des Hooks dem Modell an. Exit 1 ist ein nicht-blockierender Hook-Fehler, bei dem die Aktion trotzdem fortgesetzt wird. Diese Unterscheidung ist entscheidend: Jeder Sicherheits-Hook muss exit 2 verwenden, nicht exit 1. Claude sieht den Ablehnungsgrund und schlägt eine sicherere Alternative vor.

Wo platziere ich Hook-Konfigurationsdateien?

Hook-Konfigurationen gehören in .claude/settings.json für Hooks auf Projektebene (in Ihr Repository committet, mit Ihrem Team geteilt) oder in ~/.claude/settings.json für Hooks auf Benutzerebene (persönlich, auf jedes Projekt angewendet). Hooks auf Projektebene haben Vorrang, wenn beide vorhanden sind. Ich empfehle die Verwendung absoluter Pfade für Skriptdateien, um Probleme mit dem Arbeitsverzeichnis zu vermeiden.

Funktionieren Hooks mit Subagents?

Ja. Hooks feuern auch für Subagent-Aktionen. Wenn Claude einen Subagent über das Agent-Tool startet, werden Ihre PreToolUse- und PostToolUse-Hooks für jedes Tool ausgeführt, das der Subagent verwendet. Ohne dieses Verhalten könnte ein Subagent Ihre Sicherheits-Gates umgehen. Das SubagentStop-Event ermöglicht es Ihnen, Bereinigung oder Validierung durchzuführen, wenn ein Subagent seine Aufgabe abschließt.

Verwandte Beiträge

How to Set Up Claude Code CLI: 5-Minute Quickstart

Install Claude Code, configure your first project, and run your first agentic coding session in under 5 minutes. Covers …

13 Min. Lesezeit

Codex CLI vs Claude Code in 2026: Architecture Deep Dive

Kernel-level sandboxing vs application-layer hooks, AGENTS.md vs CLAUDE.md, cloud tasks vs subagents. A technical compar…

13 Min. Lesezeit

Claude Code Hooks: Why Each of My 95 Hooks Exists

I built 95 hooks for Claude Code. Each one exists because something went wrong. Here are the origin stories and the arch…

7 Min. Lesezeit