Claude Code Hooks sind Shell-Befehle, die Claude Code an festen Punkten seines Ablaufs ausführt: beim Sitzungsstart, vor oder nach einem Werkzeugaufruf, bei einer Freigabe-Anfrage oder wenn Claude fertig ist. Der Unterschied zu einer Regel in der CLAUDE.md ist die Verbindlichkeit: Eine Regel in der CLAUDE.md liest Claude und hält sich meistens daran. Ein Hook läuft immer, weil nicht das Modell entscheidet, sondern Claude Code selbst. Du trägst ihn als JSON in eine Einstellungsdatei ein, und ab dann gilt er in jeder Sitzung, ohne dass du daran denken musst.
Ich nutze Claude Code seit seinem Erscheinen täglich und betreibe mein Business mit einer KI-Belegschaft auf Claude-Basis. Jeder Mitarbeiter darin läuft im Terminal, und jede Freigabe, die einer von ihnen braucht, geht bei mir zuerst durch einen Hook. Alles zu Claude sammle ich auf der Seite Claude; wenn du Claude Code noch nicht kennst, fängst du besser mit Claude Code: Anleitung für Einsteiger an.
Claude Code Hooks erklärt: warum eine CLAUDE.md-Regel nicht reicht
Anthropic beschreibt Hooks in der Hooks-Anleitung so: „Hooks are user-defined shell commands. Claude Code runs them at specific points in its lifecycle, which gives you deterministic control: certain actions always happen rather than relying on the LLM to choose to run them." Der entscheidende Begriff ist „deterministic". Die Best Practices ergänzen: „Unlike CLAUDE.md instructions which are advisory, hooks are deterministic and guarantee the action happens."
Der Unterschied steckt im Wort „advisory": Die CLAUDE.md geht an das Modell, und das Modell entscheidet, wie es sie umsetzt; eine Garantie für jede Sitzung gibt die Doku nicht. Was in CLAUDE.md: das Gedächtnis von Claude Code steht, ist Kontext, kein Gesetz. Ein Hook ist das Gesetz. Anthropic formuliert die Regel dafür knapp: „Use hooks for actions that must happen every time with zero exceptions."
Die Faustregel, die ich daraus für mich abgeleitet habe: Alles, was Claude wissen soll, gehört in die CLAUDE.md. Alles, was ohne Ausnahme passieren muss, gehört in einen Hook. Und was Claude gar nicht erst dürfen soll, gehört in die Permission-Regeln der Einstellungen. Die drei Ebenen im Vergleich:
Wie ein Hook funktioniert: Ereignis, Matcher, Befehl, Exit-Code
Ein Hook besteht laut Hooks-Referenz aus vier Teilen. Das Ereignis sagt, wann er läuft, zum Beispiel PreToolUse vor jedem Werkzeugaufruf. Der Matcher grenzt ein, bei welchen Werkzeugen: Bash, Edit|Write oder ein regulärer Ausdruck wie mcp__.*; ein leerer Matcher oder * heißt alle. Der Befehl ist ein Shell-Kommando oder ein Skript. Und der Exit-Code des Befehls sagt Claude Code, was als Nächstes passiert.
Die Exit-Codes sind das, was du dir wirklich merken musst. Exit 0 heißt Erfolg: Was das Skript auf stdout schreibt, liest Claude Code als JSON aus, und bei UserPromptSubmit und SessionStart landet reiner Text als Kontext bei Claude. Exit 2 heißt blockierender Fehler: Bei PreToolUse unterbleibt der Werkzeugaufruf, bei UserPromptSubmit wird die Eingabe verworfen, bei Stop arbeitet Claude weiter, statt aufzuhören. Was das Skript auf stderr schreibt, bekommt Claude als Begründung. Jeder andere Exit-Code blockiert nicht; steht gültiges JSON in stdout, entscheidet das JSON.
Seine Eingabe bekommt das Skript als JSON auf stdin: unter anderem session_id, cwd, hook_event_name und bei Werkzeug-Ereignissen tool_name und tool_input; die vollständige Liste je Ereignis steht in der Referenz. Dazu setzt Claude Code die Umgebungsvariable CLAUDE_PROJECT_DIR.
Wo der Hook steht, entscheidet seine Reichweite: ~/.claude/settings.json gilt für alle deine Projekte, .claude/settings.json im Projekt wird per Git geteilt, .claude/settings.local.json bleibt lokal. Dazu kommen Hooks aus Plugins und aus dem Frontmatter von Skills und Subagents; alle Ebenen werden zusammengeführt. Der Befehl /hooks zeigt sie nur an, angelegt werden sie in der JSON-Datei. Anthropic empfiehlt, Claude den Hook schreiben zu lassen, etwa mit „Write a hook that blocks writes to the migrations folder".
Die sieben Ereignisse, die du im Alltag brauchst
Die Referenz listet, Stand September 2026, über dreißig Ereignisse, vom Sitzungsstart bis zum Modellwechsel. Für den Anfang reichen sieben:
SessionStart läuft beim Start, beim Fortsetzen und nach einer Kompaktierung. Gut, um verlorenen Kontext neu zu setzen.
UserPromptSubmit läuft bei jeder Eingabe, ohne Matcher. Damit prüfst oder ergänzt du, was du eintippst.
PreToolUse läuft vor jedem Werkzeugaufruf und ist das Ereignis, mit dem du Dateien wirklich schützen kannst.
PostToolUse läuft nach dem Werkzeug. Blockieren geht dann nicht mehr, prüfen und melden schon.
PermissionRequest läuft, wenn Claude Code eine Freigabe braucht. Blockieren per Exit 2 geht hier laut Referenz nicht, die Entscheidung kommt als JSON zurück. Das ist das Ereignis, auf dem meine Belegschaft aufbaut.
Stop läuft, wenn Claude fertig ist. Mit Exit 2 schickst du es zurück an die Arbeit.
Notification läuft, wenn Claude Code dich braucht, zum Beispiel bei permission_prompt oder idle_prompt.
Neben Shell-Befehlen kennt die Referenz die Hook-Typen http, mcp_tool, prompt und agent; command bleibt die nachvollziehbarste Wahl, weil du das Skript ohne Claude testen kannst.
Claude Code Hooks Tutorial: der erste Hook aus der offiziellen Doku
Das Beispiel, das ich jedem Einsteiger empfehle, kommt direkt aus der Hooks-Anleitung von Anthropic: ein Schutz für Dateien, die Claude nicht anfassen soll. Das Skript liegt unter .claude/hooks/protect-files.sh und muss mit chmod +x ausführbar sein:
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
FILE_PATH="${FILE_PATH//\\//}"
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")
for pattern in "${PROTECTED_PATTERNS[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
exit 2
fi
done
exit 0
Das Skript liest die JSON-Eingabe, holt den Dateipfad mit jq heraus und vergleicht ihn mit einer Liste geschützter Muster. Trifft eines zu, schreibt es die Begründung auf stderr und endet mit Exit 2; Claude Code bricht den Schreibzugriff ab. Eingehängt wird das Skript in .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
}
]
}
]
}
}
Der Matcher Edit|Write sorgt dafür, dass der Hook nur bei Schreibzugriffen läuft, nicht bei jedem Read oder Bash. Zum Testen bittest du Claude, eine Zeile in die .env zu schreiben; die Antwort muss die Blockade nennen. Das zweite Beispiel aus derselben Anleitung löst ein Problem, das jeder mit langen Sitzungen kennt: Nach einer Kompaktierung sind Kontextregeln weg. Ein SessionStart-Hook mit Matcher compact setzt sie neu:
{
"hooks": {
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{
"type": "command",
"command": "echo 'Reminder: use Bun, not npm. Run bun test before committing.'"
}
]
}
]
}
}
Was das echo ausgibt, landet bei Exit 0 als Kontext bei Claude. Das ist die einfachste Form eines Hooks: kein Skript, kein jq, eine Zeile Text, die nach jeder Kompaktierung sicher wieder da ist.
Ein Hook aus meiner Belegschaft: Freigaben landen im Mitarbeiter-OS
Meine KI-Mitarbeiter laufen als Claude-Code-Sitzungen im Terminal, jeder in seinem eigenen Ordner. Das Problem, das ich mit Hooks gelöst habe, ist praktisch: Ich sitze nicht in jedem Terminal. Conny und Ralf laufen regelmäßig in langen Sessions, und wenn einer eine Freigabe braucht, will ich das an einem Ort sehen und dort entscheiden.
Dafür habe ich in meiner ~/.claude/settings.json, Stand September 2026, sechs Ereignisse eingehängt: SessionStart, UserPromptSubmit, Stop, PermissionRequest, SessionEnd und PostToolUse. Alle sechs rufen dasselbe Shell-Skript mos-hook.sh auf, ohne Matcher, also für jedes Werkzeug. Das Skript meldet jedes Ereignis per HTTP-POST an mein Mitarbeiter-OS, einen lokalen Dienst auf Port 3210 mit dem Endpunkt /api/hooks. So sehe ich in einer Oberfläche, welcher Mitarbeiter gerade startet, arbeitet, fertig ist oder auf mich wartet.
Der wichtigste Fall ist PermissionRequest. Fragt ein Mitarbeiter nach einer Freigabe, wartet das Skript bis zu 25 Sekunden auf eine Antwort aus dem OS (in der settings.json stehen 30 Sekunden als Limit). Entscheide ich in dieser Zeit im OS, antwortet der Dienst mit hookSpecificOutput, und das Skript gibt diese Entscheidung an Claude Code zurück. Antwortet das OS nicht rechtzeitig, erscheint der normale Freigabe-Dialog im Terminal. Für alle anderen Ereignisse liegt das Zeitlimit bei 2 Sekunden, damit kein Mitarbeiter auf mein OS wartet.
Zwei Entscheidungen im Skript waren wichtig. Erstens endet es immer mit Exit 0, egal ob das OS erreichbar ist: Ein Hook, der die Arbeit blockiert, weil ein Hilfsdienst nicht läuft, wäre schlimmer als gar kein Hook. Zweitens erkennt das Skript Läufe, die das OS selbst gestartet hat, an Umgebungsvariablen (PASEO_AGENT_ID, MOS_RUN_ID) und überspringt sie, sonst würden sich OS und Hook gegenseitig Ereignisse zuschieben. Dass das im Betrieb funktioniert, habe ich mir im September 2026 live nachgewiesen und im Repo dokumentiert. Die Freigabe selbst erteile ich; der jeweilige Mitarbeiter dokumentiert sie mit Zeitpunkt.
Genauso wichtig ist die andere Richtung: was bei mir bewusst kein Hook ist. Secret-Scanning zum Beispiel. Ich hatte im Kopf, dass gitleaks bei mir automatisch vor jedem Commit läuft. Als Sebastian, mein SEO-Mitarbeiter, das im September 2026 nachgeprüft hat, war davon nichts da: kein Git-Hook, kein core.hooksPath, kein husky, in keinem der geprüften Repositories. Vorhanden ist gitleaks per Homebrew und eine Zeile im Regelwerk meiner Belegschaft: Vor jedem Push gitleaks git . --staged laufen lassen. Das ist eine Regel für die Mitarbeiter, keine Automatik. In einem Kundenprojekt läuft gitleaks zusätzlich als Job in der CI, also serverseitig nach dem Push und nicht lokal davor.
Die Lehre daraus passt zur Faustregel oben. In einen Hook gehört, was an einem festen Punkt im Ablauf von Claude Code passieren muss und sich dort in Sekunden entscheiden lässt. Eine Regel bleibt, was außerhalb einer Sitzung passiert oder länger dauert, als ein Hook warten darf: Ein Push geschieht nicht zwingend in einer Claude-Code-Sitzung, und ein Scan über ein ganzes Repository ist kein Zwei-Sekunden-Vorgang. Regel und CI-Job zusammen decken deshalb mehr ab als ein Hook allein. Glaubst du, bei dir laufe etwas automatisch, sieh nach, ob es in einer Datei steht.
Tests oder Linter starte ich ebenfalls nicht per Hook; meine Mitarbeiter schreiben Texte, Audits und Konfigurationen. Wenn du Software entwickelst, sind die Beispiele der Doku dein Startpunkt.
Claude Code Hooks unter Windows: PowerShell statt Bash
Unter Windows laufen Hooks in Shell-Form laut Referenz in Git Bash; ist kein Git Bash installiert, in PowerShell. Für Aufrufe über .cmd-Dateien oder .bat-Dateien ist die args-Form Pflicht, bei der Befehl und Argumente getrennt stehen. Das offizielle Muster für ein PowerShell-Skript sieht so aus: "command": "powershell.exe" mit "args": ["-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1"]. Das Skript liest stdin mit [Console]::In.ReadToEnd() | ConvertFrom-Json und antwortet mit ConvertTo-Json.
Zwei Windows-Eigenheiten aus der Doku solltest du kennen. Notification-Hooks zeigen ihre Meldung unter Umständen als Dialogfenster statt als Benachrichtigung in der Ecke. Und Shell-Profile, die bei jedem Start etwas per echo ausgeben, stellen diesen Text vor das JSON deines Hooks; Claude Code ignoriert es dann. Wenn ein Hook unter Windows scheinbar nichts tut, prüfe zuerst das Profil.
Grenzen, Fehler und Sicherheit
Hooks können Regeln verschärfen, aber nicht lockern. Die Anleitung sagt es wörtlich: „Hooks can tighten restrictions but not loosen them." Ein deny aus einem PreToolUse-Hook gilt immer, auch wenn du Claude Code mit --dangerously-skip-permissions startest; ein allow aus einem Hook hebt dagegen keine Verbotsregel auf. Hooks sind damit der richtige Ort für Schutz und der falsche für Abkürzungen.
Der zweite Punkt ist die Stop-Sperre. Ein Stop-Hook, der Claude immer wieder zurückschickt, baut schnell eine Endlosschleife. Claude Code überstimmt ihn laut Doku nach acht Blocks ohne Fortschritt; dein Skript prüft besser das Feld stop_hook_active und endet bei true mit Exit 0. Der dritte Punkt sind Zeitlimits: bis zu 600 Sekunden je command-Hook, 30 Sekunden bei UserPromptSubmit. Mein Skript nutzt 2 Sekunden, weil jede Sekunde im Hook eine Sekunde ist, in der ein Mitarbeiter wartet.
Zum Schluss die Sicherheit: Ein Hook ist Code, der mit deinen Rechten läuft, bei jedem Ereignis, ohne Nachfrage. Ein Hook in einer geteilten .claude/settings.json gilt für jeden, der Claude Code in diesem Repository startet, und hängt laut Anthropics Doku zu Berechtigungen nicht am Vertrauensdialog für den Ordner, anders als die permissions.allow-Regeln eines Projekts. Lies deshalb fremde Hooks, bevor du in einem fremden Projekt startest; für einen einzelnen Lauf schaltet --settings '{"disableAllHooks": true}' alle ab.
Häufige Fragen
Was ist der Unterschied zwischen einem Hook und einer Regel in der CLAUDE.md?
Eine Regel in der CLAUDE.md ist eine Anweisung an das Modell; Claude liest sie und entscheidet, wie es sie umsetzt. Ein Hook ist ein Befehl, den Claude Code selbst ausführt, bevor oder nachdem etwas passiert, unabhängig davon, was das Modell gerade vorhat. Anthropic nennt das erste „advisory" und das zweite „deterministic".
Kann Claude einen Hook einfach umgehen?
Nein, ein Hook läuft außerhalb des Modells. Laut Anleitung laufen PreToolUse-Hooks vor jeder Freigabe-Prüfung, auch im Modus ohne Rückfragen, und ein deny gilt immer. Was Claude kann: mit einem anderen Werkzeug an das Ziel kommen, wenn dein Matcher nur Edit|Write prüft und ein Bash-Aufruf dieselbe Datei ändert.
Wo trage ich einen Hook ein, damit er für alle meine Projekte gilt?
In ~/.claude/settings.json in deinem Home-Verzeichnis. Genau dort liegt auch mein Belegschafts-Hook, weil er in jedem Mitarbeiter-Ordner gelten soll. Laut Referenz lesen Cloud-Sitzungen von Claude Code diese Datei nicht; dort brauchst du die Projekt-Einstellungen.
Funktionieren Claude Code Hooks unter Windows ohne Git Bash?
Ja, laut Referenz laufen sie dann in PowerShell. Für Skripte nutzt du die args-Form mit powershell.exe und den Schaltern -NoProfile, -ExecutionPolicy Bypass und -File.
Warum feuert mein Hook nicht, obwohl er in der settings.json steht?
Die häufigsten Ursachen: ein Skript ohne Ausführungsrecht, ein falsch geschriebener Ereignisname, ungültiges JSON in der Einstellungsdatei oder Text, den dein Shell-Profil vor die JSON-Ausgabe stellt. Prüfe mit /hooks, ob Claude Code den Hook kennt, und starte das Skript einmal von Hand.
Wo finde ich weitere Claude Code Hooks Beispiele?
In der Hooks-Anleitung von Anthropic, die neben dem Dateischutz auch Beispiele für Formatierer, Benachrichtigungen und Kontext nach der Kompaktierung enthält. Der schnellste Weg ist aber, Claude selbst zu fragen: Beschreibe, was immer passieren soll, und lass dir Skript und JSON schreiben.
Wie du weitermachst
Wenn deine Hooks stehen, ist der nächste Schritt, die Arbeit selbst aufzuteilen: Wie Subagents, Skills und Agents zusammenspielen, erkläre ich in Claude Subagents, Skills und Agents, und was bei Subagents in der Praxis zählt, steht in Claude Code Subagents: Best Practices. Wiederkehrende Abläufe fasst du in Claude Skills: erstellen und nutzen; wenn ein Hook auf externe Dienste zugreifen soll, lohnt vorher ein Blick auf Claude MCP Server einrichten.
Die acht fertigen KI-Mitarbeiter-Pakete, jedes mit eigener Personalakte und eigenen Skills, gibt es in meiner Community. Mein Vorschlag für den Einstieg: Nimm den Dateischutz-Hook aus der Doku, trag deine drei wichtigsten Dateien ein und bitte Claude, eine davon zu ändern. Wenn die Blockade kommt, hast du verstanden, was ein Hook ist.