Claude Code Hooks — Shell-Befehle automatisch bei Sitzungs- und Tool-Ereignissen ausführen
Hooks in settings.json fügen Shell-Befehle bei Sitzungsstart, vor/nach Tool-Aufrufen und beim Sitzungsende ein. Fünf Beispiele: Sicherheitsblockierung, Auto-Formatierung, Logging, externe Benachrichtigungen.
Claude Codes Hooks — definiert unter dem hooks-Schlüssel in settings.json — ermöglichen es, Shell-Befehle automatisch bei Sitzungsstart, vor und nach Tool-Aufrufen sowie beim Sitzungsende auszuführen. So lässt sich abfangen, was die KI als Nächstes tun wird, im Nachgang aufräumen oder am Ende bereinigen. Ein leistungsstarker Erweiterungspunkt.
Ich denke, was den Hook-Mechanismus so wirkungsvoll macht, ist nicht die Automatisierung selbst, sondern die Fähigkeit, Verhalten durchzusetzen — weil ein PreToolUse-Hook, der gefährliche Befehle blockiert, zuverlässiger ist als jede Erinnerung. Früher musste man solche Sicherheitsprüfungen manuell vornehmen; heute lassen sie sich als Code formalisieren, statt als Konvention zu existieren.
Dieser Leitfaden richtet sich an Claude Code 1.x und die offizielle Hooks-Spezifikation. Er baut auf Claude Code einrichten auf und fügt eine weitere Automatisierungs- und Sicherheitsebene hinzu.
TL;DR
| Hook-Ereignis | Wird ausgelöst | Typische Verwendung |
|---|---|---|
SessionStart | Direkt nach Sitzungsbeginn | Speicher-Sync-Hinweis, Umgebungsvalidierung |
UserPromptSubmit | Direkt nach Eingabe eines Prompts | Slash-Command-Erkennung, Logging, Nachbearbeitung |
PreToolUse | Direkt vor einem Tool-Aufruf | Gefährliche Befehle blockieren, Secret-Dateien schützen |
PostToolUse | Direkt nach einem Tool-Aufruf | Auto-Formatierung (prettier), Lint, Nebeneffekt-Logging |
Stop | Direkt vor Sitzungsende | Änderungszusammenfassung, Backup, Benachrichtigung |
Wichtige Regeln:
- Exit-Code 2 blockiert den Tool-Aufruf (nur bei
PreToolUse). - Der JSON-Payload wird per stdin übergeben — mit
jqparsen. - Hooks sind Shell-Befehle — auf Unterschiede zwischen Betriebssystemen achten (macOS/Linux/Windows WSL).
Voraussetzungen
- Claude Code 1.x installiert — Claude Code einrichten
jqinstalliert (brew install jq/apt install jq/winget install jqlang.jq)- Schreibzugriff auf
settings.json(üblicherweise~/.claude/settings.json)
Basis-Konfiguration herunterladen
Die von devAlice empfohlene Fünf-Hook-Basis inklusive Deny/Allow-Liste — herunterladen und an die eigene Umgebung anpassen.
settings.json# 1. Herunterladen
curl -fsSL https://devalice.jaceclub.com/assets/ai-agents/claude-code-hooks/settings.example.json -o settings.json
# 2. SHA-256 prüfen
shasum -a 256 settings.json
# Erwartet: 30c1d4117a935cac03b601c71125c11b71733c8c59517ab129fd8913cfb8a69e
# 3. Prüfen (insbesondere PreToolUse-Hooks)
less settings.json
# 4. An die richtige Stelle kopieren (macOS/Linux)
mkdir -p ~/.claude
cp settings.json ~/.claude/settings.json⚠️ Achtung: Hooks führen beliebige Shell-Befehle aus. Immer prüfen, bevor fremde Hooks übernommen werden. Dieses Beispiel ist eine sichere Fünf-Hook-Basis — vor dem Einsatz dennoch für die eigene Umgebung anpassen.
1. Hook-Struktur — 5 Min.
Der hooks-Schlüssel in ~/.claude/settings.json:
{
"hooks": {
"EventName": [
{
"matcher": "Regex oder Tool-Name", // optional
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'eigentlicher Shell-Befehl'"
}
]
}
]
}
}Matching-Regeln:
- Kein
matcher→ löst bei jedem Ereignis aus matcher= Tool-Name (Bash,Write,Edit,Read…) → nur bei diesem Toolmatcher= Regex (^/(?:ej|euijinpro)\\b) → fürUserPromptSubmit, gleicht den Prompt-Text ab
Per stdin eingehender JSON-Payload:
{
"session_id": "...",
"transcript_path": "/path/to/transcript.jsonl",
"cwd": "/Users/...",
"tool_name": "Bash",
"tool_input": { "command": "..." },
"tool_response": { /* nur PostToolUse */ }
}Mit jq -r .tool_input.command extrahieren.
2. SessionStart — Kontext laden
Der einfachste Hook. Eine Zeile ausgeben, sobald die Sitzung beginnt:
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'printf \"[Sitzungsstart] %s\\nSpeicher-Sync: aktuell (übersprungen)\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
}
]
}
]Die Stdout-Ausgabe wird als System-Nachricht angezeigt. Typische Verwendungsmöglichkeiten:
- Automatisches Laden von Speicherdateien auslösen
- Projektspezifischen Kontext ausgeben
- Git-Status-Zusammenfassung zu Sitzungsbeginn
# Erweitertes Beispiel
command='/bin/bash -lc "echo [Sitzungsstart]; git -C \"$PWD\" status -s; git -C \"$PWD\" log --oneline -3"'3. PreToolUse — Gefährliche Befehle blockieren (wertvollster Hook)
exit 2 blockiert den Aufruf. Selbst wenn die KI einen gefährlichen Shell-Befehl ausführen möchte, wird er gestoppt.
3.1 Gefährliche Befehlsmuster
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'cmd=$(jq -r .tool_input.command); case \"$cmd\" in *\"rm -rf /\"*|*\"sudo rm\"*|*\":(){\"*) echo \"gefährlicher Befehl blockiert: $cmd\" >&2; exit 2;; esac'"
}
]
}
]Blockierte Muster:
rm -rf /— Systemzerstörungsudo rm— unkontrolliertes Löschen (bei Bedarf per Allow-List freigeben):(){— Fork-Bombe (:(){ :|: & };:)
3.2 Bearbeitungen sensibler Dateien blockieren
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in */.env|*/credentials.json|*/.aws/credentials) echo \"Secret-Datei-Bearbeitung blockiert: $path\" >&2; exit 2;; esac'"
}
]
}Verhindert versehentliche Bearbeitungen von .env, credentials.json, ~/.aws/credentials und ähnlichen Secret-Dateien.
3.3 Bestimmte Verzeichnisse schützen
command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); case \"\$path\" in /etc/*|/System/*) echo \"Systempfad blockiert: \$path\" >&2; exit 2;; esac"'4. PostToolUse — Auto-Formatierung / Lint / Log
Aufräumen nach dem Tool-Aufruf.
4.1 Prettier-Auto-Formatierung
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in *.ts|*.tsx|*.js|*.jsx) prettier --write \"$path\" 2>/dev/null || true ;; esac'"
}
]
}
]Nach jedem KI-Write/Edit formatiert prettier automatisch nach. Das kostet ca. 0,5 s pro Bearbeitung.
4.2 Geänderte Dateien aufzeichnen
command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); echo \"[geändert] \$(date +%T) \$path\" >> ~/.claude/change.log"'Erstellt eine zeitgeordnete Aufzeichnung jeder Dateiänderung in der Sitzung.
4.3 Typ-Prüfung / Lint (aufwändige Aufgaben in den Stop-Hook verschieben)
tsc --noEmit bei jeder Bearbeitung ist zu teuer. Besser im Stop-Hook (§6) bündeln.
5. UserPromptSubmit — Slash-Command-Erkennung / Nachbearbeitungs-Trigger
"UserPromptSubmit": [
{
"matcher": "^/(?:ej|euijinpro)\\b",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'echo \"[ej skill] Prompt für euijin.pro-Logging vorgemerkt\" >&2'"
}
]
}
]- Slash-Commands erkennen und externe Systeme auslösen (Slack, separates Logging)
- Bestimmte Schlüsselwörter automatisch markieren (
urgent,bug) - Token-Budget-Schwellenwerte prüfen
6. Stop — Aufräumen am Sitzungsende
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'printf \"\\n[Sitzungsende] %s\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
}
]
}
]Typische Verwendungsmöglichkeiten:
- Git-Status-Zusammenfassung geänderter Dateien
- Backup (
rsyncodergit stash) - Externe Shutdown-Benachrichtigung
- Einmalige Typ-Prüfung / Build
command='/bin/bash -lc "if git diff --quiet; then echo \"[sauber]\"; else echo \"[Änderungen]\"; git diff --stat; fi"'7. permissions — Deny-List / Allow-List
Von Hooks getrennt, aber der wichtigste Sicherheitsschlüssel in settings.json.
"permissions": {
"deny": [
"Bash(rm -rf /:*)",
"Bash(sudo rm:*)",
"Read(.env)",
"Read(.env.local)",
"Read(.aws/credentials)",
"Read(*/credentials.json)"
],
"allow": [
"Bash(git status:*)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(pnpm:*)",
"Bash(npm:*)",
"Bash(node:*)"
]
}denygewinnt bedingungslos — ergänzt die Hook-Blockierungenallowgenehmigt automatisch ohne Rückfrage (für häufige, sichere Befehle)- Alles andere löst jedes Mal eine Benutzerabfrage aus
Die Hooks in diesem Leitfaden überschneiden sich mit deny — Defense in Depth.
8. settings.json — Speicherorte und Priorität
| Speicherort | Geltungsbereich | Hinweise |
|---|---|---|
~/.claude/settings.json | Benutzer-global | Gilt für alle Projekte |
<project>/.claude/settings.json | Projekt | Wird beim Projektstart mit den Benutzereinstellungen zusammengeführt |
<project>/.claude/settings.local.json | Projekt + benutzerlokal | Empfehlung: in .gitignore aufnehmen |
Zusammenführungsreihenfolge: Benutzer → Projekt → Projekt.local. Spätere Einstellungen überschreiben frühere.
Hooks werden angehängt, nicht zusammengeführt — 5 Benutzer-Hooks + 3 Projekt-Hooks = alle 8 laufen.
9. Debugging — wenn der Hook nicht auslöst
9.1 stdin-Payload prüfen
Temporärer Debug-Hook:
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'cat > /tmp/claude-hook-debug.json'"
}
]
}→ cat /tmp/claude-hook-debug.json | jq .
9.2 Exit-Codes testen
command='/bin/bash -lc "echo dbg >&2; exit 2"'→ Prüfen ob Blockierung (PreToolUse) funktioniert.
9.3 Hook-Auslösung bestätigen
command='/bin/bash -lc "echo \"[Hook ausgelöst $$ $(date +%T)]\" >> ~/.claude/hook-trace.log"'Log nach einer Sitzung prüfen.
9.4 JSON-Syntaxfehler
settings.json vergibt keine Fehler — fehlende Kommas und übersehene Escapes sind häufige Fallen. Mit jq . ~/.claude/settings.json validieren.
10. Fehlerbehebung
„command not found: jq"
brew install jq (macOS) / sudo apt install jq (Linux) / winget install jqlang.jq (Windows).
Hook löst aus, tut aber nichts
commandmuss einzeilig sein. Für mehrzeilige Befehle\\nverwenden oder ein separates Skript aufrufen.- Shell-Escapes beachten —
\"ist innerhalb von JSON doppelt escaped.
PreToolUse exit 2 wird ignoriert
- Sicherstellen, dass
exit 2tatsächlich die letzte Zeile des Shell-Befehls erreicht. - In einer
set -e-Umgebung kanncase ;;mit exit 0 enden — stattdessenexit 2direkt im gematchten Branch setzen.
Prettier läuft nicht
- Prüfen, ob prettier im Projektstamm installiert ist (
pnpm prettier --version). - Der Hook-PATH enthält möglicherweise kein
node_modules/.bin—npx prettieroder den absoluten Pfad verwenden.
Änderungen an settings.json werden nicht übernommen
- Ein Sitzungsneustart ist erforderlich. Alternativ
/config reload(falls unterstützt). - Bei JSON-Syntaxfehlern wird der Hook-Abschnitt lautlos deaktiviert. Mit
jq .validieren.
Nächste Schritte
- Claude Code einrichten — Voraussetzung
- Agent Runner — autonome Ausführung in Kombination mit Hooks
- MCP Servers — Rollenunterschiede gegenüber Hooks
- Multi-Agenten-Workflow — Subagenten integrieren
Referenzen
Changelog
- 2026-05-12 — Erster Entwurf (devAlice M2 Seed-Erweiterung)