Claude Code Hooks sind eine der mächtigsten, aber am wenigsten genutzten Funktionen im gesamten Claude Code Ökosystem. Während die meisten Entwickler Claude Code als interaktiven Assistenten einsetzen, der auf Anweisungen wartet, ermöglichen Hooks eine völlig andere Arbeitsweise: automatische, event-getriebene Aktionen, die im Hintergrund ablaufen — ohne dass du manuell eingreifen musst.
In diesem Artikel erkläre ich dir alle vier Hook-Typen, zeige dir konkrete Konfigurationsbeispiele und gebe dir praxiserprobte Rezepte für deinen Team-Workflow mit. Los geht’s.
1. Hooks Grundlagen — Was sind Claude Code Hooks?
Claude Code Hooks sind Shell-Kommandos oder Skripte, die zu definierten Zeitpunkten im Lebenszyklus einer Claude-Code-Sitzung automatisch ausgeführt werden. Sie funktionieren ähnlich wie Git-Hooks oder CI/CD-Pipeline-Steps: Du definierst einen Trigger-Zeitpunkt, Claude Code ruft dein Skript auf — mit relevanten Informationen über den aktuellen Kontext über STDIN.
Kernprinzip: Hooks sind deterministische Automatisierungen, die du kontrollierst. Claude Code selbst ist nicht-deterministisch — Hooks sind es. Das macht sie ideal für Quality Gates, Sicherheitsprüfungen und Notifications.
Die vier Hook-Typen im Überblick
⚠
PreToolUse
Vor jeder Tool-Ausführung — kann blockieren
✅
PostToolUse
Nach jeder Tool-Ausführung — reagiert auf Ergebnis
🔔
Notification
Bei Benachrichtigungen — z.B. lange laufende Tasks
📩
PreCompact
Vor Kontext-Komprimierung — Checkpoint erstellen
Konfiguration via settings.json
Hooks werden in der Claude Code Konfigurationsdatei ~/.claude/settings.json (global) oder .claude/settings.json (projekt-lokal) definiert. Die projekt-lokale Konfiguration hat Vorrang und wird im Team geteilt.
~/.claude/settings.json (global)
// Claude Code globale Hook-Konfiguration
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/pre-write-lint.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/format-on-save.sh"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/notify.sh"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/pre-compact-checkpoint.sh"
}
]
}
]
}
}
Wie Hook-Skripte Informationen empfangen
Claude Code übergibt Kontext-Informationen über STDIN als JSON. Dein Skript liest diese Daten, verarbeitet sie und kann über den Exit-Code steuern, ob Claude Code weiterfährt (Exit 0) oder die Aktion abbricht (Exit 1 bei PreToolUse).
STDIN-Datenformat (PreToolUse Beispiel)
{
"tool": "Write",
"tool_input": {
"file_path": "/home/user/project/src/app.ts",
"content": "... Dateiinhalt ..."
},
"session_id": "sess_abc123",
"cwd": "/home/user/project"
}
| Hook-Typ |
Wann ausgelöst |
Kann blockieren? |
Typischer Einsatz |
| PreToolUse |
Vor Tool-Ausführung |
✓ Ja (Exit 1) |
Lint, Security-Check, Approval |
| PostToolUse |
Nach Tool-Ausführung |
✗ Nein |
Format, Tests triggern, Logging |
| Notification |
Bei Benachrichtigungen |
✗ Nein |
Desktop/Slack/Sound |
| PreCompact |
Vor Kontext-Komprimierung |
✗ Nein |
Checkpoint, Memory-Save |
2. PreToolUse Hooks — Vor der Tool-Ausführung eingreifen
PreToolUse ist der mächtigste Hook-Typ: Er läuft bevor Claude Code ein Tool ausführt und kann die Ausführung vollständig blockieren. Das macht ihn ideal als Quality Gate: Kein Code wird geschrieben, der den Lint nicht besteht. Kein Bash-Kommando wird ausgeführt, das nicht in der Allowlist steht.
PreToolUse Lint vor jedem Write
Führe ESLint auf dem zu schreibenden Inhalt aus, bevor die Datei gespeichert wird. Blockiere bei Fehlern.
scripts/pre-write-lint.sh
#!/usr/bin/env bash
# PreToolUse Hook: Lint-Check vor Write/Edit
# Claude Code übergibt Kontext via STDIN als JSON
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool')
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
# Nur bei TypeScript/JavaScript Dateien prüfen
if [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]]; then
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
TMPFILE=$(mktemp /tmp/claude-lint-XXXXXX.ts)
echo "$CONTENT" > "$TMPFILE"
# ESLint auf dem temporären File ausführen
if ! npx eslint --no-eslintrc -c .eslintrc.json "$TMPFILE" --quiet 2>&1; then
rm -f "$TMPFILE"
# Exit 1 = Claude Code blockiert die Write-Aktion
echo "ESLint-Fehler gefunden! Write blockiert." >&2
exit 1
fi
rm -f "$TMPFILE"
fi
# Exit 0 = Aktion erlaubt
exit 0
Matcher — Welche Tools beobachten?
über das matcher-Feld in der settings.json kannst du mit regulären Ausdrücken steuern, bei welchen Tools der Hook ausgelöst wird. So vermeidest du, dass ein Lint-Check bei jedem Bash-Aufruf ausgeführt wird.
.claude/settings.json (Matcher-Beispiele)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"comment": "Nur bei Write-Tool auslösen",
"hooks": [{ "type": "command", "command": "./scripts/pre-write-lint.sh" }]
},
{
"matcher": "Edit|Write",
"comment": "Bei Edit UND Write auslösen (OR-Verknüpfung)",
"hooks": [{ "type": "command", "command": "./scripts/secrets-scan.sh" }]
},
{
"matcher": "Bash",
"comment": "Bash-Kommandos prüfen (rm -rf, sudo etc.)",
"hooks": [{ "type": "command", "command": "./scripts/bash-safety.sh" }]
}
]
}
}
PreToolUse Sicherheitsprüfung für Bash-Kommandos
Blockiere gefährliche Bash-Kommandos wie rm -rf, sudo dd oder Netzwerk-Tunneling-Befehle automatisch.
scripts/bash-safety.sh
#!/usr/bin/env bash
# PreToolUse Hook: Gefährliche Bash-Kommandos blockieren
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
# Verbotene Muster (Regex)
BLOCKED_PATTERNS=(
"rm -rf /"
"rm -rf \*"
"sudo dd"
"mkfs\."
"> /dev/sda"
"chmod -R 777 /"
"curl.*\| bash"
"wget.*\| sh"
)
for pattern in "${BLOCKED_PATTERNS[@]}"; do
if echo "$CMD" | grep -qP "$pattern"; then
echo "⚠ SICHERHEIT: Kommando '$pattern' blockiert!" >&2
# Audit-Log schreiben
echo "$(date -Iseconds) BLOCKED: $CMD" >> /var/log/claude-audit.log
exit 1
fi
done
exit 0
3. PostToolUse Hooks — Automatisch nach jeder Tool-Ausführung
PostToolUse läuft nach einer Tool-Ausführung und empfängt nicht nur den Input, sondern auch den Output des Tools. Das macht es möglich, auf Ergebnisse zu reagieren: Formatiere Code nach dem Speichern, führe Tests nach Änderungen aus oder protokolliere jeden Git-Commit automatisch.
PostToolUse Format-on-Save nach Edit
Prettier und ESLint-Fix laufen automatisch nach jedem Edit oder Write auf TypeScript/JavaScript-Dateien.
scripts/format-on-save.sh
#!/usr/bin/env bash
# PostToolUse Hook: Format-on-Save mit Prettier + ESLint
# Wird nach Edit und Write ausgelöst
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
# Nur für unterstützte Dateitypen
if [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx|json|css|scss|html)$ ]]; then
cd "$CWD"
# Prettier formatieren
if command -v prettier &>/dev/null; then
npx prettier --write "$FILE_PATH" --log-level warn
echo "✓ Prettier: $FILE_PATH formatiert"
fi
# ESLint auto-fix (nur JS/TS)
if [[ "$FILE_PATH" =~ \.(ts|tsx|js|jsx)$ ]] && command -v eslint &>/dev/null; then
npx eslint --fix "$FILE_PATH" --quiet 2>/dev/null || true
echo "✓ ESLint-Fix: $FILE_PATH bereinigt"
fi
fi
exit 0
PostToolUse Tests nach Dateiänderung
Vitest oder Jest führt automatisch den passenden Test für jede geänderte Datei aus — nur den zugehörigen Test, nicht die ganze Suite.
scripts/run-related-tests.sh
#!/usr/bin/env bash
# PostToolUse Hook: Betroffene Tests nach Dateiänderung ausführen
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
# Nur Source-Dateien triggern Tests
if [[ "$FILE_PATH" =~ src/.*\.(ts|tsx|js|jsx)$ ]]; then
cd "$CWD"
# Passende Test-Datei ableiten (src/foo.ts → tests/foo.test.ts)
BASE=$(basename "$FILE_PATH" | sed 's/\.\(ts\|tsx\|js\|jsx\)$//')
TEST_FILE=$(find tests/ -name "${BASE}.test.*" 2>/dev/null | head -1)
if [[ -n "$TEST_FILE" ]]; then
echo "▶ Starte Tests: $TEST_FILE"
# Vitest im Background (non-blocking für PostToolUse)
npx vitest run "$TEST_FILE" --reporter=verbose 2>&1 | tail -20 &
echo "◯ Tests gestartet (PID $!)"
fi
fi
exit 0
PostToolUse Git-Status nach Commit
Zeige automatisch den Git-Status und die letzten 3 Commits nach jedem git commit Bash-Kommando.
scripts/post-git-status.sh
#!/usr/bin/env bash
# PostToolUse Hook: Git-Status nach jedem Commit-Bash-Kommando
INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
# Nur bei git commit Kommandos reagieren
if echo "$CMD" | grep -q "git commit"; then
cd "$CWD"
echo ""
echo "=== 📊 Git-Status nach Commit ==="
git status --short
echo ""
echo "=== 📚 Letzte 3 Commits ==="
git log --oneline -3
echo "=================================="
# Audit-Log mit Commit-Hash
HASH=$(git rev-parse --short HEAD 2>/dev/null)
echo "$(date -Iseconds) COMMIT $HASH by claude-code" >> /var/log/claude-audit.log
fi
exit 0
4. Notification Hooks — Benachrichtigungen bei langen Tasks
Claude Code sendet automatisch Notifications, wenn es auf Eingabe wartet oder ein lang laufender Prozess abgeschlossen ist. Mit Notification Hooks kannst du diese Ereignisse nutzen, um Desktop-Notifications, Slack-Webhooks, Discord-Nachrichten oder Sound-Alerts auszulösen.
Notification Desktop-Benachrichtigung
Automatische Desktop-Popup-Nachricht unter Linux (notify-send) oder macOS (osascript), wenn Claude Code auf Eingabe wartet.
scripts/desktop-notify.sh
#!/usr/bin/env bash
# Notification Hook: Desktop-Notification bei Claude Code Events
INPUT=$(cat)
MSG=$(echo "$INPUT" | jq -r '.message // "Claude Code braucht Eingabe"')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // ""')
# Nachricht kürzen (max. 100 Zeichen)
MSG_SHORT=$(echo "$MSG" | cut -c1-100)
# Linux (GNOME/KDE)
if command -v notify-send &>/dev/null; then
DISPLAY=:0 notify-send \
"🤖 Claude Code" \
"$MSG_SHORT" \
--icon=dialog-information \
--expire-time=5000
fi
# macOS
if command -v osascript &>/dev/null; then
osascript -e "display notification \"$MSG_SHORT\" with title \"Claude Code\" sound name \"Funk\""
fi
# Sound-Fallback
if command -v paplay &>/dev/null; then
paplay /usr/share/sounds/freedesktop/stereo/message.oga 2>/dev/null || true
fi
exit 0
Notification Slack / Discord Webhook
Sende automatisch eine Slack- oder Discord-Nachricht, wenn ein langer AI-Task abgeschlossen ist — ideal für asynchrone Team-Workflows.
scripts/slack-notify.sh
#!/usr/bin/env bash
# Notification Hook: Slack Webhook bei Claude Code Events
INPUT=$(cat)
MSG=$(echo "$INPUT" | jq -r '.message // "Claude Code Notification"')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
# Slack Webhook URL aus Umgebungsvariable (NIEMALS hardcoden!)
SLACK_WEBHOOK="$SLACK_WEBHOOK_URL"
if [[ -z "$SLACK_WEBHOOK" ]]; then
exit 0 # Kein Webhook konfiguriert, still beenden
fi
# Slack-Payload aufbauen
PAYLOAD=$(jq -n \
--arg msg "🤖 *Claude Code* (Session: ${SESSION_ID:0:8}...)\n$MSG" \
'{text: $msg}')
# Webhook senden (non-blocking)
curl -s -X POST "$SLACK_WEBHOOK" \
-H 'Content-Type: application/json' \
-d "$PAYLOAD" &>/dev/null &
echo "Slack-Notification gesendet"
exit 0
Tipp: Discord-Webhooks funktionieren identisch — ersetze einfach SLACK_WEBHOOK_URL durch DISCORD_WEBHOOK_URL und nutze das Discord-Payload-Format mit "content" statt "text".
5. PreCompact Hooks — Vor der Kontext-Komprimierung
Wenn das Kontext-Fenster von Claude Code voll wird, komprimiert es automatisch ältere Konversationsteile. Der PreCompact Hook läuft bevor diese Komprimierung stattfindet — eine perfekte Gelegenheit, um wichtige Informationen zu sichern, Checkpoints zu erstellen oder den aktuellen Zustand zu protokollieren.
PreCompact Automatischer Checkpoint
Erstelle automatisch einen CHECKPOINT.md mit dem aktuellen Task-Status, bevor der Kontext komprimiert wird. So gehen keine wichtigen Informationen verloren.
scripts/pre-compact-checkpoint.sh
#!/usr/bin/env bash
# PreCompact Hook: Automatischer Checkpoint vor Kontext-Komprimierung
INPUT=$(cat)
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
TIMESTAMP=$(date +%Y%m%dT%H%M%S)
CHECKPOINT_DIR="$CWD/.work/checkpoints"
mkdir -p "$CHECKPOINT_DIR"
CHECKPOINT_FILE="$CHECKPOINT_DIR/$TIMESTAMP-pre-compact.md"
# Checkpoint mit Git-Status, offenen Dateien etc.
cat > "$CHECKPOINT_FILE" << EOF
# Checkpoint vor PreCompact
**Timestamp:** $(date -Iseconds)
**Session:** $SESSION_ID
**CWD:** $CWD
## Git-Status
\`\`\`
$(cd "$CWD" && git status --short 2>/dev/null || echo "Kein Git-Repo")
\`\`\`
## Letzte Commits
\`\`\`
$(cd "$CWD" && git log --oneline -5 2>/dev/null || echo "Keine Commits")
\`\`\`
## Offene Tasks (TODO/FIXME)
\`\`\`
$(cd "$CWD" && grep -rn "TODO\|FIXME" src/ 2>/dev/null | head -20 || echo "Keine offenen TODOs")
\`\`\`
EOF
echo "✓ Checkpoint erstellt: $CHECKPOINT_FILE"
# Checkpoint-Verzeichnis aufräumen (max. 10 letzte behalten)
ls -t "$CHECKPOINT_DIR"/*.md 2>/dev/null | tail -n +11 | xargs rm -f
exit 0
PreCompact Memory Save via CLI
Wichtige Erkenntnisse der aktuellen Session automatisch in das persistente Memory-System speichern, bevor der Kontext verloren geht.
scripts/pre-compact-memory.sh
#!/usr/bin/env bash
# PreCompact Hook: Wichtige Session-Infos in Memory speichern
INPUT=$(cat)
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
# Nur wenn memory CLI verfügbar
if ! command -v memory &>/dev/null; then
exit 0
fi
# Letzten Commit als Memory-Eintrag speichern
LAST_COMMIT=$(cd "$CWD" && git log --oneline -1 2>/dev/null)
if [[ -n "$LAST_COMMIT" ]]; then
memory save "PreCompact Checkpoint (Session ${SESSION_ID:0:8}): Letzter Commit in $(basename $CWD): $LAST_COMMIT" \
--agent "claude-code" 2>/dev/null || true
echo "✓ Memory-Save: $LAST_COMMIT"
fi
exit 0
Hinweis: PreCompact-Hooks können die Komprimierung nicht blockieren — sie laufen parallel. Halte deine Checkpoint-Skripte daher schnell (< 5 Sekunden). Langsamere Operationen sollten in den Hintergrund ausgelagert werden.
6. Praxis-Rezepte — Vollständige Team-Workflows
Die wirkliche Stärke von Hooks entfaltet sich, wenn du mehrere kombinierst. Hier sind produktionserprobte Rezepte für typische Team-Szenarien.
Rezept 1: TypeScript-Projekt mit Auto-QA
Für TypeScript-Projekte kombinieren wir PreToolUse (Type-Check vor Write), PostToolUse (Prettier nach Edit) und Notification (Slack bei Abschluss):
.claude/settings.json (TypeScript Full-QA)
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "./scripts/typecheck.sh"
},
{
"type": "command",
"command": "./scripts/secrets-scan.sh"
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "./scripts/bash-safety.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "./scripts/format-on-save.sh"
},
{
"type": "command",
"command": "./scripts/run-related-tests.sh"
}
]
},
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "./scripts/post-git-status.sh"
}
]
}
],
"Notification": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/desktop-notify.sh"
},
{
"type": "command",
"command": "./scripts/slack-notify.sh"
}
]
}
],
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "./scripts/pre-compact-checkpoint.sh"
}
]
}
]
}
}
Rezept 2: Secrets-Scanner als PreToolUse Gate
Verhindere automatisch, dass Claude Code versehentlich API-Keys, Passwörter oder Private Keys in Dateien schreibt:
scripts/secrets-scan.sh
#!/usr/bin/env bash
# PreToolUse Hook: Secrets-Scanner (API-Keys, Passwörter, Private Keys)
INPUT=$(cat)
CONTENT=$(echo "$INPUT" | jq -r '.tool_input.content // ""')
# Secret-Patterns (AWS Keys, Generic API Keys, Private Keys etc.)
SECRET_PATTERNS=(
"AKIA[0-9A-Z]{16}" # AWS Access Key
"sk-[a-zA-Z0-9]{48}" # OpenAI API Key
"ghp_[a-zA-Z0-9]{36}" # GitHub Personal Access Token
"-----BEGIN (RSA |EC )?PRIVATE KEY" # Private Key
"password\s*[:=]\s*['\"][^'\"]{8,}" # Hardcoded Password
"api_key\s*[:=]\s*['\"][^'\"]{16,}" # Generic API Key
)
FOUND_SECRETS=0
for pattern in "${SECRET_PATTERNS[@]}"; do
if echo "$CONTENT" | grep -qP "$pattern" 2>/dev/null; then
echo "🔒 SECRET GEFUNDEN: Pattern '$pattern' erkannt!" >&2
echo "Hardcoded Secrets sind verboten! Nutze Umgebungsvariablen." >&2
FOUND_SECRETS=1
fi
done
if [[ $FOUND_SECRETS -eq 1 ]]; then
# Audit-Log Eintrag
echo "$(date -Iseconds) SECRET-BLOCKED by claude-hook" >> /var/log/claude-audit.log
exit 1 # Write blockieren!
fi
exit 0
Rezept 3: TypeScript Type-Check vor Write
scripts/typecheck.sh
#!/usr/bin/env bash
# PreToolUse Hook: TypeScript Type-Check auf betroffene Dateien
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // ""')
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
# Nur TypeScript-Dateien prüfen
if [[ "$FILE_PATH" =~ \.(ts|tsx)$ ]]; then
cd "$CWD"
# Schneller tsc-Check (ohne Emit)
if ! npx tsc --noEmit --strict 2>&1 | grep -q "error TS"; then
echo "✓ TypeScript: Keine Typfehler"
else
ERRORS=$(npx tsc --noEmit --strict 2>&1 | grep "error TS" | head -5)
echo "❌ TypeScript-Fehler gefunden:" >&2
echo "$ERRORS" >&2
# Warnung ausgeben, aber NICHT blockieren (nur informieren)
# Für hartes Blockieren: exit 1
fi
fi
exit 0
Rezept 4: Audit-Log für Compliance
Für Teams mit Compliance-Anforderungen (SOC 2, ISO 27001) ist ein vollständiger Audit-Log aller Claude Code Aktionen wichtig:
scripts/audit-log.sh
#!/usr/bin/env bash
# PostToolUse Hook: Vollständiges Audit-Log aller Claude Code Aktionen
INPUT=$(cat)
TOOL=$(echo "$INPUT" | jq -r '.tool')
SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "unknown"')
CWD=$(echo "$INPUT" | jq -r '.cwd // "."')
USER=$(whoami)
TIMESTAMP=$(date -Iseconds)
# Tool-spezifische Details
case "$TOOL" in
Write|Edit)
DETAIL=$(echo "$INPUT" | jq -r '.tool_input.file_path // "unknown"')
;;
Bash)
DETAIL=$(echo "$INPUT" | jq -r '.tool_input.command // ""' | cut -c1-80)
;;
*)
DETAIL=$(echo "$INPUT" | jq -r '.tool_input | keys | join(",")' 2>/dev/null || echo "")
;;
esac
# JSON-Audit-Log Eintrag
LOG_ENTRY=$(jq -n \
--arg ts "$TIMESTAMP" \
--arg user "$USER" \
--arg session "$SESSION_ID" \
--arg tool "$TOOL" \
--arg detail "$DETAIL" \
--arg cwd "$CWD" \
'{timestamp: $ts, user: $user, session: $session, tool: $tool, detail: $detail, cwd: $cwd}')
LOG_FILE="/var/log/claude-audit-$(date +%Y%m%d).jsonl"
echo "$LOG_ENTRY" >> "$LOG_FILE"
exit 0
Best Practices für Hook-Skripte
- Immer Exit-Codes korrekt setzen: Exit 0 = OK, Exit 1 = Fehler/Blockieren (nur bei PreToolUse wirksam)
- Fehler nie schlucken: Nutze
set -euo pipefail oder explizite Fehlerbehandlung
- Performance: PreToolUse-Hooks blockieren Claude Code bis zur Rückgabe — halte sie unter 5 Sekunden
- Secrets aus Umgebungsvariablen: Niemals API-Keys hardcoden — nutze
$SLACK_WEBHOOK_URL etc.
- STDIN immer lesen: Auch wenn du die Daten nicht brauchst — Claude Code wartet sonst
- Logging: Schreibe wichtige Events in
/var/log/claude-audit.log für Nachvollziehbarkeit
- Idempotenz: PostToolUse-Hooks können mehrfach für dieselbe Datei laufen — Skripte müssen das vertragen
Hook-Debugging: Füge set -x am Anfang deines Skripts ein und leite STDERR in eine Logdatei: exec 2>>/tmp/claude-hook-debug.log. So siehst du genau, was dein Hook tut.
Zusammenfassung: Wann welcher Hook?
| Use Case |
Hook-Typ |
Matcher |
Blocking? |
| Lint-Check vor Write |
PreToolUse |
Write |
✓ Ja |
| Secrets-Scanner |
PreToolUse |
Write|Edit |
✓ Ja |
| Bash-Kommando-Whitelist |
PreToolUse |
Bash |
✓ Ja |
| Format-on-Save |
PostToolUse |
Edit|Write |
✗ Nein |
| Tests nach Änderung |
PostToolUse |
Edit|Write |
✗ Nein |
| Audit-Log |
PostToolUse |
(kein Matcher) |
✗ Nein |
| Desktop-Notification |
Notification |
— |
✗ Nein |
| Slack/Discord Alert |
Notification |
— |
✗ Nein |
| Checkpoint erstellen |
PreCompact |
— |
✗ Nein |
| Memory-Save |
PreCompact |
— |
✗ Nein |
Claude Code Hooks sind ein Game-Changer für Teams, die AI-Coding-Tools professionell einsetzen wollen. Statt darauf zu hoffen, dass Claude Code immer die richtigen Entscheidungen trifft, definierst du klare, deterministischen Regeln — und gibst dem AI-Assistenten damit feste Leitplanken, die automatisch greifen.
Bereit für den nächsten Schritt?
Starte deinen kostenlosen Trial und baue deine ersten Claude Code Hooks mit vorgefertigten Skript-Templates — für dein Team, in Minuten einsatzbereit.
Kostenlos starten →
Kein Setup, kein Kreditkartenanforderung — direkt loslegen.