Claude Code Hooks Tutorial: 5 produktionsreife Hooks von Grund auf
Claude Code führt in der überwiegenden Mehrheit der Fälle die richtige Aktion aus. Die verbleibenden Randfälle reichen vom Force-Push auf main über das Übergehen Ihres Formatters bis zum Commit von Code, der den Lint-Lauf nicht besteht. Hooks beseitigen diese Randfälle, indem sie an 31 Punkten im Lebenszyklus von Claudes Workflow (Stand August 2026) deterministische Gates einziehen.1 Dieses Tutorial gehört zu meiner Serie AI engineering über den Aufbau produktionsreifer Agentensysteme. Sie greifen jedes Mal, ausnahmslos, unabhängig von der Formulierung des Prompts oder vom Verhalten des Modells.
TL;DR: Hooks sind Shell-Befehle, die von Lebenszyklus-Ereignissen in Claude Code ausgelöst werden.1 PreToolUse-Hooks prüfen Aktionen und blockieren sie (Exit-Code 2 = blockieren, Exit-Code 0 = zulassen).2 PostToolUse-Hooks validieren und formatieren im Nachhinein. Konfiguriert werden sie in .claude/settings.json über einen matcher (ein exakter Tool-Name, eine mit | getrennte Liste oder ein regulärer Ausdruck) und ein verschachteltes hooks-Array.3 Das Tutorial unten baut fünf produktionsreife Hooks: Auto-Formatter, Sicherheits-Gate, Test-Runner, Benachrichtigung und Qualitätsprüfung vor dem Commit.
Die wichtigsten Erkenntnisse
- Einzelentwickler: Beginnen Sie mit dem Auto-Formatter (Hook 1) und dem Sicherheits-Gate (Hook 2). Diese beiden Hooks verhindern die häufigsten Fehler von Claude Code, ohne laufende Wartung zu erfordern.
- Team-Leads: Checken Sie Ihre Hooks in
.claude/settings.jsonim Repository ein. Jedes Teammitglied erhält damit automatisch dieselben Sicherheits-Gates und Qualitätsprüfungen. - Security-Engineers: Exit-Code 2 blockiert die Aktion.2 Exit-Code 1 protokolliert lediglich eine Warnung. Jeder PreToolUse-Sicherheits-Hook muss
exit 2verwenden, sonst erzwingt er gar nichts.
Was sind Hooks?
Hooks sind Shell-Befehle, die bei bestimmten Lebenszyklus-Ereignissen während einer Claude Code Sitzung ausgeführt werden. Sie laufen außerhalb des LLM als schlichte Skripte, ausgelöst durch Claudes Aktionen, nicht als Prompts, die das Modell interpretiert.
Vier zentrale Kategorien decken die häufigsten Anwendungsfälle ab (Claude Code dokumentiert mit Stand August 2026 31 Ereignistypen):1
- Sitzungs-Ereignisse:
SessionStartfeuert beim Beginn einer Sitzung,SessionEndbei deren Ende undStopjedes Mal, wenn Claude eine Antwort abschließt (nicht nur am Sitzungsende). Nutzen Sie diese für Setup, Aufräumarbeiten und Benachrichtigungen. - Tool-Ereignisse:
PreToolUseundPostToolUsefeuern, bevor und nachdem Claude ein Tool verwendet (eine Datei schreiben, einen Bash-Befehl ausführen oder Code durchsuchen). Das sind die mächtigsten Hooks, denn sie können einzelne Aktionen prüfen und blockieren. - Benachrichtigungs-Ereignisse:
Notificationfeuert, wenn Claude eine Benachrichtigung erzeugt. Praktisch, um Meldungen an Slack, an Desktop-Benachrichtigungen oder an Logging-Systeme weiterzuleiten. - Subagenten-Ereignisse:
SubagentStopfeuert, wenn ein Subagent (über das Agent-Tool gestartet) fertig ist.4 Hooks feuern auch für Aktionen von Subagenten, Ihre Sicherheits-Gates greifen also rekursiv.
Die Semantik der Exit-Codes ist entscheidend.2 Exit 0 bedeutet Erfolg (fortfahren). Exit 2 bedeutet, dass die Aktion blockiert wird. Exit 1 steht für einen nicht blockierenden Hook-Fehler, bei dem die Aktion trotzdem ausgeführt wird. Jeder sicherheitskritische Hook muss exit 2 verwenden, damit sein Gate tatsächlich greift.
Das mentale Modell: drei Arten von Garantien
Bevor Sie einen Hook schreiben, fragen Sie sich: Welche Art von Garantie brauche ich?
Formatierungsgarantien stellen Konsistenz im Nachhinein her. PostToolUse-Hooks auf Write/Edit führen nach jeder Dateiänderung Ihren Formatter aus. Was das Modell ausgibt, spielt dabei keine Rolle, weil der Formatter alles normalisiert. Diese Hooks sind idempotent und lassen sich gefahrlos bei jeder Bearbeitung ausführen.
Sicherheitsgarantien verhindern gefährliche Aktionen, bevor sie ausgeführt werden. PreToolUse-Hooks auf Bash prüfen Befehle und blockieren destruktive Muster mit Exit-Code 2. Diese Hooks müssen schnell sein (unter 500 ms), denn sie sitzen vor jedem passenden Tool-Aufruf, und sie müssen Exit 2 verwenden (nicht Exit 1), weil Exit 1 nur warnt, ohne zu blockieren.
Qualitätsgarantien prüfen den Zustand an Entscheidungspunkten. PreToolUse-Hooks auf git commit-Befehle starten Ihren Linter oder Ihre Test-Suite und blockieren den Commit, wenn die Qualitätsprüfungen fehlschlagen. Anders als Formatierungs-Hooks, die bei jeder Bearbeitung feuern, greifen Qualitäts-Hooks nur zu bestimmten Momenten und halten den Overhead damit gering.
Der konzeptionelle Vorfahr sind Git-Hooks8: pre-commit, pre-push und post-commit erfüllen dieselben drei Rollen. Claude Code Hooks weiten dieses Muster von Git-Operationen auf jede Tool-Aktion des Agenten aus. Diese Entwicklung seziere ich in every hook is a scar: Jeder Hook existiert, weil ohne ihn etwas schiefgegangen ist.
Grundlagen der Hook-Konfiguration
Hooks liegen in Ihren Einstellungsdateien:
- Auf Projektebene:
.claude/settings.jsonim Wurzelverzeichnis Ihres Repositorys (geteilt mit Ihrem Team)3 - Auf Benutzerebene:
~/.claude/settings.json(Ihre persönlichen Hooks, global angewendet)3
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 besitzt einen matcher, der Tool-Namen wie Bash, Write, Edit, Read, Glob, Grep oder Agent filtert, sowie ein hooks-Array mit Hook-Definitionen. Die Matcher-Semantik laut Referenz: "*", "" oder ein weggelassener Matcher trifft auf alles zu; ein Wert aus Buchstaben, Ziffern, _, -, |, Kommas und Leerzeichen ist ein exakter Name oder eine Liste, Write|Edit trifft also auf beide Tools zu (der Unterstrich zählt bei MCP-Tool-Namen wie mcp__github__search_code); alles andere wird als nicht verankerter regulärer Ausdruck behandelt. Die Auswertung beachtet Groß- und Kleinschreibung – bash trifft niemals auf Bash zu. Jeder Hook gibt einen type an ("command" für Shell-Befehle) sowie den auszuführenden command.
Registrierte Hooks können Sie mit dem schreibgeschützten /hooks-Browser innerhalb einer Sitzung einsehen; zum Hinzufügen, Ändern oder Entfernen bearbeiten Sie das Einstellungs-JSON direkt.5
Wenn ein Hook feuert, liefert Claude Code den Kontext als JSON-Objekt über stdin: den Tool-Namen, die Tool-Eingabe (einschließlich file_path bei Dateioperationen) und Metadaten zur Sitzung.6 Ihr Skript liest stdin – typischerweise mit jq – und entscheidet auf dieser Grundlage. Einige Umgebungsvariablen liefern zusätzlichen Kontext – $CLAUDE_PROJECT_DIR zur Pfadauflösung, $CLAUDE_EFFORT für die aktuelle Effort-Stufe –, doch tool-spezifische Felder wie der Dateipfad kommen ausschließlich über stdin; eine Variable $FILE_PATH pro Tool gibt es nicht.
5 praktische Hooks
Jeder Hook unten löst ein reales Problem, auf das ich gestoßen bin, während ich Claude Code als mein wichtigstes Entwicklungswerkzeug eingesetzt habe. Alle Beispiele verwenden das korrekte verschachtelte Hook-Schema aus der Hooks-Referenz7.
1. Automatisch formatieren beim Bearbeiten einer Datei
Claude schreibt funktional korrekten Code, der gelegentlich gegen die Formatierungsregeln Ihres Projekts verstößt. Zunächst habe ich „führe nach dem Bearbeiten von Python-Dateien immer black aus“ in meine CLAUDE.md geschrieben, doch die Anweisung wirkte nur in etwa 80 % der Fälle. Bei einer komplexen Änderung über mehrere Dateien hinweg ließ das Modell den Formatierungsschritt mitunter aus. Ein PostToolUse-Hook beseitigt diese Inkonsistenz vollständig: Ihr Formatter läuft nach jedem Schreibvorgang, unabhängig davon, wofür das Modell sich entschieden hat.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(jq -r \".tool_input.file_path // empty\"); if [[ \"$FILE\" == *.py ]]; then black --quiet \"$FILE\" 2>/dev/null; elif [[ \"$FILE\" == *.js || \"$FILE\" == *.ts ]]; then npx prettier --write \"$FILE\" 2>/dev/null; fi'"
}
]
}
]
}
}
Der Hook liest die JSON-Eingabe des Tools von stdin und extrahiert .tool_input.file_path mit jq – dieses stdin-Objekt ist der einzige Ort, an dem der Dateipfad steht, denn Claude Code setzt keine tool-spezifischen Umgebungsvariablen. Anschließend prüft er die Dateiendung und startet den passenden Formatter: Python-Dateien bekommen black, JavaScript- und TypeScript-Dateien prettier. Das 2>/dev/null unterdrückt störende Ausgaben, sodass Sie nur echte Fehler sehen.
Bei größeren Projekten verschieben Sie den Inline-Befehl der Lesbarkeit halber in ein eigenständiges Skript.
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. Die erste Fassung dieses Hooks entstand, nachdem Claude während einer Refactoring-Sitzung einen Force-Push auf main gemacht hatte. (Die weiter reichenden Folgen von Agenten-Autonomie behandle ich in anatomy of a claw und Claude Code as infrastructure.) Das Modell hatte die Aufgabe erhalten, „die Änderungen zu pushen“, und legte das als git push --force origin main aus, weil der Branch auseinandergelaufen war. Die Reparatur dauerte Sekunden; der Vorfall führte zu einem dauerhaften Gate.
{
"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|:\\(\\)\\s*\\{\\s*:\"; then echo \"BLOCKED: Dangerous command detected: $CMD\" >&2; exit 2; fi'"
}
]
}
]
}
}
Wenn dieser Hook mit Code 2 endet, bricht Claude Code den anstehenden Befehl ab. Die Fehlermeldung erscheint sowohl in Ihrem Terminal als auch in Claudes Kontext, sodass das Modell versteht, warum die Aktion fehlgeschlagen ist, und eine sicherere Alternative vorschlägt.
Blockierte Muster:
- rm -rf / (rekursives Löschen ab dem Wurzelverzeichnis)
- git push --force main und git push -f main (Force-Push auf den main-Branch)
- git reset --hard (Vernichten nicht committeter Arbeit)
- DROP TABLE (versehentliche Zerstörung der Datenbank)
- Fork-Bomben (das Muster trifft den Auftakt :(){ und erfasst damit Varianten mit und ohne Leerzeichen)
Passen Sie diese Liste an Ihre Umgebung an. Produktionsdatenbanken verlangen zusätzliche destruktive SQL-Muster. Deployments über die CLI verlangen Wächter für die Deployment-Befehle.
3. Test-Runner nach Änderungen
Sobald Claude eine Python-Datei bearbeitet, laufen automatisch die zugehörigen Tests. Ein sofortiger Testlauf fängt Regressionen ab, bevor sie sich über drei oder vier weitere Dateiänderungen hinweg aufschaukeln.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(jq -r \".tool_input.file_path // empty\"); if [[ \"$FILE\" == *.py && \"$FILE\" != *test_* ]]; then TEST_FILE=\"tests/test_$(basename \"$FILE\")\"; if [[ -f \"$TEST_FILE\" ]]; then if ! OUT=$(python -m pytest \"$TEST_FILE\" -x --tb=short 2>&1); then echo \"TESTS FAILED after editing $FILE:\" >&2; echo \"$OUT\" | tail -20 >&2; exit 2; fi; fi; fi'"
}
]
}
]
}
}
Der Hook entnimmt dem stdin-JSON den Pfad der bearbeiteten Datei, prüft, ob es sich um eine Python-Quelldatei handelt (und nicht um eine Testdatei selbst), sucht nach der zugehörigen Testdatei gemäß der Namenskonvention mit test_-Präfix und führt sie aus, sofern vorhanden. Die Option -x stoppt beim ersten Fehlschlag, und tail -20 hält die Ausgabe knapp. Nützlich wird der Hook erst durch das Exit 2 im Fehlerfall: Ein PostToolUse-Hook kann die bereits erfolgte Bearbeitung nicht rückgängig machen, doch Exit 2 reicht die Ausgabe der fehlgeschlagenen Tests über stderr an Claude weiter, das den Schaden daraufhin behebt, bevor es weitergeht. Eine Variante, die den Fehlschlag lediglich mit Exit 0 ausgibt, schiebt ihn ins Debug-Log, wo niemand hinsieht.
Hinweis: Der Hook oben setzt ein flaches Verzeichnis tests/ mit test_-Präfix voraus. Bei Projekten, die den Quellbaum spiegeln (etwa tests/api/test_users.py zu src/api/users.py), ersetzen Sie die Zeile TEST_FILE durch:
TEST_FILE="tests/$(echo "$FILE" | sed 's|.*/src/||; s|\([^/]*\)\.py$|test_\1.py|')"
Besonders wertvoll ist der Test-Runner-Hook bei Refactoring-Sitzungen, in denen Claude mehrere Dateien anfasst. Ohne sofortige Rückmeldung schaukeln sich Fehler auf: Claude bearbeitet Datei A, zerschießt damit die Tests von Datei B und bearbeitet dann Datei C auf Basis des kaputten Zustands von B. Bis Sie den Fehlschlag bemerken, müssen drei Dateien repariert werden statt einer. Ein Testlauf nach jeder Bearbeitung fängt den ersten Bruch sofort ab.
4. Benachrichtigung, wenn Claude fertig ist
Lange Claude Code Durchläufe können Minuten dauern. Statt das Terminal zu beobachten, lassen Sie sich benachrichtigen, sobald Claude seine Antwort beendet hat. (Stop feuert am Ende jeder Antwort – für einen Hook beim tatsächlichen Sitzungsende registrieren Sie stattdessen SessionEnd.)
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude finished responding\" with title \"Claude Code\"'"
}
]
}
]
}
}
Die macOS-Variante oben nutzt osascript für eine native Benachrichtigung. Unter Linux ersetzen Sie die osascript-Zeile durch notify-send "Claude Code" "Finished responding". Für Benachrichtigungen in Slack verwenden Sie einen Webhook:
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
-d '{"text": "Claude Code finished responding"}'
Die Slack-Variante nutze ich für Hintergrundaufgaben, die mit & <task> gestartet werden (dem Hintergrundmodus von Claude Code). Die Desktop-Benachrichtigung deckt interaktive Sitzungen ab.
5. Qualitätsprüfung vor dem Commit
Bevor Claude git commit ausführt, prüfen Sie, ob der Code den Linter besteht. Ein Lint-Gate vor dem Commit fängt Probleme ab, die reine Formatierung übersieht: ungenutzte Importe, 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'"
}
]
}
]
}
}
Das Qualitäts-Gate wird nur aktiv, wenn der Bash-Befehl mit git commit beginnt. Es startet ruff (einen schnellen Python-Linter) mit den Regelgruppen für Fehler, pyflakes und Warnungen. Gibt es Beanstandungen, blockiert der Hook den Commit (Exit 2), und Claude sieht die Lint-Ausgabe, woraufhin es die Probleme in der Regel behebt und es erneut versucht.
Sie können mehrere Qualitätsprüfungen stapeln: mypy für die Typprüfung, bandit für Sicherheitsscans oder die eigenen Validierungsskripte Ihres Projekts. PreToolUse-Hooks auf Bash-Befehle geben Ihnen ein programmierbares Gate vor jeder Shell-Aktion.
PreToolUse und PostToolUse in .claude/settings.json: die Referenz
Wenn Sie nach der Konfigurationsform von PreToolUse/PostToolUse gesucht haben und hier gelandet sind: Das ist die Kurzfassung. Beide Ereignisse liegen unter dem Schlüssel hooks in .claude/settings.json (Projekt) oder ~/.claude/settings.json (Benutzer); die Geltungsbereiche werden kombiniert, identische Handler dabei entdoppelt. Ein Block, der beide Ereignisse verdrahtet:3
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": ".claude/hooks/guard.sh" }]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": ".claude/hooks/format.sh" }]
}
]
}
}
Der Vertrag in drei Zeilen: Beide Ereignisse liefern das Tool-JSON über stdin (.tool_input.command bei Bash, .tool_input.file_path bei Write/Edit – eine tool-spezifische Umgebungsvariable gibt es nicht).6 PreToolUse feuert vor dem Tool-Aufruf und kann ihn mit Exit 2 blockieren. PostToolUse feuert, nachdem das Tool erfolgreich war – die Aktion lässt sich damit nicht rückgängig machen, doch Exit 2 spielt die stderr-Ausgabe an Claude zurück, das daraufhin behebt, was der Hook bemängelt hat.2
Die vollständige Dokumentation beider Ereignisse – JSON-Ausgabefelder, permissionDecision, updatedInput, Timeouts – steht in der offiziellen Referenz code.claude.com/docs/en/hooks; der Hooks-Abschnitt meines Claude Code Guides deckt dasselbe Gebiet mit praxiserprobten Mustern ab.
Ereignisnamen geraten? Die Zuordnung
Hook-Ereignisse, nach denen gesucht wird, und das, was Claude Code tatsächlich auslöst:1
| Wenn Sie geraten haben … | Das echte Ereignis |
|---|---|
onStart / onSessionStart |
SessionStart |
onFinish / onEnd / onStop |
Stop (feuert, sobald Claude eine Antwort abschließt) oder SessionEnd (Sitzung wird geschlossen) |
onToolUse / beforeToolUse |
PreToolUse |
afterToolUse |
PostToolUse |
onPrompt / onUserMessage |
UserPromptSubmit |
onError |
PostToolUseFailure (Tool-Fehler) oder StopFailure (API-Fehler) |
Insgesamt gibt es 31 Ereignisse – die Ereignistabelle im Guide führt jedes einzelne auf.
Ein PreToolUse-, PostToolUse- und Stop-Hook in einer Konfiguration
Die drei meistgesuchten Ereignisse, gemeinsam verdrahtet – ein Befehlswächter, ein Formatter und eine Benachrichtigung bei Fertigstellung:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [{ "type": "command", "command": ".claude/hooks/guard-bash.sh" }]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [{ "type": "command", "command": "bash -c 'FILE=$(jq -r \".tool_input.file_path // empty\"); [[ \"$FILE\" == *.py ]] && black --quiet \"$FILE\" || true'" }]
}
],
"Stop": [
{
"hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude finished responding\" with title \"Claude Code\"'" }]
}
]
}
}
guard-bash.sh ist das Sicherheits-Gate aus Hook 2, ausgelagert in ein eigenständiges Skript: Speichern Sie den Rumpf des bash -c-Einzeilers aus Hook 2 (alles zwischen den äußeren einfachen Anführungszeichen) als .claude/hooks/guard-bash.sh, ergänzen Sie eine Shebang-Zeile #!/bin/bash und machen Sie die Datei mit chmod +x ausführbar – oder übernehmen Sie das fertige Skript gleichen Namens aus Claude Code Hooks Explained. Jedes Ereignis behält seine eigene Semantik: Der PreToolUse-Wächter kann Befehle ablehnen (Exit 2), der PostToolUse-Formatter läuft nach jeder passenden Bearbeitung, und der Stop-Hook feuert bei jedem Antwortende – ein Matcher ist dafür nicht nötig: Stop ist kein Tool-Ereignis, es gibt also nichts zu filtern.1
Tipps zum Debuggen von Hooks
Hooks scheitern häufiger stillschweigend, als man erwarten würde. Fünf Techniken, mit denen ich sie debugge:
- Testen Sie Skripte zuerst eigenständig. Leiten Sie Beispiel-JSON von Hand in Ihr Skript:
echo '{"tool_input":{"command":"git commit -m test"}}' | bash your-hook.sh. Scheitert es außerhalb von Claude Code, scheitert es auch darin. - Wissen Sie, wohin stderr tatsächlich geht. Stderr erreicht Claudes Kontext nur, wenn der Hook mit 2 endet; bei Exit 0 landet es im Debug-Log, und bei anderen Exit-Codes ungleich null erscheint im Transkript lediglich ein Hinweis auf einen Hook-Fehler. Während der Entwicklung starten Sie
claude --debug(oder/debugmitten in der Sitzung) und beobachten das Debug-Log, in dem die Ausgabe von Hooks mit Exit 0 landet. - Achten Sie auf Fehler in jq. Ist Ihr JSON-Pfad falsch, liefert
jq9 stillschweigendnull, und Ihre Bedingungen greifen nicht. Prüfen Sie Ihrejq-Ausdrücke gegen echte Tool-Eingaben. - Überprüfen Sie die Exit-Codes. Exit 2 blockiert Aktionen. Exit 1 warnt nur. Ein PreToolUse-Hook, der versehentlich
exit 1verwendet, erzwingt gar nichts und wirkt dabei funktionsfähig. Beginnen Sie freizügig (standardmäßig Exit 0) und setzen Sieexit 2nur für konkret blockierte Muster ein. - Halten Sie Hooks schnell. Hooks laufen synchron. Ein Hook, der 5 Sekunden braucht, verlängert jede passende Tool-Nutzung um 5 Sekunden. Ich halte alle meine Hooks unter 2 Sekunden, idealerweise unter 500 Millisekunden.
Der häufigste Fehler bei Hooks: ein Sicherheits-Gate mit exit 1 statt exit 2 zu schreiben. Beim Testen wirkt der Hook funktionsfähig, weil die Warnung im Terminal erscheint. Doch Exit 1 ist eine nicht blockierende Warnung. Der gefährliche Befehl wird trotzdem ausgeführt. Diesen Fehler habe ich in den Hook-Konfigurationen von drei verschiedenen Teams gesehen, die allesamt glaubten, Force-Pushes unterbunden zu haben. Testen Sie jeden Sicherheits-Hook, indem Sie das blockierte Muster auslösen und prüfen, ob die Aktion tatsächlich verhindert und nicht nur bemängelt wurde.
Nächste Schritte
Diese fünf Hooks decken die Grundlagen ab: Formatierung, Sicherheit, Tests, Benachrichtigungen und Qualitäts-Gates. Sobald Ihnen diese Muster vertraut sind, können Sie Hooks für Kontextinjektion (projektspezifische Anweisungen zum Sitzungsstart), Rekursionswächter (gegen endlose Subagenten-Schleifen) und Workflow-Orchestrierung (das Verketten mehrstufiger Abläufe) bauen.
Zur Hook-Architektur, zum vollständigen Lebenszyklus mit 31 Ereignissen und zu fortgeschrittenen Mustern lesen Sie den Hooks-Abschnitt meines ausführlichen Claude Code guide oder den Durchgang Ereignis für Ereignis in Claude Code Hooks Explained.
Über die Entstehungsgeschichten meiner 95 produktiven Hooks habe ich außerdem in Claude Code Hooks: Why Each of My 95 Hooks Exists geschrieben; dort stehen die Vorfälle, die jeden einzelnen ausgelöst haben.
Quellen
FAQ
Können Hooks Claude Code daran hindern, einen Befehl auszuführen?
Ja. PreToolUse-Hooks blockieren jede Tool-Aktion, indem sie mit Code 2 enden. Claude Code bricht die anstehende Aktion ab und zeigt dem Modell die stderr-Ausgabe des Hooks. Exit 1 ist ein nicht blockierender Hook-Fehler, bei dem die Aktion trotzdem ausgeführt wird. Der Unterschied zwischen den Exit-Codes zählt: Jeder Sicherheits-Hook muss exit 2 verwenden, nicht exit 1.2 Claude sieht den Ablehnungsgrund und schlägt eine sicherere Alternative vor.
Wo lege ich die Konfigurationsdateien für Hooks ab?
Hook-Konfigurationen gehören in .claude/settings.json für Hooks auf Projektebene (ins Repository eingecheckt, mit Ihrem Team geteilt) oder in ~/.claude/settings.json für Hooks auf Benutzerebene (persönlich, in jedem Projekt wirksam). Existieren beide, werden die Hooks kombiniert statt überschrieben: Jeder passende Hook aus jedem Geltungsbereich läuft, identische Handler werden entdoppelt. Für Skriptdateien empfehle ich absolute Pfade, um Probleme mit dem Arbeitsverzeichnis zu vermeiden.
Funktionieren Hooks mit Subagenten?
Ja. Hooks feuern auch für Aktionen von Subagenten.4 Startet Claude über das Agent-Tool einen Subagenten, laufen Ihre PreToolUse- und PostToolUse-Hooks für jedes Tool, das dieser Subagent verwendet. Ohne rekursive Durchsetzung könnte ein Subagent Ihre Sicherheits-Gates umgehen. Mit dem Ereignis SubagentStop führen Sie Aufräumarbeiten oder Prüfungen aus, sobald ein Subagent seine Aufgabe abgeschlossen hat.4
Wie viele Hooks sind zu viele?
Die Grenze setzt die Performance, nicht die Anzahl. Jeder Hook läuft synchron, die gesamte Ausführungszeit kommt also zu jedem passenden Tool-Aufruf hinzu. Ich betreibe 95 Hooks über Einstellungen auf Benutzer- und Projektebene ohne spürbare Latenz, weil jeder Hook in unter 200 ms fertig ist. Die Schwelle, auf die ich achte: Verlängert ein PostToolUse-Hook jede Dateibearbeitung um mehr als 500 ms, fühlt sich die Sitzung träge an. Messen Sie Ihre Hooks mit time, bevor Sie sie ausrollen. Zehn schnelle Hooks schlagen zwei langsame.
-
Anthropic, “Hooks reference — Hook events.” code.claude.com/docs/en/hooks#hook-events ↩↩↩↩↩
-
Anthropic, “Hooks reference — Exit code output.” code.claude.com/docs/en/hooks#exit-code-output ↩↩↩↩↩
-
Anthropic, “Hooks reference — Configuration.” code.claude.com/docs/en/hooks#configuration ↩↩↩↩
-
Anthropic, “Hooks reference — Hook events” (SubagentStart/SubagentStop). code.claude.com/docs/en/hooks#hook-events ↩↩↩
-
Anthropic, “Hooks reference — Configuration” (das
/hooks-Menü). code.claude.com/docs/en/hooks#configuration ↩ -
Anthropic, “Hooks reference — Hook input and output.” code.claude.com/docs/en/hooks#hook-input-and-output ↩↩
-
Anthropic, “Hooks reference — Configuration” (das verschachtelte Hook-Schema). code.claude.com/docs/en/hooks#configuration ↩
-
Git-Dokumentation, “Customizing Git: Git Hooks.” git-scm.com/book/en/v2/Customizing-Git-Git-Hooks ↩
-
jq-Handbuch, “Command-line JSON processor.” jqlang.github.io/jq/manual ↩