Vollständige Analyse des Claude Code Hooks-Mechanismus: fünf Ereignistypen, Exit-Code-Kontrolle und vier reale Konfigurationen für auditierbare, abfangbare und automatisierbare KI-Aktionen.
Jedes Mal, wenn Claude Code eine Datei liest, Code schreibt oder einen Befehl ausführt, läuft im Hintergrund ein Ereignissystem. Hooks sind die Schnittstelle, um sich mit diesem System zu verbinden — du kannst jederzeit eigene Logik einschleusen: automatisch Linter ausführen, Operationen protokollieren, gefährliche Aktionen blockieren oder beliebige Shell-Befehle auslösen.
Dieser Artikel erklärt den Hooks-Mechanismus, die Konfiguration und die praktische Anwendung vollständig.
Hooks sind Shell-Befehle, die in settings.json konfiguriert sind und von Claude Code automatisch ausgeführt werden, wenn bestimmte Ereignisse eintreten.
Die treffendste Analogie: git hooks. Git kann Skripte vor und nach Operationen wie commit und push auslösen. Claude Code Hooks funktionieren genauso — der Unterschied ist, dass die Auslösepunkte die Werkzeugaufrufe der KI sind.
Warum ist das wichtig?
Je leistungsfähiger Claude Code wird, desto mehr brauchst du eine deterministische Kontrollschicht. Hooks bieten:
- Ausführungsgarantien unabhängig vom Prompt (Claude kann Anweisungen ignorieren, aber Hooks laufen immer)
- Auditierbare Operationsprotokolle
- Automatisierte Qualitätsprüfungen
| Typ | Auslöser | Typische Verwendung |
|---|---|---|
PreToolUse |
Vor einem Werkzeugaufruf | Gefährliche Operationen blockieren, Absicht protokollieren |
PostToolUse |
Nach einem Werkzeugaufruf | Auto-Lint, Tests ausführen |
PreCompact |
Vor der Kontextkomprimierung | Snapshot des aktuellen Zustands speichern |
Notification |
Wenn Claude eine Benachrichtigung sendet | Desktop-Benachrichtigungen, Slack-Nachrichten |
Stop |
Wenn Claude die Antwort abschließt | Logs zusammenfassen, Folge-Workflows auslösen |
Am häufigsten verwendet werden PreToolUse und PostToolUse — zum Abfangen und Nachbearbeiten von Werkzeugaufrufen.
Hooks werden in ~/.claude/settings.json (global) oder in .claude/settings.json im Projektstamm (Projektebene) geschrieben:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npm run lint --silent"
}
]
}
]
}
}
Drei Kernfelder:
matcher: Regex, das Werkzeugnamen abgleicht und bestimmt, welche Werkzeugaufrufe diesen Hook auslösen. "Write|Edit" wird beim Schreiben oder Bearbeiten von Dateien ausgelöst. Leer lassen oder ".*" verwenden, um alle Werkzeuge abzugleichen.type: Derzeit nur "command".command: Beliebiger Shell-Befehl.Werkzeugnamen, die du für Matcher kennen musst:
| Werkzeugname | Aktion |
|---|---|
Write |
Neue Datei schreiben |
Edit |
Datei bearbeiten |
Bash |
Shell-Befehl ausführen |
Read |
Datei lesen |
Glob |
Dateisuche |
Grep |
Inhaltssuche |
TodoWrite |
Aufgabenliste aktualisieren |
Während der Ausführung werden Daten als JSON über stdin übergeben:
{
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.rb",
"content": "..."
},
"tool_response": "..."
}
PreToolUse erhält tool_input (die Argumente vor dem Aufruf). PostToolUse erhält zusätzlich tool_response (den Rückgabewert des Werkzeugs).
Beispiel eines Hooks mit bedingter Logik:
#!/bin/bash
input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path')
# Rubocop nur für .rb-Dateien ausführen
if [[ "$file" == *.rb ]]; then
rubocop "$file" --autocorrect-all --no-color
fi
Der Exit-Code des Hooks steuert das nachfolgende Verhalten von Claude Code:
| Exit-Code | Bedeutung |
|---|---|
0 |
Erfolg, Ausführung fortsetzen |
2 |
Blockieren: aktuellen Werkzeugaufruf abbrechen, stderr-Ausgabe an Claude zurücksenden |
| Anderer Nicht-Null | Fehler protokollieren, aber fortfahren |
Exit-Code 2 ist am nützlichsten — er erlaubt es, Abfanglogik in PreToolUse zu schreiben, um Claude an einer Operation zu hindern und den Grund mitzuteilen.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
}
]
}
]
}
}
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'cmd=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .command); if echo \"$cmd\" | grep -qE \"rm.*/(migrations|seeds)\"; then echo \"Das Löschen der Verzeichnisse migrations oder seeds ist nicht erlaubt\" >&2; exit 2; fi'"
}
]
}
]
}
}
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|Bash",
"hooks": [
{
"type": "command",
"command": "echo \"$(date '+%Y-%m-%d %H:%M:%S') $CLAUDE_TOOL_NAME: $(echo $CLAUDE_TOOL_INPUT | jq -c .)\" >> ~/.claude/audit.log"
}
]
}
]
}
}
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude ist fertig\" with title \"Claude Code\"'"
}
]
}
]
}
}
Hooks können an zwei Stellen platziert werden:
Global (~/.claude/settings.json): Regeln, die für alle Projekte gelten — Logs, Benachrichtigungen.
Projektebene (.claude/settings.json): Projektspezifische Prüfungen. Projektebene- und globale Hooks werden zusammengeführt und beide ausgeführt.
Bei Teamprojekten empfiehlt es sich, das projektspezifische settings.json in git einzuchecken, damit alle dasselbe Hooks ausführen.
Schritte zur Fehlersuche, wenn ein Hook nicht ausgelöst wird:
write ist nicht Writeecho "hook triggered" >> /tmp/hook.log beginnen, um das Auslösen zu bestätigenDer Kernwert von Hooks ist es, unvorhersehbares KI-Verhalten mit deterministischen Ingenieursstandards zu verbinden. Claude kann vergessen, Tests auszuführen, ein PostToolUse-Hook nicht. Claude kann versehentlich Dateien löschen, aber ein PreToolUse-Interceptor lässt das nicht zu.
Fang mit einem Hook an: Auto-Lint nach dem Schreiben von Dateien. Wenn das läuft, füge weitere hinzu.