Claude Code Hooks Cookbook: 12 Patterns für sichere und produktive Coding-Agents
Zwölf erprobte Hook-Patterns für Claude Code: automatische Tests, Security-Scans, Audit-Logs und Format-Enforcement, mit Konfigurationsbeispielen.
Veröffentlicht: 2026-05-30. Aktualisiert: 2026-05-30.
Das Wichtigste in Kürze
- Hooks sind die Antwort auf 'wie verhindere ich, dass der Agent unsicheren Code committet': automatisch, deterministisch, ohne Vertrauen aufs gute Verhalten.
- Pre-Tool-Hooks validieren bevor etwas passiert, Post-Tool-Hooks reagieren auf Ergebnisse - beide haben unterschiedliche Sweet Spots.
- Standardisierte Hooks im Team-Repo schlagen individuelle Setups: ein Update, alle profitieren.
- Audit-Logging via Hook ist die einfachste Compliance-Maßnahme, die ihr in einem AI-Coding-Setup machen könnt.
- Hooks sind Shell-Skripte oder TypeScript - keine Magie. Wer pre-commit-hooks kennt, kennt Claude-Code-Hooks.
Hooks sind das Stellrad, das aus einem hoffnungsvollen AI-Coding-Setup ein zuverlässiges Engineering-Werkzeug macht. Sie laufen automatisch vor oder nach jedem Tool-Call von Claude Code - validieren, loggen, blockieren, oder reagieren. Wer pre-commit-hooks aus dem klassischen Git-Workflow kennt, kennt das Prinzip. Dieser Cookbook zeigt 12 production-erprobte Patterns mit Konfigurationsbeispielen, die ihr in eurem Team direkt einsetzen könnt.
Wie Hooks technisch funktionieren
Hooks werden in der settings.json eines Projekts (oder global in ~/.claude/settings.json) konfiguriert. Sie matchen auf Tool-Namen oder Tool-Pattern und führen ein Skript aus - entweder vor oder nach der Tool-Ausführung. Konfigurationsschnitt sieht so aus:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/pre-bash.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/post-edit.sh"
}
]
}
]
}
}
Drei Dinge sind wichtig:
- Matcher ist ein Regex auf den Tool-Namen.
Bash,Edit|Write,.*- alles funktioniert. - Skripte liegen typisch in
.claude/hooks/und werden ins Git committed. - Exit-Code entscheidet bei Pre-Hooks, ob die Aktion durchgehen darf (Exit 0 = erlaubt, alles andere = blockiert).
Jetzt zu den 12 Patterns.
Pattern 1: Pre-Commit Test-Run vor git commit
Use-Case: Verhindert, dass Claude einen Commit durchführt, der lokale Tests bricht.
#!/bin/bash
# .claude/hooks/pre-bash-git-commit.sh
TOOL_INPUT=$(cat)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" == "git commit"* ]]; then
echo "Running tests before commit..."
npm test || {
echo "TESTS FAILED - commit blocked." >&2
exit 1
}
fi
exit 0
Settings.json-Eintrag matcht auf Bash und ruft das Skript. Wenn Tests fehlschlagen, exit 1, Commit wird verhindert.
Pattern 2: Post-Edit Type-Check
Use-Case: Nach jeder Datei-Änderung in einem TypeScript-Projekt automatisch tsc --noEmit laufen, um Type-Fehler sofort zu fangen.
#!/bin/bash
# .claude/hooks/post-edit-tsc.sh
TOOL_INPUT=$(cat)
FILE=$(echo "$TOOL_INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
npx tsc --noEmit 2>&1 | head -30 >&2
fi
Hinweis: Hier kein Exit 1 - Post-Hook kann ohnehin nicht mehr blockieren. Aber der Type-Fehler erscheint sofort im Agent-Context.
Pattern 3: Pre-Deploy Diff-Anzeige bei kubectl apply
Use-Case: Sicherheitsnetz für DevOps-Tasks. Bevor kubectl apply läuft, automatisch kubectl diff anzeigen.
#!/bin/bash
TOOL_INPUT=$(cat)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" == "kubectl apply"* ]]; then
FILE=$(echo "$COMMAND" | grep -oP '(?<=-f )[^ ]+')
if [ -n "$FILE" ]; then
echo "=== kubectl diff for $FILE ==="
kubectl diff -f "$FILE" 2>&1 || true
echo "============================="
fi
fi
Verhindert nichts, aber zwingt zur bewussten Entscheidung.
Pattern 4: Audit-Log pro Tool-Call
Use-Case: Compliance-Anforderung - jeder Tool-Call wird strukturiert geloggt.
#!/bin/bash
# .claude/hooks/audit-log.sh
TOOL_INPUT=$(cat)
TIMESTAMP=$(date -Iseconds)
TOOL_NAME=$(echo "$TOOL_INPUT" | jq -r '.tool_name')
USER=$(whoami)
REPO=$(basename "$PWD")
echo "{\"ts\":\"$TIMESTAMP\",\"user\":\"$USER\",\"repo\":\"$REPO\",\"tool\":\"$TOOL_NAME\"}" \
>> ~/.claude/audit.log
Einfacher Append-Log. In Production schickt der gleiche Hook strukturierte Logs an SIEM (Splunk, Datadog, CloudWatch).
Pattern 5: Pre-Bash Block für gefährliche Befehle
Use-Case: Bestimmte Befehle (z.B. rm -rf /, kubectl delete ns prod, aws s3 rm --recursive) hart blockieren.
#!/bin/bash
TOOL_INPUT=$(cat)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.tool_input.command // empty')
DANGEROUS=(
"rm -rf /"
"kubectl delete ns prod"
"aws s3 rm --recursive"
"DROP DATABASE"
"TRUNCATE TABLE"
)
for pattern in "${DANGEROUS[@]}"; do
if [[ "$COMMAND" == *"$pattern"* ]]; then
echo "BLOCKED: command matches dangerous pattern '$pattern'" >&2
exit 1
fi
done
exit 0
Macht aus „Agent darf alles" ein „Agent darf alles außer den 5 schlimmsten Dingen".
Pattern 6: Post-Write Format-Enforcement
Use-Case: Wenn Claude eine Datei schreibt, automatisch Prettier drüberlaufen lassen.
#!/bin/bash
TOOL_INPUT=$(cat)
FILE=$(echo "$TOOL_INPUT" | jq -r '.tool_input.file_path // empty')
if [ -n "$FILE" ] && [ -f "$FILE" ]; then
case "$FILE" in
*.ts|*.tsx|*.js|*.jsx|*.json|*.md)
npx prettier --write "$FILE" 2>/dev/null
;;
esac
fi
Resultat: alle vom Agent geschriebenen Dateien folgen dem Projektstandard, ohne dass der Agent Prettier-Regeln im Kontext halten muss.
Pattern 7: Pre-Edit Permission-Check pro Pfad
Use-Case: Bestimmte Verzeichnisse (z.B. secrets/, infra/prod/) für Claude blockieren.
#!/bin/bash
TOOL_INPUT=$(cat)
FILE=$(echo "$TOOL_INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED=(
"secrets/"
"infra/prod/"
".env.production"
)
for prefix in "${PROTECTED[@]}"; do
if [[ "$FILE" == *"$prefix"* ]]; then
echo "BLOCKED: $FILE is in protected path '$prefix'" >&2
exit 1
fi
done
exit 0
Komplementär zum permissions-Feld in der settings.json - gibt feinere Kontrolle.
Pattern 8: Post-Install Security-Audit
Use-Case: Nach npm install oder pnpm add automatisch Security-Audit auf neue Dependencies.
#!/bin/bash
TOOL_INPUT=$(cat)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" == "npm install"* || "$COMMAND" == "pnpm add"* ]]; then
echo "=== Security audit ==="
npm audit --audit-level=moderate 2>&1 | tail -20 >&2
fi
Findet bekannte CVEs in neuen Dependencies, bevor sie ins Git landen.
Pattern 9: Pre-Edit Branch-Lock
Use-Case: Verhindert, dass Claude auf main direkt commits macht.
#!/bin/bash
BRANCH=$(git branch --show-current 2>/dev/null)
PROTECTED=("main" "master" "production" "release")
for p in "${PROTECTED[@]}"; do
if [[ "$BRANCH" == "$p" ]]; then
echo "BLOCKED: cannot edit on protected branch '$BRANCH'" >&2
echo "Use a feature branch: git checkout -b feature/your-change" >&2
exit 1
fi
done
exit 0
Settings.json matcht auf Edit|Write. Verhindert versehentliche Direkt-Edits auf Production-Branches.
Pattern 10: Post-Bash Slack-Notification bei Deploy
Use-Case: Nach jedem Deployment-Befehl eine Notification ans Team schicken.
#!/bin/bash
TOOL_INPUT=$(cat)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" == *"deploy"* || "$COMMAND" == "kubectl apply"* ]]; then
USER=$(whoami)
REPO=$(basename "$PWD")
curl -X POST -H "Content-Type: application/json" \
-d "{\"text\":\"Deploy executed by $USER in $REPO\"}" \
"$SLACK_WEBHOOK_URL" >/dev/null 2>&1
fi
Macht Deploys sichtbar - kein „heimliches Deployen" möglich.
Pattern 11: Pre-Bash Cost-Estimate für AWS-Befehle
Use-Case: Bei terraform plan automatisch eine Cost-Estimation einblenden.
#!/bin/bash
TOOL_INPUT=$(cat)
COMMAND=$(echo "$TOOL_INPUT" | jq -r '.tool_input.command // empty')
if [[ "$COMMAND" == "terraform plan"* ]]; then
if command -v infracost >/dev/null 2>&1; then
echo "=== Cost estimate ==="
infracost breakdown --path . 2>&1 | tail -15 >&2
echo "===================="
fi
fi
Cost-Transparenz bei jedem Plan, ohne dass jemand explizit dran denken muss.
Pattern 12: Post-Edit Doc-Reminder
Use-Case: Wenn Claude eine API-Definition ändert, daran erinnern, die zugehörige Doku zu updaten.
#!/bin/bash
TOOL_INPUT=$(cat)
FILE=$(echo "$TOOL_INPUT" | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *"routes/"*".ts" || "$FILE" == *"controllers/"*".ts" ]]; then
DOC_FILE="docs/api/$(basename "$FILE" .ts).md"
if [ ! -f "$DOC_FILE" ] || [ "$FILE" -nt "$DOC_FILE" ]; then
echo "REMINDER: API-File geändert. Doku $DOC_FILE ist älter - bitte aktualisieren." >&2
fi
fi
Erinnert an Doc-Updates, ohne sie zu erzwingen.
Hooks im Team-Setup organisieren
Wer Hooks team-weit nutzt, sollte drei Prinzipien beachten:
- Ins Git committen.
.claude/hooks/ist Teil des Repos, nicht persönlich. Ein zentrales Update profitiert alle. - Versioniert + dokumentiert. Pro Hook-Skript ein Kommentar-Header: was tut es, wann läuft es, was sind die Exit-Codes.
- Schnell halten. Sub-Sekunde, wenn möglich. Längere Validierungen besser in separaten Workflows (CI) als in Pre-Hooks.
Für größere Engineering-Organisationen ist eine zentrale Hook-Library (als NPM-Package oder Git-Submodule) das nächste Level. Updates der Library propagieren in alle Repos. Mehr dazu: CLAUDE.md, Hooks & Skills Engineering.
Was kein Hook-Pattern sein sollte
Drei Anti-Patterns, die ich in Audits regelmäßig sehe:
- Hooks als Test-Suite-Ersatz. Hooks sind Sicherheitsnetze für AI-Coding-Sessions, nicht euer CI. Test-Suite läuft im CI, nicht im Pre-Hook.
- Hooks ohne Logging. Wenn ein Hook still etwas tut, ist die Debugging-Erfahrung furchtbar. Jeder Hook sollte mindestens loggen, dass er gelaufen ist.
- Hooks, die das Agent-Verhalten überschreiben. Wenn Claude eine API ändern soll und der Hook das blockiert, wird Claude versuchen, den Hook zu umgehen. Hooks sind Guardrails, nicht Antagonisten.
Nächste Schritte
Wer einen ersten Hook bauen will: kopiert Pattern 1 (Pre-Commit Test-Run), passt das npm test an euer Test-Kommando an, committet die .claude/hooks/pre-bash-git-commit.sh. 15 Minuten Aufwand, sofortiger Nutzen.
Wer eine projekt- oder organisations-weite Hook-Library aufbauen will: das ist der Sweet Spot meines Services CLAUDE.md, Hooks & Skills Engineering.
Verwandte Posts: Claude Code Setup-Guide, Claude Code im Entwickler-Alltag, MCP-Server entwickeln.
Häufige Fragen
Was sind Claude Code Hooks?
Hooks sind Skripte, die Claude Code vor (Pre-Hook) oder nach (Post-Hook) bestimmten Tool-Aufrufen automatisch ausführt. Beispiele: vor jedem git commit Tests laufen lassen, nach jedem npm install Security-Audit triggern, vor jedem kubectl apply den Diff anzeigen.
Wie unterscheidet sich ein Pre-Hook von einem Post-Hook?
Pre-Hooks laufen BEVOR ein Tool ausgeführt wird und können die Ausführung verhindern (durch Non-Zero-Exit). Sie sind richtig für Validation und Permission-Checks. Post-Hooks laufen NACH der Ausführung und reagieren auf das Ergebnis. Sie sind richtig für Logging, Notifications und Cleanup.
Wo werden Hooks konfiguriert?
In der settings.json im .claude/-Ordner. Hooks sind Shell-Skripte (oder beliebige ausführbare Dateien), die mit Tool-Pattern-Matching getriggered werden. Skripte liegen typisch in .claude/hooks/ und werden ins Git committed - damit das ganze Team dieselbe Konfiguration hat.
Können Hooks die Tool-Ausführung blockieren?
Pre-Hooks ja: ein Non-Zero-Exit-Code bricht die Ausführung ab und gibt dem Agent die Fehlermeldung. Damit lassen sich harte Security- oder Quality-Gates implementieren. Post-Hooks können nicht mehr blockieren - die Aktion ist bereits passiert.
Verlangsamen Hooks den Workflow merklich?
Bei kurzen Hooks (Format-Check, Lint, Pre-Commit-Validation) typisch unter 1 Sekunde - kaum spürbar. Bei längeren Hooks (vollständige Test-Suites) lohnt sich Caching oder Inkrementelle Variante. Ein gut designter Hook fällt im Workflow nicht auf, fängt aber 80% der Probleme.