Hook-Lebenszyklus
Hooks werden an bestimmten Punkten während einer Claude Code-Sitzung ausgelöst. Wenn ein Ereignis ausgelöst wird und ein Matcher passt, übergibt Claude Code JSON-Kontext über das Ereignis an Ihren Hook-Handler. Für Command-Hooks kommt die Eingabe über stdin an. Für HTTP-Hooks kommt sie als POST-Request-Body an. Ihr Handler kann dann die Eingabe überprüfen, Maßnahmen ergreifen und optional eine Entscheidung zurückgeben. Ereignisse fallen in drei Rhythmen:- einmal pro Sitzung:
SessionStartundSessionEnd - einmal pro Runde:
UserPromptSubmit,StopundStopFailure - bei jedem Tool-Aufruf innerhalb der agentengesteuerten Schleife:
PreToolUseundPostToolUse
Wie ein Hook aufgelöst wird
Um zu sehen, wie diese Teile zusammenpassen, betrachten Sie diesenPreToolUse-Hook, der destruktive Shell-Befehle blockiert. Der matcher grenzt auf Bash-Tool-Aufrufe ein und die if-Bedingung grenzt weiter auf Bash-Unterbefehle ein, die mit rm * übereinstimmen, daher wird block-rm.sh nur ausgeführt, wenn beide Filter passen:
permissionDecision von "deny" zurück, wenn es rm -rf enthält:
Bash "rm -rf /tmp/build" auszuführen. Hier ist, was passiert:
1
Ereignis wird ausgelöst
Das
PreToolUse-Ereignis wird ausgelöst. Claude Code sendet die Tool-Eingabe als JSON über stdin an den Hook:2
Matcher prüft
Der Matcher
"Bash" passt zum Tool-Namen, daher wird diese Hook-Gruppe aktiviert. Wenn Sie den Matcher weglassen oder "*" verwenden, wird die Gruppe bei jedem Auftreten des Ereignisses aktiviert.3
If-Bedingung prüft
Die
if-Bedingung "Bash(rm *)" passt, weil rm -rf /tmp/build ein Unterbefehl ist, der mit rm * übereinstimmt, daher wird dieser Handler ausgeführt. Wenn der Befehl npm test gewesen wäre, würde die if-Prüfung fehlschlagen und block-rm.sh würde nie ausgeführt, wodurch der Prozess-Spawn-Overhead vermieden wird. Das Feld if ist optional; ohne es wird jeder Handler in der passenden Gruppe ausgeführt.4
Hook-Handler wird ausgeführt
Das Skript überprüft den vollständigen Befehl und findet Wenn der Befehl eine sicherere
rm -rf, daher gibt es eine Entscheidung auf stdout aus:rm-Variante gewesen wäre, wie rm file.txt, würde das Skript stattdessen exit 0 treffen. Exit-Code 0 ohne Ausgabe bedeutet, dass der Hook keine Entscheidung zu melden hat, daher wird der Tool-Aufruf durch den normalen Berechtigungsfluss fortgesetzt. Der Hook kann den Aufruf ablehnen, aber Stille bedeutet nicht, dass er ihn genehmigt.5
Claude Code handelt nach dem Ergebnis
Claude Code liest die JSON-Entscheidung, blockiert den Tool-Aufruf und zeigt Claude den Grund an.
Konfiguration
Hooks werden in JSON-Einstellungsdateien definiert. Die Konfiguration hat drei Verschachtelungsebenen:- Wählen Sie ein Hook-Ereignis aus, auf das Sie reagieren möchten, wie
PreToolUseoderStop - Fügen Sie eine Matcher-Gruppe hinzu, um zu filtern, wann es ausgelöst wird, wie „nur für das Bash-Tool”
- Definieren Sie einen oder mehrere Hook-Handler, die ausgeführt werden, wenn sie passen
Diese Seite verwendet spezifische Begriffe für jede Ebene: Hook-Ereignis für den Lebenszyklus-Punkt, Matcher-Gruppe für den Filter und Hook-Handler für den Shell-Befehl, HTTP-Endpunkt, MCP-Tool, Prompt oder Agent, der ausgeführt wird. „Hook” allein bezieht sich auf die allgemeine Funktion.
Hook-Speicherorte
Der Ort, an dem Sie einen Hook definieren, bestimmt seinen Umfang:
Weitere Informationen zur Auflösung von Einstellungsdateien finden Sie unter Einstellungen. Enterprise-Administratoren können
allowManagedHooksOnly verwenden, um Benutzer-, Projekt- und Plugin-Hooks zu blockieren. Hooks von Plugins, die in verwalteten Einstellungen enabledPlugins erzwungen aktiviert sind, sind ausgenommen, daher können Administratoren überprüfte Hooks über einen Organisations-Marketplace verteilen. Siehe Hook-Konfiguration.
Matcher-Muster
Das Feldmatcher filtert, wann Hooks ausgelöst werden. Wie ein Matcher evaluiert wird, hängt von den Zeichen ab, die er enthält:
Ein Matcher auf dem Regex-Pfad wird mit
RegExp.prototype.test von JavaScript getestet, was bei einer Übereinstimmung irgendwo im Wert erfolgreich ist. Edit.* passt sowohl zu Edit als auch zu NotebookEdit; wickeln Sie das Muster in ^ und $ ein, wie in ^Edit$, wenn Sie eine Ganzzahl-Übereinstimmung benötigen.
Komma-Trennzeichen und die Toleranz für umgebendes Leerzeichen erfordern Claude Code v2.1.191 oder später.
Bindestriche in der Exact-Match-Menge erfordern Claude Code v2.1.195 oder später. In früheren Versionen wird ein hyphenierter Name wie code-reviewer als unverankerte Regex evaluiert, daher wird er auch für senior-code-reviewer ausgelöst; verankern Sie ihn als ^code-reviewer$ in diesen Versionen, um nur diesen Namen zu treffen.
FileChanged und StopFailure verwenden einen engeren Exact-Match-Satz von nur Buchstaben, Ziffern, _ und |. Ein Bindestrich, Leerzeichen oder Komma in einem Matcher für diese beiden Ereignisse hält ihn auf dem Regex-Pfad, und nur | trennt Alternativen. Jedes andere Ereignis mit Matcher-Unterstützung in der folgenden Tabelle akzeptiert | oder ,.
Das Ereignis FileChanged folgt diesen Regeln nicht, wenn es seine Überwachungsliste erstellt. Siehe FileChanged.
Jeder Ereignistyp passt auf ein anderes Feld:
Der Matcher wird gegen ein Feld aus der JSON-Eingabe ausgeführt, die Claude Code an Ihren Hook über stdin sendet. Für Tool-Ereignisse ist dieses Feld
tool_name. Jeder Abschnitt Hook-Ereignis listet den vollständigen Satz von Matcher-Werten und das Eingabeschema für dieses Ereignis auf.
Dieses Beispiel führt ein Linting-Skript nur aus, wenn Claude eine Datei schreibt oder bearbeitet:
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay und CwdChanged unterstützen keine Matcher und werden immer bei jedem Auftreten ausgelöst. Wenn Sie ein matcher-Feld zu diesen Ereignissen hinzufügen, wird es stillschweigend ignoriert.
Für Tool-Ereignisse können Sie enger filtern, indem Sie das Feld if auf einzelnen Hook-Handlern setzen. if verwendet Berechtigungsregel-Syntax, um gegen den Tool-Namen und die Argumente zusammen zu passen, daher wird "Bash(git *)" ausgeführt, wenn ein Bash-Befehl mit git * übereinstimmt und "Edit(*.ts)" wird nur für TypeScript-Dateien ausgeführt.
MCP-Tools abgleichen
MCP Server-Tools erscheinen als reguläre Tools in Tool-Ereignissen (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), daher können Sie sie auf die gleiche Weise abgleichen wie jeden anderen Tool-Namen.
MCP-Tools folgen dem Benennungsmuster mcp__<server>__<tool>, zum Beispiel:
mcp__memory__create_entities: Memory-Server-Tool zum Erstellen von Entitätenmcp__filesystem__read_file: Filesystem-Server-Tool zum Lesen von Dateienmcp__github__search_repositories: GitHub-Server-Suchtool
.* zum Server-Präfix hinzu. Das .* ist erforderlich: Ein Matcher wie mcp__memory oder mcp__brave-search enthält nur Exact-Match-Zeichen, daher wird er als exakte Zeichenkette verglichen und passt zu keinem Tool.
mcp__memory__.*passt zu allen Tools vommemory-Servermcp__brave-search__.*passt zu allen Tools von einem Server, dessen Name einen Bindestrich enthältmcp__.*__write.*passt zu jedem Tool, dessen Name mitwritebeginnt, von jedem Server
mcp__brave-search als unverankerte Regex evaluiert und passt zu jedem Tool von diesem Server. Die Form mcp__brave-search__.* funktioniert auf jeder Version.
Tools von einem Plugin-gebündelten MCP-Server verwenden ein Scope-Server-Segment, das den Plugin-Namen enthält: mcp__plugin_<plugin-name>_<server-name>__<tool>. Ein Matcher, der gegen den bloßen Server-Schlüssel geschrieben ist, wird niemals für diese Tools ausgelöst. Für ein Plugin namens my-plugin, das einen Server unter dem Schlüssel db bündelt, erscheint ein query-Tool als mcp__plugin_my-plugin_db__query, daher ist der Matcher für jedes Tool von diesem Server mcp__plugin_my-plugin_db__.*. Verwenden Sie denselben Scope-Tool-Namen im Feld if eines Handlers. Siehe Plugin-bereitgestellte MCP-Server, um zu erfahren, wie der Scope-Name erstellt wird.
Dieses Beispiel protokolliert alle Memory-Server-Operationen und validiert Schreibvorgänge von jedem MCP-Server:
Hook-Handler-Felder
Jedes Objekt im innerenhooks-Array ist ein Hook-Handler: der Shell-Befehl, HTTP-Endpunkt, MCP-Tool, LLM-Prompt oder Agent, der ausgeführt wird, wenn der Matcher passt. Es gibt fünf Typen:
- Command-Hooks (
type: "command"): führen einen Shell-Befehl aus. Ihr Skript erhält die JSON-Eingabe des Ereignisses über stdin und kommuniziert Ergebnisse über Exit-Codes und stdout zurück. - HTTP-Hooks (
type: "http"): senden die JSON-Eingabe des Ereignisses als HTTP-POST-Request an eine URL. Der Endpunkt kommuniziert Ergebnisse über den Response-Body mit dem gleichen JSON-Ausgabeformat wie Command-Hooks zurück. - MCP-Tool-Hooks (
type: "mcp_tool"): rufen ein Tool auf einem bereits verbundenen MCP-Server auf. Die Textausgabe des Tools wird wie Command-Hook-stdout behandelt. - Prompt-Hooks (
type: "prompt"): senden einen Prompt an ein Claude-Modell für eine Single-Turn-Evaluierung. Das Modell gibt eine Ja/Nein-Entscheidung als JSON zurück. Siehe Prompt-basierte Hooks. - Agent-Hooks (
type: "agent"): spawnen einen Subagenten, der Tools wie Read, Grep und Glob verwenden kann, um Bedingungen zu überprüfen, bevor eine Entscheidung zurückgegeben wird. Agent-Hooks sind experimentell und können sich ändern. Siehe Agent-basierte Hooks.
args dedupliziert, und HTTP-Hooks werden nach URL dedupliziert.
Handler werden im aktuellen Verzeichnis mit der Umgebung von Claude Code ausgeführt. Die Umgebungsvariable $CLAUDE_CODE_REMOTE wird in Remote-Web-Umgebungen auf "true" gesetzt und ist in der lokalen CLI nicht gesetzt. Ab v2.1.199 wird $CLAUDE_CODE_BRIDGE_SESSION_ID auf die Remote Control Sitzungs-ID gesetzt, während die lokale Sitzung eine aktive Remote Control-Verbindung hat.
Gemeinsame Felder
Diese Felder gelten für alle Hook-Typen:
Das Feld
if enthält genau eine Berechtigungsregel. Es gibt keine &&-, ||- oder List-Syntax zum Kombinieren von Regeln; um mehrere Bedingungen anzuwenden, definieren Sie einen separaten Hook-Handler für jeden.
Für Bash-Muster hängt davon ab, ob Ihr Hook-Befehl ausgeführt wird, von der Form des Musters und dem Bash-Befehl, den Claude aufruft. Führende VAR=value-Zuweisungen werden vor dem Abgleich entfernt.
Der Filter schlägt auch offen fehl und führt Ihren Hook unabhängig vom Muster aus, wenn der Bash-Befehl nicht geparst werden kann. Da der
if-Filter Best-Effort ist, verwenden Sie das Berechtigungssystem statt eines Hooks, um ein hartes Zulassen oder Verweigern durchzusetzen.
Command-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren Command-Hooks diese Felder:
Ein Command-Hook wird als Exec-Form ausgeführt, wenn
args gesetzt ist, und als Shell-Form, wenn args weggelassen ist. Setzen Sie args, wenn der Hook auf einen Pfad-Platzhalter verweist, da jedes Element als ein Argument ohne Anführungszeichen übergeben wird. Lassen Sie args weg, wenn Sie Shell-Funktionen wie Pipes oder && benötigen, oder wenn keine dieser Bedenken zutrifft.
Exec-Form wird ausgeführt, wenn args vorhanden ist. Claude Code löst command als ausführbare Datei auf PATH auf und spawnt sie direkt mit args als Argumentvektor. Es gibt keine Shell, daher ist jedes args-Element genau ein Argument, wie geschrieben, und Pfad-Platzhalter wie ${CLAUDE_PLUGIN_ROOT} werden als einfache Zeichenketten in command und in jedes args-Element ersetzt. Sonderzeichen wie Apostrophe, $ und Backticks werden wörtlich durchgeleitet, da es keine Shell gibt, die sie interpretiert. Auf keiner Plattform findet Shell-Tokenisierung statt.
Shell-Form wird ausgeführt, wenn args fehlt. Die command-Zeichenkette wird an eine Shell übergeben: sh -c auf macOS und Linux, Git Bash unter Windows oder PowerShell, wenn Git Bash nicht installiert ist. Setzen Sie das Feld shell, um explizit zu wählen. Die Shell tokenisiert die Zeichenkette, erweitert Variablen und interpretiert Pipes, &&, Umleitungen und Globs.
Unter Windows erfordert die Exec-Form, dass
command zu einer echten ausführbaren Datei wie .exe aufgelöst wird. Die .cmd und .bat Shims, die npm, npx, eslint und andere Tools in node_modules/.bin installieren, sind keine ausführbaren Dateien und können ohne Shell nicht gespawnt werden. Um sie in Exec-Form auszuführen, rufen Sie das zugrunde liegende Skript direkt mit node auf, zum Beispiel "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Das node plus Skript-Pfad-Muster funktioniert auf jeder Plattform, da node.exe eine echte Binärdatei ist. Um einen .cmd oder .bat Shim nach Name auszuführen, verwenden Sie Shell-Form.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT und CLAUDE_PLUGIN_DATA auf dem gespawnten Prozess, daher kann ein Skript process.env.CLAUDE_PLUGIN_ROOT lesen, unabhängig davon, wie es gestartet wurde.
Plugin-Hooks ersetzen zusätzlich ${user_config.*} Werte, nur in Exec-Form: Der Wert wird als einfache Zeichenkette in command und in jedes args-Element ersetzt, daher wird er nicht erneut von der Shell geparst.
Ein Shell-Form-Plugin-Hook, dessen command auf ${user_config.*} verweist, schlägt mit einem Fehler fehl, anstatt ausgeführt zu werden. Um einen Optionswert aus einem Shell-Form-Hook zu verwenden, lesen Sie die Umgebungsvariable $CLAUDE_PLUGIN_OPTION_<KEY>, wie $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL für eine webhook_url-Option, oder setzen Sie args, um den Hook auf Exec-Form umzuschalten. Vor v2.1.207 ersetzten Shell-Form-Plugin-Hook-Befehle auch ${user_config.*}.
In Exec-Form ist
command nur der ausführbare Name oder Pfad. Wenn command ein bloßer Name ohne Pfad-Trennzeichen ist und Leerzeichen neben args enthält, protokolliert Claude Code eine Warnung, da das Spawn fehlschlagen wird: Es gibt keine ausführbare Datei namens node script.js. Verschieben Sie die zusätzlichen Token in args. Absolute Pfade mit Leerzeichen, wie C:\Program Files\nodejs\node.exe, sind eine einzelne gültige ausführbare Datei und lösen die Warnung nicht aus.HTTP-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren HTTP-Hooks diese Felder:
Claude Code sendet die JSON-Eingabe des Hooks als POST-Request-Body mit
Content-Type: application/json. Der Response-Body verwendet das gleiche JSON-Ausgabeformat wie Command-Hooks.
Die Fehlerbehandlung unterscheidet sich von Command-Hooks: Nicht-2xx-Antworten, Verbindungsfehler und Timeouts führen alle zu nicht-blockierenden Fehlern, die die Ausführung fortsetzen lassen. Um einen Tool-Aufruf zu blockieren oder eine Berechtigung zu verweigern, geben Sie eine 2xx-Antwort mit einem JSON-Body zurück, der decision: "block" oder ein hookSpecificOutput mit permissionDecision: "deny" enthält.
Dieses Beispiel sendet PreToolUse-Ereignisse an einen lokalen Validierungsdienst und authentifiziert sich mit einem Token aus der MY_TOKEN-Umgebungsvariable:
MCP-Tool-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren MCP-Tool-Hooks diese Felder:
Die Textausgabe des Tools wird wie Command-Hook-stdout behandelt: Wenn sie als gültige JSON-Ausgabe geparst wird, wird sie als Entscheidung verarbeitet, andernfalls wird sie als Klartext angezeigt. Wenn der benannte Server nicht verbunden ist oder das Tool
isError: true zurückgibt, erzeugt der Hook einen nicht-blockierenden Fehler und die Ausführung wird fortgesetzt.
MCP-Tool-Hooks sind auf jedem Hook-Ereignis verfügbar, sobald Claude Code sich mit Ihren MCP-Servern verbunden hat. SessionStart und Setup werden normalerweise ausgelöst, bevor Server die Verbindung beenden, daher sollten Hooks auf diesen Ereignissen beim ersten Ausführen den Fehler „nicht verbunden” erwarten.
Dieses Beispiel ruft das Tool security_scan auf dem MCP-Server my_server nach jedem Write oder Edit auf und übergibt den Pfad der bearbeiteten Datei:
Prompt- und Agent-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren Prompt- und Agent-Hooks diese Felder:Skripte nach Pfad referenzieren
Verwenden Sie diese Platzhalter, um Hook-Skripte relativ zum Projekt- oder Plugin-Root zu referenzieren, unabhängig vom Arbeitsverzeichnis, wenn der Hook ausgeführt wird:${CLAUDE_PROJECT_DIR}: das Projekt-Root. Claude Code setzt diese Variable auch in der Umgebung von stdio MCP-Servern und Plugin-LSP-Servern.${CLAUDE_PLUGIN_ROOT}: das Installationsverzeichnis des Plugins, für Skripte, die mit einem Plugin gebündelt sind. Ändert sich bei jedem Plugin-Update.${CLAUDE_PLUGIN_DATA}: das persistente Datenverzeichnis des Plugins, für Abhängigkeiten und Zustand, die Plugin-Updates überstehen sollten.
args-Element als ein Argument ohne Shell-Tokenisierung, daher benötigen Pfade mit Leerzeichen oder Sonderzeichen keine Anführungszeichen. In Shell-Form wickeln Sie jeden Platzhalter in doppelte Anführungszeichen ein.
- Projekt-Skripte
- Plugin-Skripte
Dieses Beispiel verwendet
${CLAUDE_PROJECT_DIR}, um einen Style-Checker aus dem .claude/hooks/-Verzeichnis des Projekts nach jedem Write- oder Edit-Tool-Aufruf auszuführen:Hooks in Skills und Agents
Zusätzlich zu Einstellungsdateien und Plugins können Hooks direkt in Skills und Subagenten mit Frontmatter definiert werden. Diese Hooks sind auf den Lebenszyklus der Komponente beschränkt und werden nur ausgeführt, wenn diese Komponente aktiv ist. Alle Hook-Ereignisse werden unterstützt. Für Subagenten werdenStop-Hooks automatisch in SubagentStop konvertiert, da dies das Ereignis ist, das ausgelöst wird, wenn ein Subagent fertig ist.
Hooks verwenden das gleiche Konfigurationsformat wie einstellungsbasierte Hooks, sind aber auf die Lebensdauer der Komponente beschränkt und werden bereinigt, wenn sie fertig ist.
Dieser Skill definiert einen PreToolUse-Hook, der ein Sicherheitsvalidierungsskript vor jedem Bash-Befehl ausführt:
Das Menü /hooks
Geben Sie /hooks in Claude Code ein, um einen schreibgeschützten Browser für Ihre konfigurierten Hooks zu öffnen. Das Menü zeigt jedes Hook-Ereignis mit einer Anzahl konfigurierter Hooks, ermöglicht es Ihnen, in Matcher zu bohren, und zeigt die vollständigen Details jedes Hook-Handlers. Verwenden Sie es, um die Konfiguration zu überprüfen, zu prüfen, aus welcher Einstellungsdatei ein Hook stammt, oder einen Hook-Befehl, Prompt oder URL zu überprüfen.
Das Menü zeigt alle fünf Hook-Typen an: command, prompt, agent, http und mcp_tool. Jeder Hook ist mit einem [type]-Präfix und einer Quelle gekennzeichnet, die angibt, wo er definiert wurde:
User: aus~/.claude/settings.jsonProject: aus.claude/settings.jsonLocal: aus.claude/settings.local.jsonPlugin: aushooks/hooks.jsoneines PluginsSession: in Speicher für die aktuelle Sitzung registriertBuilt-in: intern von Claude Code registriert
Hooks deaktivieren oder entfernen
Um einen Hook zu entfernen, löschen Sie seinen Eintrag aus der Einstellungs-JSON-Datei. Um alle Hooks vorübergehend zu deaktivieren, ohne sie zu entfernen, setzen Sie"disableAllHooks": true in Ihrer Einstellungsdatei. Es gibt keine Möglichkeit, einen einzelnen Hook zu deaktivieren, während er in der Konfiguration bleibt.
Die Einstellung disableAllHooks respektiert die Hierarchie der verwalteten Einstellungen. Wenn ein Administrator Hooks durch verwaltete Richtlinieneinstellungen konfiguriert hat, kann disableAllHooks, das in Benutzer-, Projekt- oder lokalen Einstellungen gesetzt ist, diese verwalteten Hooks nicht deaktivieren. Nur disableAllHooks, das auf der Ebene der verwalteten Einstellungen gesetzt ist, kann verwaltete Hooks deaktivieren.
Direkte Bearbeitungen von Hooks in Einstellungsdateien werden normalerweise automatisch vom Datei-Watcher aufgegriffen.
Hook-Eingabe und -Ausgabe
Command-Hooks erhalten JSON-Daten über stdin und kommunizieren Ergebnisse über Exit-Codes, stdout und stderr. HTTP-Hooks erhalten die gleiche JSON als POST-Request-Body und kommunizieren Ergebnisse über den HTTP-Response-Body. Dieser Abschnitt behandelt Felder und Verhalten, die allen Ereignissen gemeinsam sind. Jeder Abschnitt eines Ereignisses unter Hook-Ereignisse enthält sein spezifisches Eingabeschema und Optionen zur Entscheidungskontrolle. Auf macOS und Linux werden Command-Hooks seit v2.1.139 in ihrer eigenen Sitzung ohne steuerndes Terminal ausgeführt. Der Hook-Prozess und alle untergeordneten Prozesse können/dev/tty nicht öffnen oder Escape-Sequenzen direkt an die Claude Code-Benutzeroberfläche senden. Windows hat kein /dev/tty. Um eine Nachricht dem Benutzer auf jeder Plattform anzuzeigen, geben Sie systemMessage in der JSON-Ausgabe zurück. Um eine Desktop-Benachrichtigung auszulösen, einen Fenstertitel zu setzen oder die Glocke zu läuten, geben Sie stattdessen terminalSequence zurück.
Gemeinsame Eingabefelder
Hook-Ereignisse erhalten diese Felder als JSON, zusätzlich zu ereignisspezifischen Feldern, die in jedem Abschnitt Hook-Ereignis dokumentiert sind. Für Command-Hooks kommt diese JSON über stdin an. Für HTTP-Hooks kommt sie als POST-Request-Body an.
Wenn mit
--agent oder innerhalb eines Subagenten ausgeführt, sind zwei zusätzliche Felder enthalten:
Nur
SessionStart-Hooks können ein model-Feld erhalten, und es ist nicht garantiert, dass es vorhanden ist. Es gibt keine $CLAUDE_MODEL-Umgebungsvariable. Ein Hook-Prozess erbt die übergeordnete Umgebung, sodass er $ANTHROPIC_MODEL lesen kann, wenn Sie sie in Ihrer Shell setzen, aber dieser Wert ändert sich nicht, wenn Sie während einer Sitzung mit /model Modelle wechseln. Ein Satz von Variablen wird nicht vererbt: Claude Code entfernt OTEL_*-Exportervariablen aus jedem Unterprozess, den es spawnt, einschließlich Hooks.
Zum Beispiel erhält ein PreToolUse-Hook für einen Bash-Befehl dies über stdin:
tool_name und tool_input sind ereignisspezifisch. Jeder Abschnitt Hook-Ereignis dokumentiert die zusätzlichen Felder für dieses Ereignis.
Exit-Code-Ausgabe
Der Exit-Code aus Ihrem Hook-Befehl teilt Claude Code mit, ob die Aktion fortgesetzt, blockiert oder ignoriert werden soll. Exit 0 bedeutet Erfolg. Claude Code analysiert stdout auf JSON-Ausgabefelder. JSON-Ausgabe wird nur bei Exit 0 verarbeitet. Für die meisten Ereignisse wird stdout in das Debug-Log geschrieben, aber nicht im Transkript angezeigt. Die Ausnahmen sindUserPromptSubmit, UserPromptExpansion und SessionStart, wo stdout als Kontext hinzugefügt wird, den Claude sehen und darauf reagieren kann.
Exit 2 bedeutet ein blockierender Fehler. Claude Code ignoriert stdout und jede JSON darin. Stattdessen wird der stderr-Text an Claude als Fehlermeldung zurückgegeben. Die Auswirkung hängt vom Ereignis ab: PreToolUse blockiert den Tool-Aufruf, UserPromptSubmit lehnt den Prompt ab, und so weiter. Siehe Exit-Code-2-Verhalten pro Ereignis für die vollständige Liste.
Jeder andere Exit-Code ist ein nicht-blockierender Fehler für die meisten Hook-Ereignisse. Das Transkript zeigt eine <hook name> hook error-Benachrichtigung gefolgt von der ersten Zeile von stderr, damit Sie die Ursache ohne --debug identifizieren können. Die Ausführung wird fortgesetzt und der vollständige stderr wird in das Debug-Log geschrieben.
Zum Beispiel ein Hook-Befehlsskript, das gefährliche Bash-Befehle blockiert:
Exit-Code-2-Verhalten pro Ereignis
Exit-Code 2 ist die Art, wie ein Hook signalisiert „Stopp, mach das nicht.” Die Auswirkung hängt vom Ereignis ab, da einige Ereignisse Aktionen darstellen, die blockiert werden können (wie ein Tool-Aufruf, der noch nicht stattgefunden hat), und andere Dinge darstellen, die bereits passiert sind oder nicht verhindert werden können.
Für
SessionStart, Setup und SubagentStart wird der Exit-Code-2-stderr im Transkript als <hook name> hook error-Benachrichtigung dargestellt, auf die gleiche Weise wie ein nicht-blockierender Fehler. Claude sieht ihn nicht, und die Sitzung oder der Subagent wird fortgesetzt. Für SubagentStart wird die Benachrichtigung im eigenen Transkript des Subagenten angezeigt, nicht im übergeordneten Gespräch.
Ab Claude Code v2.1.199 zeigen SessionStart, Setup und SubagentStart Exit-Code-2-stderr im Transkript an. Frühere Versionen schrieben es nur in das Debug-Log.
HTTP-Response-Behandlung
HTTP-Hooks verwenden HTTP-Statuscodes und Response-Bodies anstelle von Exit-Codes und stdout:- 2xx mit leerem Body: Erfolg, äquivalent zu Exit-Code 0 ohne Ausgabe
- 2xx mit Plain-Text-Body: Erfolg, der Text wird als Kontext hinzugefügt
- 2xx mit JSON-Body: Erfolg, analysiert mit dem gleichen JSON-Ausgabe-Schema wie Command-Hooks
- Nicht-2xx-Status: Nicht-blockierender Fehler, Ausführung wird fortgesetzt
- Verbindungsfehler oder Timeout: Nicht-blockierender Fehler, Ausführung wird fortgesetzt
JSON-Ausgabe
Exit-Codes ermöglichen es Ihnen, zuzulassen oder zu blockieren, aber JSON-Ausgabe gibt Ihnen eine feinere Kontrolle. Anstatt mit Code 2 zu beenden, um zu blockieren, beenden Sie mit 0 und geben Sie ein JSON-Objekt auf stdout aus. Claude Code liest spezifische Felder aus diesem JSON, um das Verhalten zu steuern, einschließlich Entscheidungskontrolle zum Blockieren, Zulassen oder Eskalieren an den Benutzer.Sie müssen einen Ansatz pro Hook wählen, nicht beide: Verwenden Sie entweder Exit-Codes allein zum Signalisieren, oder beenden Sie mit 0 und geben Sie JSON für strukturierte Kontrolle aus. Claude Code verarbeitet JSON nur bei Exit 0. Wenn Sie mit 2 beenden, wird jede JSON ignoriert.
additionalContext, systemMessage und Plain-stdout, sind auf 10.000 Zeichen begrenzt. Ausgabe, die dieses Limit überschreitet, wird in einer Datei gespeichert und durch eine Vorschau und einen Dateipfad ersetzt, auf die gleiche Weise wie große Tool-Ergebnisse behandelt werden.
Das JSON-Objekt unterstützt drei Arten von Feldern:
- Universelle Felder wie
continuefunktionieren über alle Ereignisse hinweg. Diese sind in der Tabelle unten aufgelistet. - Top-Level
decisionundreasonwerden von einigen Ereignissen verwendet, um zu blockieren oder Feedback zu geben. hookSpecificOutputist ein verschachteltes Objekt für Ereignisse, die reichere Kontrolle benötigen. Es erfordert einhookEventName-Feld, das auf den Ereignisnamen gesetzt ist.
Um Claude unabhängig vom Ereignistyp vollständig zu stoppen:
Terminal-Benachrichtigungen ausgeben
Das FeldterminalSequence erfordert Claude Code v2.1.141 oder später.
Hooks werden ohne steuerndes Terminal ausgeführt, daher schlägt das direkte Schreiben von Escape-Sequenzen zu /dev/tty fehl. Geben Sie stattdessen die Escape-Sequenz im Feld terminalSequence zurück und Claude Code gibt sie für Sie über seinen eigenen Terminal-Schreibpfad aus. Dies ist race-frei, funktioniert innerhalb von tmux und GNU screen und funktioniert unter Windows, wo es kein /dev/tty gibt.
Das Feld akzeptiert einen String aus einer oder mehreren zugelassenen Escape-Sequenzen:
- OSC
0,1,2: Fenster- und Symboltitel - OSC
9: iTerm2-, ConEmu-, Windows Terminal- und WezTerm-Benachrichtigungen, einschließlich9;4Taskleisten-Fortschritt - OSC
99: Kitty-Benachrichtigungen - OSC
777: urxvt-, Ghostty- und Warp-Benachrichtigungen - Bare BEL
Notification-Hook aus. Die Escape-Sequenz wird mit printf-Oktalescapes erstellt, sodass die Steuerbytes niemals auf der Shell-Befehlszeile erscheinen, und jq -n --arg erstellt die JSON-Ausgabe, sodass Anführungszeichen, Backslashes und Zeilenumbrüche in der Benachrichtigungsmeldung korrekt escaped werden:
{ "terminalSequence": "..." } ist die gleiche aus jeder Shell oder Sprache. Unter Windows erstellen Sie die Escape-Zeichenkette in PowerShell oder einem Skript und geben das gleiche JSON-Objekt aus.
terminalSequence ist der unterstützte Ersatz für Hooks, die zuvor Escape-Sequenzen direkt zu /dev/tty schrieben. Die Zulassungsliste ist auf Sequenzen beschränkt, die den Cursor nicht bewegen oder Farben ändern können, sodass ein Hook niemals eine On-Screen-Eingabeaufforderung beschädigen kann.Kontext für Claude hinzufügen
Das FeldadditionalContext übergibt einen String aus Ihrem Hook in Claudes Kontextfenster. Claude Code umhüllt den String in eine Systemerinnerung und fügt ihn in das Gespräch an dem Punkt ein, an dem der Hook ausgelöst wurde. Claude liest die Erinnerung bei der nächsten Modellanfrage, aber sie wird nicht als Chat-Nachricht in der Benutzeroberfläche angezeigt.
Geben Sie additionalContext innerhalb von hookSpecificOutput neben dem Ereignisnamen zurück:
- SessionStart, Setup und SubagentStart: am Anfang des Gesprächs, vor der ersten Eingabeaufforderung
- UserPromptSubmit und UserPromptExpansion: neben der eingereichten Eingabeaufforderung
- PreToolUse, PostToolUse, PostToolUseFailure und PostToolBatch: neben dem Tool-Ergebnis
- Stop und SubagentStop: am Ende des Zugs. Das Gespräch wird fortgesetzt, sodass Claude auf das Feedback reagieren kann. Siehe Stop-Entscheidungskontrolle
additionalContext für das gleiche Ereignis zurückgeben, erhält Claude alle Werte. Wenn ein Wert 10.000 Zeichen überschreitet, schreibt Claude Code den vollständigen Text in eine Datei im Sitzungsverzeichnis und übergibt Claude stattdessen den Dateipfad mit einer kurzen Vorschau.
Verwenden Sie additionalContext für Informationen, die Claude über den aktuellen Zustand Ihrer Umgebung oder die gerade ausgeführte Operation wissen sollte:
- Umgebungszustand: der aktuelle Branch, das Bereitstellungsziel oder aktive Feature-Flags
- Bedingte Projektregeln: welcher Test-Befehl für die gerade bearbeitete Datei gilt, welche Verzeichnisse in diesem Worktree schreibgeschützt sind
- Externe Daten: offene Probleme, die Ihnen zugewiesen sind, aktuelle CI-Ergebnisse, Inhalte, die von einem internen Service abgerufen wurden
bun test” werden als Projektinformationen gelesen. Text, der als Out-of-Band-Systembefehle formuliert ist, kann Claudes Prompt-Injection-Abwehr auslösen, was dazu führt, dass Claude den Text an Sie zurückgibt, anstatt ihn als Kontext zu behandeln.
Nach der Injektion wird der Text im Sitzungstranskript gespeichert. Für Mid-Session-Ereignisse wie PostToolUse oder UserPromptSubmit wird beim Fortsetzen mit --continue oder --resume der gespeicherte Text erneut abgespielt, anstatt den Hook für vergangene Umdrehungen erneut auszuführen, sodass Werte wie Zeitstempel oder Commit-SHAs beim Fortsetzen veraltet werden. SessionStart-Hooks werden beim Fortsetzen mit source auf "resume" gesetzt erneut ausgeführt, sodass sie ihren Kontext aktualisieren können.
Entscheidungskontrolle
Nicht jedes Ereignis unterstützt das Blockieren oder Steuern des Verhaltens durch JSON. Die Ereignisse, die dies tun, verwenden jeweils einen anderen Satz von Feldern, um diese Entscheidung auszudrücken. Verwenden Sie diese Tabelle als schnelle Referenz, bevor Sie einen Hook schreiben:
Einige Ereignisse können auch Inhalte umschreiben, anstatt nur zuzulassen oder zu blockieren:
PreToolUse:updatedInputdirekt unterhookSpecificOutputersetzt die Argumente eines Tools, bevor es ausgeführt wird. Siehe PreToolUse-EntscheidungskontrollePermissionRequest:updatedInputinnerhalb desdecision-Objekts. Siehe PermissionRequest-EntscheidungskontrollePostToolUse:updatedToolOutputersetzt das Ergebnis des Tools. Siehe PostToolUse-EntscheidungskontrolleUserPromptSubmit: kann den Prompt nicht ersetzen; injiziert nuradditionalContextdaneben
PreToolUse für ausgehende Tool-Eingaben und PostToolUse für eingehende Tool-Ergebnisse ab.
Hier sind Beispiele für jedes Muster in Aktion:
- Top-Level-Entscheidung
- PreToolUse
- PermissionRequest
Wird von
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange und PreCompact verwendet. Der einzige Wert ist "block". Um die Aktion fortzusetzen, lassen Sie decision aus Ihrem JSON weg, oder beenden Sie mit 0 ohne jede JSON:Hook-Ereignisse
Jedes Ereignis entspricht einem Punkt im Lebenszyklus von Claude Code, an dem Hooks ausgeführt werden können. Die folgenden Abschnitte sind in der Reihenfolge des Lebenszyklus angeordnet: von der Sitzungseinrichtung durch die agentengesteuerte Schleife bis zum Sitzungsende. Jeder Abschnitt beschreibt, wann das Ereignis ausgelöst wird, welche Matcher es unterstützt, die JSON-Eingabe, die es erhält, und wie das Verhalten durch die Ausgabe gesteuert wird.SessionStart
Wird ausgeführt, wenn Claude Code eine neue Sitzung startet oder eine vorhandene Sitzung fortsetzt. Nützlich zum Laden von Entwicklungskontext wie vorhandenen Problemen oder kürzlichen Änderungen an Ihrer Codebasis oder zum Einrichten von Umgebungsvariablen. Für statischen Kontext, der kein Skript erfordert, verwenden Sie stattdessen CLAUDE.md. SessionStart wird bei jeder Sitzung ausgeführt, daher halten Sie diese Hooks schnell. Nurtype: "command" und type: "mcp_tool" Hooks werden unterstützt.
Der Matcher-Wert entspricht der Art, wie die Sitzung initiiert wurde:
SessionStart-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten SessionStart-Hookssource und optional model, agent_type und session_title:
SessionStart-Entscheidungskontrolle
Jeder Text, den Ihr Hook-Skript auf stdout ausgibt, wird als Kontext für Claude hinzugefügt. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, können Sie diese ereignisspezifischen Felder zurückgeben:suppressOutput oder sessionTitle kombinieren müssen.
Verwenden Sie reloadSkills, wenn ein SessionStart-Hook Skills installiert oder aktualisiert. Die Skill-Erkennung wird normalerweise ausgeführt, bevor SessionStart-Hooks fertig sind, daher würden Dateien, die der Hook in ~/.claude/skills/ oder .claude/skills/ schreibt, sonst erst in der nächsten Sitzung erscheinen. Dieses Beispiel synchronisiert ein gemeinsames Skills-Repository und fordert die Neuscan an:
Umgebungsvariablen beibehalten
SessionStart-Hooks haben Zugriff auf die UmgebungsvariableCLAUDE_ENV_FILE, die einen Dateipfad bereitstellt, in dem Sie Umgebungsvariablen für nachfolgende Bash-Befehle beibehalten können.
Um einzelne Umgebungsvariablen zu setzen, schreiben Sie export-Anweisungen in CLAUDE_ENV_FILE. Verwenden Sie Anhängen (>>), um Variablen zu bewahren, die von anderen Hooks gesetzt wurden:
CLAUDE_ENV_FILE ist für SessionStart-, Setup-, CwdChanged- und FileChanged-Hooks verfügbar. Andere Hook-Typen haben keinen Zugriff auf diese Variable.Setup
Wird nur ausgelöst, wenn Sie Claude Code mit--init-only starten oder mit --init oder --maintenance im nicht-interaktiven Modus mit dem -p-Flag. Es wird nicht beim normalen Start ausgelöst. Verwenden Sie es für einmalige Abhängigkeitsinstallation oder geplante Bereinigung, die Sie explizit von CI oder Skripten aus auslösen, getrennt vom normalen Sitzungsstart. Für Initialisierung pro Sitzung verwenden Sie stattdessen SessionStart.
Der Matcher-Wert entspricht dem CLI-Flag, das den Hook ausgelöst hat:
--init-only führt Setup-Hooks und SessionStart-Hooks mit dem startup-Matcher aus und beendet sich dann, ohne ein Gespräch zu starten. --init und --maintenance lösen Setup-Hooks nur aus, wenn sie mit -p kombiniert werden; in einer interaktiven Sitzung lösen diese beiden Flags derzeit keine Setup-Hooks aus.
Da Setup nicht bei jedem Start ausgelöst wird, kann ein Plugin, das eine Abhängigkeit installiert benötigt, sich nicht allein auf Setup verlassen. Das praktische Muster ist, die Abhängigkeit bei der ersten Verwendung zu überprüfen und bei Fehlen zu installieren, zum Beispiel ein Hook oder eine Skill, die auf ${CLAUDE_PLUGIN_DATA}/node_modules testet und npm install ausführt, wenn es fehlt. Siehe das Verzeichnis für persistente Daten, um zu erfahren, wo Sie installierte Abhängigkeiten speichern können.
Setup-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten Setup-Hooks ein Feldtrigger, das auf "init" oder "maintenance" gesetzt ist:
Setup-Entscheidungskontrolle
Setup-Hooks können nicht blockieren. Jeder Nicht-Null-Exit-Code, einschließlich 2, zeigt stderr dem Benutzer als<hook name> hook error-Hinweis an, und die Ausführung wird fortgesetzt. Im nicht-interaktiven Modus wird Hook-Ausgabe nur angezeigt, wenn Sie mit --verbose starten.
Um Informationen in Claudes Kontext zu übergeben, geben Sie additionalContext in der JSON-Ausgabe zurück; einfacher stdout wird nur in das Debug-Protokoll geschrieben. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, können Sie diese ereignisspezifischen Felder zurückgeben:
CLAUDE_ENV_FILE. Variablen, die in diese Datei geschrieben werden, bleiben in nachfolgenden Bash-Befehlen für die Sitzung erhalten, genau wie in SessionStart-Hooks. Nur type: "command" und type: "mcp_tool" Hooks werden unterstützt.
InstructionsLoaded
Wird ausgelöst, wenn eineCLAUDE.md- oder .claude/rules/*.md-Datei in den Kontext geladen wird. Dieses Ereignis wird beim Sitzungsstart für eifrig geladene Dateien ausgelöst und später erneut, wenn Dateien träge geladen werden, zum Beispiel wenn Claude auf ein Unterverzeichnis zugreift, das eine verschachtelte CLAUDE.md enthält, oder wenn bedingte Regeln mit paths:-Frontmatter passen. Der Hook unterstützt keine Blockierung oder Entscheidungskontrolle. Er wird asynchron zu Beobachtungszwecken ausgeführt.
Der Matcher wird gegen load_reason ausgeführt. Verwenden Sie zum Beispiel "matcher": "session_start", um nur für Dateien zu feuern, die beim Sitzungsstart geladen werden, oder "matcher": "path_glob_match|nested_traversal", um nur für träge Ladevorgänge zu feuern.
InstructionsLoaded-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten InstructionsLoaded-Hooks diese Felder:InstructionsLoaded-Entscheidungskontrolle
InstructionsLoaded-Hooks haben keine Entscheidungskontrolle. Sie können das Laden von Anweisungen nicht blockieren oder ändern. Verwenden Sie dieses Ereignis für Audit-Protokollierung, Compliance-Tracking oder Beobachtbarkeit.UserPromptSubmit
Wird ausgeführt, wenn der Benutzer einen Prompt einreicht, bevor Claude ihn verarbeitet. Dies ermöglicht es Ihnen, zusätzlichen Kontext basierend auf dem Prompt/Gespräch hinzuzufügen, Prompts zu validieren oder bestimmte Arten von Prompts zu blockieren.UserPromptSubmit-Hooks haben ein Standard-Timeout von 30 Sekunden für command-, http- und mcp_tool-Typen, kürzer als das 600-Sekunden-Standard für diese Typen bei anderen Ereignissen. Da dieser Hook vor jedem Prompt ausgeführt wird und die Modellverarbeitung blockiert, bis er abgeschlossen ist, stellt ein feststeckender Hook die Sitzung still. Wenn Ihr Hook mehr Zeit benötigt, setzen Sie das Feld timeout im Hook-Eintrag.
Ein UserPromptSubmit-Hook, der sein Timeout erreicht, wird abgebrochen und seine Ausgabe, einschließlich aller additionalContext, wird verworfen. Der Prompt erreicht Claude immer noch ohne diesen Kontext. Ab v2.1.196 zeigt das Transkript einen Hinweis, der den Hook, das ausgelöste Timeout und dass die Ausgabe verworfen wurde, benennt. Frühere Versionen brechen den Hook ohne Hinweis ab.
Ein Agent SDK-Callback-Hook auf UserPromptSubmit, der sein Timeout erreicht, blockiert den Prompt mit einer Nachricht, die den Hook und das Timeout benennt, da ein Callback dort als Richtlinien-Gate fungieren kann, das nicht offen fehlschlagen darf. Die Sitzung wird fortgesetzt. Vor v2.1.208 endete ein Callback-Timeout bei diesem Ereignis die Runde mit einem Ausführungsfehler.
UserPromptSubmit-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten UserPromptSubmit-Hooks das Feldprompt, das den Text enthält, den der Benutzer eingereicht hat.
UserPromptSubmit-Entscheidungskontrolle
UserPromptSubmit-Hooks können steuern, ob ein Benutzer-Prompt verarbeitet wird und Kontext hinzufügen. Alle JSON-Ausgabefelder sind verfügbar.
Es gibt zwei Möglichkeiten, Kontext zum Gespräch bei Exit-Code 0 hinzuzufügen:
- Plain-Text-stdout: Jeder Nicht-JSON-Text, der auf stdout geschrieben wird, wird als Kontext hinzugefügt
- JSON mit
additionalContext: Verwenden Sie das JSON-Format unten für mehr Kontrolle. Das FeldadditionalContextwird als Kontext hinzugefügt
additionalContext wird als Systemerinnerung eingefügt, die Claude ohne sichtbaren Transkript-Eintrag liest.
Um einen Prompt zu blockieren, geben Sie ein JSON-Objekt mit decision auf "block" zurück:
UserPromptExpansion
Wird ausgeführt, wenn ein vom Benutzer eingegebener Befehl in einen Prompt erweitert wird, bevor er Claude erreicht. Verwenden Sie dies, um bestimmte Befehle von direkter Aufrufe zu blockieren, Kontext für eine bestimmte Skill einzufügen oder zu protokollieren, welche Befehle Benutzer aufrufen. Zum Beispiel kann ein Hook, derdeploy passt, /deploy blockieren, es sei denn, eine Genehmigungsdatei ist vorhanden, oder ein Hook, der eine Review-Skill passt, kann die Review-Checkliste des Teams als additionalContext anhängen.
Dieses Ereignis deckt den Pfad ab, den PreToolUse nicht abdeckt: Ein PreToolUse-Hook, der das Skill-Tool passt, wird nur ausgelöst, wenn Claude das Tool aufruft, aber das direkte Eingeben von /skillname umgeht PreToolUse. UserPromptExpansion wird auf diesem direkten Pfad ausgelöst.
Passt auf command_name. Lassen Sie den Matcher leer, um auf jedem Prompt-Typ-Befehl zu feuern.
UserPromptExpansion-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten UserPromptExpansion-Hooksexpansion_type, command_name, command_args, command_source und die ursprüngliche prompt-Zeichenkette. Das Feld expansion_type ist slash_command für Skill- und benutzerdefinierte Befehle oder mcp_prompt für MCP-Server-Prompts.
UserPromptExpansion-Entscheidungskontrolle
UserPromptExpansion-Hooks können die Erweiterung blockieren oder Kontext hinzufügen. Alle JSON-Ausgabefelder sind verfügbar.
MessageDisplay
Wird ausgeführt, während eine Assistenten-Nachricht auf den Bildschirm gestreamt wird. Claude Code zeigt die Nachricht in Inkrementen an: Jedes Mal, wenn ein Batch neu abgeschlossener Zeilen zum Rendern bereit ist, wird der Hook einmal mit diesen Zeilen ausgeführt und Claude Code rendert den Ersatztext des Hooks an ihrer Stelle. Eine lange Nachricht erzeugt mehrere Aufrufe; eine kurze Nachricht kann nur einen erzeugen. Verwenden Sie MessageDisplay, um:- Markdown für eine minimale Anzeige zu entfernen
- Den Text zu transformieren, den eine Agent SDK-Anwendung ihren Benutzern zeigt
- API-Schlüssel oder interne Hostnamen aus Claudes Antworten zu redigieren
timeout im Hook-Eintrag.
MessageDisplay ist nur für die Anzeige: Der Ersatztext ändert nur das, was auf dem Bildschirm gerendert wird. Das Transkript und das, was Claude sieht, behalten den ursprünglichen Text, daher sieht Claude den Ersatz nie, und der ausführliche Modus zeigt das Original. Der Hook erhält nur Assistenten-Nachrichtentext, daher werden Tool-Ergebnisse und der Text, den Sie eingeben, unverändert gerendert.
MessageDisplay unterstützt keine Matcher und wird für jede Assistenten-Nachricht ausgelöst, die Text streamt; Nachrichten ohne Text, wie nur Tool-Aufruf-Antworten, lösen es nicht aus.
In nicht-interaktiven Läufen, einschließlich Agent SDK-Abfragen und claude -p, wird MessageDisplay einmal pro Assistenten-Nachricht statt einmal pro Batch von Zeilen ausgeführt. Der einzelne Aufruf kommt an, nachdem die Nachricht abgeschlossen ist und trägt den vollständigen Nachrichtentext: index ist 0, final ist true und delta hält die gesamte Nachricht. Ein Hook, der den delta-Text für jede Nachricht erfasst, erhält den gleichen Gesamttext in beiden Modi.
MessageDisplay-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten MessageDisplay-Hooks Kennungen für die Runde und Nachricht, die Position dieses Aufrufs innerhalb der Nachricht und den neuen Text indelta. Batch-Grenzen hängen davon ab, wie der Text streamt, daher verwenden Sie index und final, um den Fortschritt durch eine Nachricht zu verfolgen, anstatt zu erwarten, dass Zeilen auf eine bestimmte Weise gruppiert werden.
MessageDisplay-Ausgabe
Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, können MessageDisplay-HooksdisplayContent zurückgeben, um das Delta auf dem Bildschirm zu ersetzen:
MessageDisplay-Hooks haben keine Entscheidungskontrolle. Sie können die Nachricht nicht blockieren oder ändern, was im Transkript gespeichert oder an Claude gesendet wird.
Dieses Beispiel entfernt Markdown-Formatierung aus Claudes Antworten für eine Nur-Text-Anzeige. Das Skript liest jeden Batch von stdin, entfernt Fettdruck-Marker und Inline-Code-Backticks aus
delta und gibt das Ergebnis als displayContent zurück.
- macOS/Linux
- Windows (PowerShell)
Registrieren Sie einen Command-Hook für das Ereignis in Ihrer Einstellungsdatei:Speichern Sie dieses Skript unter Das Skript benötigt
.claude/hooks/plain-display.sh in Ihrem Projekt und machen Sie es mit chmod +x ausführbar:jq auf Ihrem PATH.jq fehlt, zeigt Claude Code den ursprünglichen Text an und notiert den Fehler nur in der Debug-Ausgabe, nicht in der Sitzung.
PreToolUse
Wird ausgeführt, nachdem Claude Tool-Parameter erstellt hat und bevor der Tool-Aufruf verarbeitet wird. Passt auf Tool-Namen:Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode und alle MCP-Tool-Namen.
Verwenden Sie PreToolUse-Entscheidungskontrolle, um die Verwendung des Tools zuzulassen, zu verweigern, um Berechtigung zu bitten oder zu verschieben.
PreToolUse-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PreToolUse-Hookstool_name, tool_input und tool_use_id. Die Felder tool_input hängen vom Tool ab:
Führt Shell-Befehle aus.
Erstellt oder überschreibt eine Datei.
Ersetzt eine Zeichenkette in einer vorhandenen Datei.
Liest Dateiinhalte.
Findet Dateien, die einem Glob-Muster entsprechen.
Durchsucht Dateiinhalte mit regulären Ausdrücken.
Ruft Web-Inhalte ab und verarbeitet sie.
Durchsucht das Web.
Spawnt einen Subagenten.
In
PostToolUse trägt tool_response für einen abgeschlossenen Agent-Aufruf den abschließenden Text des Subagenten zusammen mit Nutzungstelemetrie. Lesen Sie diese Felder, um Pro-Subagent-Kosten aus einem Hook zu erfassen:
Für Hintergrund-Subagenten gibt das Tool sofort nach dem Starten des Subagenten zurück, daher trägt
tool_response keine Nutzungsfelder. Es hat stattdessen status: "async_launched", agentId, description, prompt, outputFile und resolvedModel.
Das Feld resolvedModel nennt das Modell, auf dem der Subagent tatsächlich ausgeführt wird, das sich vom Wert model in tool_input unterscheiden kann. Es erfordert Claude Code v2.1.174 oder später.
Stellt dem Benutzer eine bis vier Multiple-Choice-Fragen.
Präsentiert einen Plan und fordert den Benutzer auf, ihn zu genehmigen, bevor Claude den Plan-Modus verlässt. Claude schreibt den Plan vor dem Aufruf des Tools in eine Datei auf der Festplatte, daher trägt die wörtliche
tool_input vom Modell nur allowedPrompts. Claude Code injiziert den Plan-Inhalt und den Dateipfad, bevor die Eingabe an Hooks übergeben wird.
In
PostToolUse ist tool_response ein Objekt mit plan- und filePath-Feldern, die den genehmigten Plan enthalten, plus interne Status-Flags. Lesen Sie tool_response.plan für den Plan-Inhalt, anstatt die Datei von der Festplatte neu zu lesen.
PreToolUse-Entscheidungskontrolle
PreToolUse-Hooks können steuern, ob ein Tool-Aufruf fortgesetzt wird. Im Gegensatz zu anderen Hooks, die ein Top-Level-Feld decision verwenden, gibt PreToolUse seine Entscheidung in einem hookSpecificOutput-Objekt zurück. Dies gibt ihm reichere Kontrolle: vier Ergebnisse (zulassen, verweigern, fragen oder verschieben) plus die Möglichkeit, die Tool-Eingabe vor der Ausführung zu ändern.
Wenn mehrere PreToolUse-Hooks unterschiedliche Entscheidungen zurückgeben, ist die Priorität
deny > defer > ask > allow.
Wenn ein Hook "ask" zurückgibt, enthält der dem Benutzer angezeigte Berechtigungsprompt ein Label, das angibt, woher der Hook stammt: zum Beispiel [User], [Project], [Plugin] oder [Local]. Dies hilft Benutzern zu verstehen, welche Konfigurationsquelle eine Bestätigung anfordert.
AskUserQuestion und ExitPlanMode erfordern Benutzerinteraktion und blockieren normalerweise im nicht-interaktiven Modus mit dem -p-Flag. Das Zurückgeben von permissionDecision: "allow" zusammen mit updatedInput erfüllt diese Anforderung: Der Hook liest die Tool-Eingabe von stdin, erfasst die Antwort über Ihre eigene Benutzeroberfläche und gibt sie in updatedInput zurück, damit das Tool ohne Aufforderung ausgeführt wird. Das Zurückgeben von "allow" allein ist nicht ausreichend für diese Tools. Für AskUserQuestion geben Sie das ursprüngliche questions-Array zurück und fügen Sie ein answers-Objekt hinzu, das jede Frage auf die gewählte Antwort abbildet.
Connector-Tools die Ihre Organisation auf ask gesetzt hat fordern auch dann auf, wenn ein Hook "allow" zurückgibt.
Ab v2.1.199 ist ein MCP-Tool, dessen Server es mit _meta["anthropic/requiresUserInteraction"] markiert, strenger: Ein Hook kann seine Genehmigungsaufforderung nicht mit "allow" überspringen, mit oder ohne updatedInput, da Claude Code nicht bestätigen kann, dass der Hook die Interaktion erfasst hat, die das Tool benötigt.
PreToolUse verwendete zuvor Top-Level-Felder
decision und reason, diese sind jedoch für dieses Ereignis veraltet. Verwenden Sie stattdessen hookSpecificOutput.permissionDecision und hookSpecificOutput.permissionDecisionReason. Die veralteten Werte "approve" und "block" werden auf "allow" und "deny" abgebildet. Andere Ereignisse wie PostToolUse und Stop verwenden weiterhin Top-Level-Felder decision und reason als ihr aktuelles Format.Ein Tool-Aufruf verschieben
"defer" ist für Integrationen, die claude -p als Subprozess ausführen und seine JSON-Ausgabe lesen, wie eine Agent SDK-App oder eine benutzerdefinierte Benutzeroberfläche, die auf Claude Code aufgebaut ist. Es ermöglicht diesem aufrufenden Prozess, Claude bei einem Tool-Aufruf zu pausieren, Eingaben über seine eigene Schnittstelle zu erfassen und dort fortzufahren, wo er aufgehört hat. Claude Code respektiert diesen Wert nur im nicht-interaktiven Modus mit dem -p-Flag. In interaktiven Sitzungen protokolliert es eine Warnung und ignoriert das Hook-Ergebnis.
Das Tool AskUserQuestion ist der typische Fall: Claude möchte den Benutzer etwas fragen, aber es gibt kein Terminal zum Antworten. Der Roundtrip funktioniert so:
- Claude ruft
AskUserQuestionauf. DerPreToolUse-Hook wird ausgelöst. - Der Hook gibt
permissionDecision: "defer"zurück. Das Tool wird nicht ausgeführt. Der Prozess beendet sich mitstop_reason: "tool_deferred"und dem ausstehenden Tool-Aufruf, der im Transkript erhalten bleibt. - Der aufrufende Prozess liest
deferred_tool_useaus dem SDK-Ergebnis, zeigt die Frage in seiner eigenen Benutzeroberfläche an und wartet auf eine Antwort. - Der aufrufende Prozess führt
claude -p --resume <session-id>aus. Der gleiche Tool-Aufruf löstPreToolUseerneut aus. - Der Hook gibt
permissionDecision: "allow"mit der Antwort inupdatedInputzurück. Das Tool wird ausgeführt und Claude setzt fort.
deferred_tool_use trägt die id, den name und die input des Tools. Die input sind die Parameter, die Claude für den Tool-Aufruf generiert hat, erfasst vor der Ausführung:
cleanupPeriodDays-Aufbewahrungssweep, die Sitzungsdateien nach 30 Tagen standardmäßig löscht. Wenn die Antwort nicht bereit ist, wenn Sie fortsetzen, kann der Hook erneut "defer" zurückgeben und der Prozess beendet sich auf die gleiche Weise. Der aufrufende Prozess steuert, wann die Schleife unterbrochen wird, indem er schließlich "allow" oder "deny" vom Hook zurückgibt.
"defer" funktioniert nur, wenn Claude einen einzelnen Tool-Aufruf in der Runde macht. Wenn Claude mehrere Tool-Aufrufe gleichzeitig macht, wird "defer" mit einer Warnung ignoriert und das Tool wird durch den normalen Berechtigungsfluss fortgesetzt. Die Einschränkung existiert, weil Resume nur einen Tool-Aufruf erneut ausführen kann: Es gibt keine Möglichkeit, einen Aufruf aus einem Batch zu verschieben, ohne die anderen ungelöst zu lassen.
Wenn das verschobene Tool nicht mehr verfügbar ist, wenn Sie fortsetzen, beendet sich der Prozess mit stop_reason: "tool_deferred_unavailable" und is_error: true bevor der Hook ausgelöst wird. Dies geschieht, wenn ein MCP-Server, der das Tool bereitgestellt hat, für die fortgesetzte Sitzung nicht verbunden ist. Die Nutzlast deferred_tool_use ist immer noch enthalten, damit Sie identifizieren können, welches Tool fehlte.
--resume stellt den Berechtigungsmodus wieder her, der aktiv war, als das Tool verschoben wurde, daher müssen Sie --permission-mode nicht erneut übergeben. Die Ausnahmen sind plan und bypassPermissions, die niemals übertragen werden. Das explizite Übergeben von --permission-mode bei der Wiederaufnahme überschreibt den wiederhergestellten Wert.PermissionRequest
Wird ausgeführt, wenn dem Benutzer ein Berechtigungsdialog angezeigt wird. Verwenden Sie PermissionRequest-Entscheidungskontrolle, um im Namen des Benutzers zuzulassen oder zu verweigern. Passt auf Tool-Namen, gleiche Werte wie PreToolUse.PermissionRequest-Eingabe
PermissionRequest-Hooks erhaltentool_name- und tool_input-Felder wie PreToolUse-Hooks, aber ohne tool_use_id. Ein optionales Array permission_suggestions enthält die Optionen „Immer zulassen”, die der Benutzer normalerweise im Berechtigungsdialog sehen würde. Der Unterschied liegt darin, wann der Hook ausgelöst wird: PermissionRequest-Hooks werden ausgeführt, wenn ein Berechtigungsdialog dem Benutzer angezeigt werden soll, während PreToolUse-Hooks vor der Tool-Ausführung unabhängig vom Berechtigungsstatus ausgeführt werden.
PermissionRequest-Entscheidungskontrolle
PermissionRequest-Hooks können Berechtigungsanfragen zulassen oder verweigern. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, kann Ihr Hook-Skript ein decision-Objekt mit diesen ereignisspezifischen Feldern zurückgeben:
Berechtigungsupdate-Einträge
Das AusgabefeldupdatedPermissions und das permission_suggestions-Eingabefeld verwenden beide das gleiche Array von Einträgen. Jeder Eintrag hat einen type, der seine anderen Felder bestimmt, und ein destination, das steuert, wo die Änderung geschrieben wird.
setMode mit bypassPermissions nimmt nur Auswirkungen an, wenn die Sitzung mit Bypass-Modus bereits verfügbar gestartet wurde: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions oder permissions.defaultMode: "bypassPermissions" in Einstellungen, und der Modus ist nicht durch permissions.disableBypassPermissionsMode deaktiviert. Andernfalls ist das Update ein No-Op. bypassPermissions wird niemals als defaultMode persistiert, unabhängig von destination.destination auf jedem Eintrag bestimmt, ob die Änderung im Speicher bleibt oder in einer Einstellungsdatei persistiert wird.
Ein Hook kann eines der
permission_suggestions widerspiegeln, die er als seine eigene updatedPermissions-Ausgabe erhalten hat, was gleichbedeutend mit der Auswahl dieser Option „Immer zulassen” durch den Benutzer im Dialog ist.
PostToolUse
Wird unmittelbar nach erfolgreichem Abschluss eines Tools ausgeführt. Passt auf Tool-Namen, gleiche Werte wie PreToolUse.PostToolUse-Eingabe
PostToolUse-Hooks werden ausgelöst, nachdem ein Tool bereits erfolgreich ausgeführt wurde. Die Eingabe enthält sowohl tool_input, die an das Tool gesendeten Argumente, als auch tool_response, das Ergebnis, das es zurückgegeben hat. Das genaue Schema für beide hängt vom Tool ab.
PostToolUse-Entscheidungskontrolle
PostToolUse-Hooks können Claude nach der Tool-Ausführung Feedback geben. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
Das Beispiel unten ersetzt die Ausgabe eines
Bash-Aufrufs. Der Ersatzwert entspricht der Ausgabeform des Bash-Tools:
PostToolUseFailure
Wird ausgeführt, wenn ein Tool, das mit der Ausführung begonnen hat, fehlschlägt: Das Tool hat einen Fehler geworfen oder ein MCP-Tool hat ein Fehlerergebnis zurückgegeben. Verwenden Sie dies, um Fehler zu protokollieren, Warnungen zu senden oder korrektes Feedback an Claude zu geben. Passt auf Tool-Namen, gleiche Werte wie PreToolUse.Dieses Ereignis wird nicht für Tool-Aufrufe ausgelöst, die vor der Ausführung abgelehnt werden: Ein unbekannter Tool-Name, Eingabe, die Schema- oder Tool-spezifische Validierung nicht besteht, oder eine Berechtigungsverweigerung. Validierungsablehnungen werden als
tool_use_error-Ergebnisse zurückgegeben und treten vor Hooks auf, daher werden weder PreToolUse noch dieses Ereignis ausgelöst. Berechtigungsverweigerungen lösen PreToolUse aus, aber nicht dieses Ereignis; siehe PermissionDenied.PostToolUseFailure-Eingabe
PostToolUseFailure-Hooks erhalten die gleichentool_name- und tool_input-Felder wie PostToolUse, zusammen mit Fehlerinformationen als Top-Level-Felder:
PostToolUseFailure-Entscheidungskontrolle
PostToolUseFailure-Hooks können Claude nach einem Tool-Fehler Kontext geben. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
PostToolBatch
Wird einmal ausgeführt, nachdem jeder Tool-Aufruf in einem Batch aufgelöst wurde, bevor Claude Code die nächste Anfrage an das Modell sendet.PostToolUse wird einmal pro Tool ausgeführt, was bedeutet, dass es gleichzeitig ausgeführt wird, wenn Claude parallele Tool-Aufrufe macht. PostToolBatch wird genau einmal mit dem vollständigen Batch ausgeführt, daher ist es der richtige Ort, um Kontext einzufügen, der von der Menge der Tools abhängt, die ausgeführt wurden, anstatt von einem einzelnen Tool. Es gibt keinen Matcher für dieses Ereignis.
PostToolBatch-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PostToolBatch-Hookstool_calls, ein Array, das jeden Tool-Aufruf im Batch beschreibt:
tool_response enthält den gleichen Inhalt, den das Modell im entsprechenden tool_result-Block erhält. Der Wert ist eine serialisierte Zeichenkette oder ein Content-Block-Array, genau wie das Tool es ausgegeben hat. Für Read bedeutet das Zeilennummern-Präfix-Text anstelle von rohen Dateiinhalten. Antworten können groß sein, daher analysieren Sie nur die Felder, die Sie benötigen.
Die Form von
tool_response unterscheidet sich von der von PostToolUse. PostToolUse übergibt das strukturierte Output-Objekt des Tools, wie {filePath: "...", success: true} für Write; PostToolBatch übergibt den serialisierten tool_result-Inhalt, den das Modell sieht.PostToolBatch-Entscheidungskontrolle
PostToolBatch-Hooks können Kontext für Claude einfügen. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
decision: "block" oder continue: false stoppt die agentengesteuerte Schleife vor dem nächsten Modell-Aufruf.
PermissionDenied
Wird ausgeführt, wenn der Auto-Mode-Klassifizierer einen Tool-Aufruf verweigert. Dieser Hook wird nur im Auto-Mode ausgelöst: Er wird nicht ausgeführt, wenn Sie einen Berechtigungsdialog manuell verweigern, wenn einPreToolUse-Hook einen Aufruf blockiert oder wenn eine deny-Regel passt. Verwenden Sie ihn, um Klassifizierer-Ablehnungen zu protokollieren, die Konfiguration anzupassen oder dem Modell zu sagen, dass es den Tool-Aufruf möglicherweise erneut versuchen kann.
Passt auf Tool-Namen, gleiche Werte wie PreToolUse.
PermissionDenied-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PermissionDenied-Hookstool_name, tool_input, tool_use_id und reason.
PermissionDenied-Entscheidungskontrolle
PermissionDenied-Hooks können dem Modell sagen, dass es den verweigerten Tool-Aufruf möglicherweise erneut versuchen kann. Geben Sie ein JSON-Objekt mithookSpecificOutput.retry auf true zurück:
retry true ist, fügt Claude Code eine Nachricht zum Gespräch hinzu, die dem Modell mitteilt, dass es den Tool-Aufruf möglicherweise erneut versuchen kann. Die Ablehnung selbst wird nicht rückgängig gemacht. Wenn Ihr Hook keine JSON zurückgibt oder retry: false zurückgibt, bleibt die Ablehnung bestehen und das Modell erhält die ursprüngliche Ablehnungsmeldung.
Notification
Wird ausgeführt, wenn Claude Code Benachrichtigungen sendet. Passt auf Benachrichtigungstyp. Matcher weglassen, um Hooks für alle Benachrichtigungstypen auszuführen.
Die Typen
agent_needs_input und agent_completed erfordern Claude Code v2.1.198 oder später.
Verwenden Sie separate Matcher, um verschiedene Handler je nach Benachrichtigungstyp auszuführen. Diese Konfiguration löst ein berechtigungsspezifisches Warnungsskript aus, wenn Claude Genehmigung benötigt, und eine andere Benachrichtigung, wenn Claude untätig war:
Notification-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten Notification-Hooksmessage mit dem Benachrichtigungstext, ein optionales title und notification_type, das angibt, welcher Typ ausgelöst wurde.
systemMessage gelten.
SubagentStart
Wird ausgeführt, wenn ein Claude Code-Subagent über das Agent-Tool spawnt wird. Unterstützt Matcher zum Filtern nach Agent-Typname. Für eingebaute Agents ist dies der Agent-Name wiegeneral-purpose, Explore oder Plan. Für benutzerdefinierte Subagenten ist dies das Feld name aus dem Frontmatter des Agenten, nicht der Dateiname.
Für Subagenten, die von einem Plugin bereitgestellt werden, ist der Agent-Typ die Plugin-spezifische Kennung wie my-plugin:reviewer, nicht der bloße Frontmatter-Name. Der Doppelpunkt platziert einen Plugin-spezifischen Namen auf dem regulären Ausdruckspfad, daher verankern Sie den Matcher mit ^ und $ für eine genaue Übereinstimmung: ^my-plugin:reviewer$.
SubagentStart-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten SubagentStart-Hooksagent_id mit der eindeutigen Kennung für den Subagenten und agent_type mit dem Agent-Namen, den der Matcher filtert.
SubagentStop
Wird ausgeführt, wenn ein Claude Code-Subagent fertig mit der Antwort ist. Passt auf Agent-Typ, gleiche Werte wie SubagentStart.SubagentStop-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten SubagentStop-Hooksstop_hook_active, agent_id, agent_type, agent_transcript_path und last_assistant_message. Das Feld agent_type ist der Wert, der zum Filtern von Matchern verwendet wird. Der transcript_path ist das Transkript der Hauptsitzung, während agent_transcript_path das eigene Transkript des Subagenten ist, das in einem verschachtelten subagents/-Ordner gespeichert ist. Das Feld last_assistant_message enthält den Textinhalt der letzten Antwort des Subagenten, daher können Hooks darauf zugreifen, ohne die Transkript-Datei zu analysieren.
SubagentStop-Hooks erhalten auch die Arrays background_tasks und session_crons, die unter Stop-Eingabe beschrieben sind, verfügbar in Claude Code v2.1.145 oder später. Beide Arrays sind auf die übergeordnete Sitzung beschränkt, nicht auf den Subagenten.
hookSpecificOutput.additionalContext mit hookEventName auf "SubagentStop" gesetzt, für Nicht-Fehler-Feedback, das den Subagenten am Laufen hält. Das Zurückgeben von decision: "block" mit einem reason hält den Subagenten am Laufen und liefert reason an den Subagenten als nächste Anweisung. Um Kontext in die übergeordnete Sitzung nach der Rückkehr eines Subagenten einzufügen, verwenden Sie stattdessen einen PostToolUse-Hook auf dem Agent-Tool.
TaskCreated
Wird ausgeführt, wenn eine Aufgabe über dasTaskCreate-Tool erstellt wird. Verwenden Sie dies, um Benennungskonventionen durchzusetzen, Aufgabenbeschreibungen zu erfordern oder zu verhindern, dass bestimmte Aufgaben erstellt werden.
Wenn ein TaskCreated-Hook mit Code 2 beendet wird, wird die Aufgabe nicht erstellt und die stderr-Nachricht wird dem Modell als Feedback zurückgegeben. Um den Teammate stattdessen vollständig zu stoppen, geben Sie JSON mit {"continue": false, "stopReason": "..."} zurück. TaskCreated-Hooks unterstützen keine Matcher und werden bei jedem Auftreten ausgelöst.
TaskCreated-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten TaskCreated-Hookstask_id, task_subject und optional task_description, teammate_name und team_name.
TaskCreated-Entscheidungskontrolle
TaskCreated-Hooks unterstützen zwei Möglichkeiten, die Aufgabenerstellung zu steuern:- Exit-Code 2: Die Aufgabe wird nicht erstellt und die stderr-Nachricht wird dem Modell als Feedback zurückgegeben.
- JSON
{"continue": false, "stopReason": "..."}: Stoppt den Teammate vollständig, was demStop-Hook-Verhalten entspricht. DerstopReasonwird dem Benutzer angezeigt.
TaskCompleted
Wird ausgeführt, wenn eine Aufgabe als abgeschlossen markiert wird. Dies wird in zwei Situationen ausgelöst: wenn ein Agent eine Aufgabe explizit über das TaskUpdate-Tool als abgeschlossen markiert, oder wenn ein Agent-Team-Teammate seine Runde mit laufenden Aufgaben beendet. Verwenden Sie dies, um Abschluss-Kriterien wie bestandene Tests oder Lint-Checks durchzusetzen, bevor eine Aufgabe geschlossen werden kann. Wenn einTaskCompleted-Hook mit Code 2 beendet wird, wird die Aufgabe nicht als abgeschlossen markiert und die stderr-Nachricht wird dem Modell als Feedback zurückgegeben. Um den Teammate stattdessen vollständig zu stoppen, geben Sie JSON mit {"continue": false, "stopReason": "..."} zurück. TaskCompleted-Hooks unterstützen keine Matcher und werden bei jedem Auftreten ausgelöst.
TaskCompleted-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten TaskCompleted-Hookstask_id, task_subject und optional task_description, teammate_name und team_name.
TaskCompleted-Entscheidungskontrolle
TaskCompleted-Hooks unterstützen zwei Möglichkeiten, den Aufgabenabschluss zu steuern:- Exit-Code 2: Die Aufgabe wird nicht als abgeschlossen markiert und die stderr-Nachricht wird dem Modell als Feedback zurückgegeben.
- JSON
{"continue": false, "stopReason": "..."}: Stoppt den Teammate vollständig, was demStop-Hook-Verhalten entspricht. DerstopReasonwird dem Benutzer angezeigt.
Stop
Wird ausgeführt, wenn der Haupt-Claude Code-Agent fertig mit der Antwort ist. Wird nicht ausgeführt, wenn der Stopp durch eine Benutzerunterbrechung verursacht wurde. API-Fehler lösen stattdessen StopFailure aus.Stop-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten Stop-Hooksstop_hook_active, last_assistant_message, background_tasks und session_crons. Das Feld stop_hook_active ist true, wenn Claude Code bereits als Ergebnis eines Stop-Hooks fortgesetzt wird. Überprüfen Sie diesen Wert oder verarbeiten Sie das Transkript, um zu verhindern, dass Claude Code unbegrenzt läuft. Claude Code überschreibt den Hook und beendet die Runde nach 8 aufeinanderfolgenden Blockierungen.
Das Feld last_assistant_message enthält den Textinhalt von Claudes letzter Antwort, daher können Hooks darauf zugreifen, ohne die Transkript-Datei zu analysieren.
Die Arrays background_tasks und session_crons, verfügbar in Claude Code v2.1.145 oder später, ermöglichen es Hooks, zwischen „Sitzung ist fertig” und „Sitzung ist pausiert und wartet auf Hintergrundarbeit, um sie wieder zu aktivieren” zu unterscheiden. Beide Arrays sind vorhanden, wenn die Task-Registry erreichbar ist, und sind leer, wenn nichts läuft oder geplant ist.
Jeder Eintrag in background_tasks beschreibt eine laufende Aufgabe und verwendet diese Felder:
Jeder Eintrag in
session_crons beschreibt einen sitzungsspezifischen geplanten Wakeup, stammt von CronCreate, ScheduleWakeup und /loop:
Dieses Beispiel zeigt eine Stop-Eingabe mit einer laufenden Shell-Aufgabe und einem wiederkehrenden Cron:
Stop-Entscheidungskontrolle
Stop- und SubagentStop-Hooks können steuern, ob Claude fortgesetzt wird. Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
additionalContext, wenn der Hook wie beabsichtigt funktioniert und Claude Anleitung gibt, wie „Führen Sie die Test-Suite aus, bevor Sie fertig sind”. Es hält das Gespräch durch die gleichen Schleifenschutzmechanismen wie decision: "block" am Laufen, nämlich die stop_hook_active-Eingabe und die 8-aufeinanderfolgende-Fortsetzungs-Obergrenze, aber das Transkript kennzeichnet es als Stop hook feedback und es wird keine Hook-Fehler-Benachrichtigung angezeigt:
StopFailure
Wird stattdessen von Stop ausgeführt, wenn die Runde aufgrund eines API-Fehlers endet. Ausgabe und Exit-Code werden ignoriert. Verwenden Sie dies, um Fehler zu protokollieren, Warnungen zu senden oder Wiederherstellungsmaßnahmen zu ergreifen, wenn Claude aufgrund von Ratenlimits, Authentifizierungsproblemen oder anderen API-Fehlern keine Antwort abschließen kann.StopFailure-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten StopFailure-Hookserror, optionales error_details und optionales last_assistant_message. Das Feld error identifiziert den Fehlertyp und wird zum Filtern von Matchern verwendet.
TeammateIdle
Wird ausgeführt, wenn ein Agent-Team-Teammate nach Abschluss seiner Runde untätig werden soll. Verwenden Sie dies, um Qualitätsgates vor dem Stoppen eines Teammates durchzusetzen, wie das Erfordern von bestandenen Lint-Checks oder das Überprüfen, dass Ausgabedateien vorhanden sind. Wenn einTeammateIdle-Hook mit Code 2 beendet wird, erhält der Teammate die stderr-Nachricht als Feedback und arbeitet weiter, anstatt untätig zu werden. Um den Teammate stattdessen vollständig zu stoppen, geben Sie JSON mit {"continue": false, "stopReason": "..."} zurück. TeammateIdle-Hooks unterstützen keine Matcher und werden bei jedem Auftreten ausgelöst.
TeammateIdle-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten TeammateIdle-Hooksteammate_name und team_name.
TeammateIdle-Entscheidungskontrolle
TeammateIdle-Hooks unterstützen zwei Möglichkeiten, das Teammate-Verhalten zu steuern:- Exit-Code 2: Der Teammate erhält die stderr-Nachricht als Feedback und arbeitet weiter, anstatt untätig zu werden.
- JSON
{"continue": false, "stopReason": "..."}: Stoppt den Teammate vollständig, was demStop-Hook-Verhalten entspricht. DerstopReasonwird dem Benutzer angezeigt.
ConfigChange
Wird ausgeführt, wenn sich eine Konfigurationsdatei während einer Sitzung ändert. Verwenden Sie dies, um Einstellungsänderungen zu überprüfen, Sicherheitsrichtlinien durchzusetzen oder nicht autorisierte Änderungen an Konfigurationsdateien zu blockieren. ConfigChange-Hooks werden für Änderungen an Einstellungsdateien, verwalteten Richtlinieneinstellungen und Skill-Dateien ausgelöst. Das Feldsource in der Eingabe teilt Ihnen mit, welche Art von Konfiguration sich geändert hat, und das optionale Feld file_path gibt den Pfad zur geänderten Datei an.
Der Matcher filtert auf die Konfigurationsquelle:
Dieses Beispiel protokolliert alle Konfigurationsänderungen für Sicherheitsaudits:
ConfigChange-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten ConfigChange-Hookssource und optional file_path. Das Feld source gibt an, welche Konfigurationsart sich geändert hat, und file_path gibt den Pfad zur spezifischen Datei an, die geändert wurde.
ConfigChange-Entscheidungskontrolle
ConfigChange-Hooks können Konfigurationsänderungen von der Anwendung blockieren. Verwenden Sie Exit-Code 2 oder ein JSONdecision, um die Änderung zu verhindern. Wenn blockiert, werden die neuen Einstellungen nicht auf die laufende Sitzung angewendet.
policy_settings-Änderungen können nicht blockiert werden. Hooks werden immer noch für policy_settings-Quellen ausgelöst, daher können Sie sie für Audit-Protokollierung verwenden, aber jede Blockierungsentscheidung wird ignoriert. Dies stellt sicher, dass von Unternehmen verwaltete Einstellungen immer wirksam werden.
CwdChanged
Wird ausgeführt, wenn das Arbeitsverzeichnis während einer Sitzung wechselt, zum Beispiel wenn Claude einencd-Befehl ausführt. Verwenden Sie dies, um auf Verzeichniswechsel zu reagieren: Laden Sie Umgebungsvariablen neu, aktivieren Sie projektspezifische Toolchains oder führen Sie Setup-Skripte automatisch aus. Paare mit FileChanged für Tools wie direnv, die verzeichnisspezifische Umgebungen verwalten.
CwdChanged-Hooks haben Zugriff auf CLAUDE_ENV_FILE. Variablen, die in diese Datei geschrieben werden, bleiben in nachfolgenden Bash-Befehlen für die Sitzung erhalten, genau wie in SessionStart-Hooks.
CwdChanged unterstützt keine Matcher und wird bei jedem Verzeichniswechsel ausgelöst.
CwdChanged-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten CwdChanged-Hooksold_cwd und new_cwd.
CwdChanged-Ausgabe
Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, können CwdChanged-HookswatchPaths zurückgeben, um dynamisch zu setzen, welche Dateipfade FileChanged überwacht:
CwdChanged-Hooks haben keine Entscheidungskontrolle. Sie können den Verzeichniswechsel nicht blockieren.
FileChanged
Wird ausgeführt, wenn sich eine überwachte Datei auf der Festplatte ändert. Nützlich zum Neuladen von Umgebungsvariablen, wenn Projekt-Konfigurationsdateien geändert werden. Das Feldmatcher für dieses Ereignis dient zwei Zwecken:
- Erstellen Sie die Überwachungsliste: Der Wert wird auf
|aufgeteilt und jedes Segment wird als Dateiname im Arbeitsverzeichnis registriert, daher überwacht".envrc|.env"genau diese zwei Dateien. Regex-Muster sind hier nicht nützlich: Ein Wert wie^\.envwürde eine Datei überwachen, die buchstäblich^\.envheißt. - Filtern Sie, welche Hooks ausgeführt werden: Wenn sich eine überwachte Datei ändert, wird der gleiche Wert verwendet, um zu filtern, welche Hook-Gruppen ausgeführt werden, wobei die Standard-Matcher-Regeln gegen den Basename der geänderten Datei verwendet werden.
CLAUDE_ENV_FILE. Variablen, die in diese Datei geschrieben werden, bleiben in nachfolgenden Bash-Befehlen für die Sitzung erhalten, genau wie in SessionStart-Hooks.
FileChanged-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten FileChanged-Hooksfile_path und event.
FileChanged-Ausgabe
Zusätzlich zu den JSON-Ausgabefeldern, die für alle Hooks verfügbar sind, können FileChanged-HookswatchPaths zurückgeben, um dynamisch zu aktualisieren, welche Dateipfade überwacht werden:
FileChanged-Hooks haben keine Entscheidungskontrolle. Sie können die Dateiänderung nicht blockieren.
WorktreeCreate
Wird ausgeführt, wenn ein Worktree erstellt wird, entweder vonclaude --worktree oder von einem Subagent mit isolation: "worktree". Standardmäßig erstellt Claude Code die isolierte Arbeitskopie mit git worktree. Wenn Sie einen WorktreeCreate-Hook konfigurieren, ersetzt er das Standard-Git-Verhalten und ermöglicht es Ihnen, ein anderes Versionskontrollsystem wie SVN, Perforce oder Mercurial zu verwenden.
Da der Hook das Standard-Verhalten vollständig ersetzt, wird .worktreeinclude nicht verarbeitet. Wenn Sie lokale Konfigurationsdateien wie .env in den neuen Worktree kopieren müssen, tun Sie dies in Ihrem Hook-Skript.
Der Hook muss den absoluten Pfad zum erstellten Worktree-Verzeichnis zurückgeben. Claude Code verwendet diesen Pfad als Arbeitsverzeichnis für die isolierte Sitzung. Siehe WorktreeCreate-Ausgabe für wie jeder Hook-Typ den Pfad zurückgibt.
Dieses Beispiel erstellt eine SVN-Arbeitskopie und gibt den Pfad aus, damit Claude Code ihn verwenden kann. Ersetzen Sie die Repository-URL durch Ihre eigene:
name aus der JSON-Eingabe auf stdin, checkt eine frische Kopie in ein neues Verzeichnis aus und gibt den Verzeichnispath aus. Das echo in der letzten Zeile ist das, was Claude Code als Worktree-Pfad liest. Leiten Sie jede andere Ausgabe zu stderr um, damit sie nicht mit dem Pfad interferiert.
WorktreeCreate-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten WorktreeCreate-Hooks das Feldname. Dies ist eine Slug-Kennung für den neuen Worktree, entweder vom Benutzer angegeben oder automatisch generiert, zum Beispiel bold-oak-a3f2.
WorktreeCreate-Ausgabe
WorktreeCreate-Hooks verwenden nicht das Standard-Allow/Block-Entscheidungsmodell. Stattdessen bestimmt der Erfolg oder Misserfolg des Hooks das Ergebnis. Der Hook muss den absoluten Pfad zum erstellten Worktree-Verzeichnis zurückgeben:- Command-Hooks (
type: "command"): geben den Pfad auf stdout aus. Claude Code entfernt ANSI-Escape-Codes, bevor diese Zeile gelesen wird, daher werden Shell-Startup-Banner, die vor Ihremechogedruckt werden, ignoriert. Leiten Sie jede andere Hook-Ausgabe zu stderr um. - HTTP-Hooks (
type: "http"): geben{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }im Response-Body zurück.
-p stagnierte es etwa 30 Sekunden, bevor es mit Code 0 beendet wurde.
WorktreeRemove
Wird ausgeführt, wenn ein Worktree entfernt wird, entweder wenn Sie eine--worktree-Sitzung beenden und wählen, sie zu entfernen, oder wenn ein Subagent mit isolation: "worktree" fertig ist. Dies ist das Bereinigungspendant zu WorktreeCreate.
Für Git-basierte Worktrees handhabt Claude Code die Bereinigung automatisch mit git worktree remove. Wenn Sie einen WorktreeCreate-Hook für ein nicht-Git-Versionskontrollsystem konfiguriert haben, koppeln Sie ihn mit einem WorktreeRemove-Hook, um die Bereinigung zu handhaben. Ohne einen wird das Worktree-Verzeichnis auf der Festplatte belassen.
Claude Code übergibt den Pfad, den WorktreeCreate zurückgegeben hat, als worktree_path in der Hook-Eingabe. Dieses Beispiel liest diesen Pfad und entfernt das Verzeichnis:
WorktreeRemove-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten WorktreeRemove-Hooks das Feldworktree_path, das der absolute Pfad zum entfernten Worktree ist.
PreCompact
Wird ausgeführt, bevor Claude Code einen Komprimierungsvorgang ausführen soll. Der Matcher-Wert gibt an, ob die Komprimierung manuell oder automatisch ausgelöst wurde:
Exit mit Code 2, um die Komprimierung zu blockieren. Für ein manuelles
/compact wird die stderr-Nachricht dem Benutzer angezeigt. Sie können auch blockieren, indem Sie JSON mit "decision": "block" zurückgeben.
Das Blockieren der automatischen Komprimierung hat unterschiedliche Auswirkungen, je nachdem, wann es ausgelöst wird. Wenn die Komprimierung proaktiv ausgelöst wurde, bevor das Kontextlimit erreicht wurde, überspringt Claude Code sie und das Gespräch wird unkomprimiert fortgesetzt. Wenn die Komprimierung ausgelöst wurde, um sich von einem Kontextlimit-Fehler zu erholen, der bereits von der API zurückgegeben wurde, wird der zugrunde liegende Fehler angezeigt und die aktuelle Anfrage schlägt fehl.
PreCompact-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PreCompact-Hookstrigger und custom_instructions. Für manual enthält custom_instructions das, was der Benutzer in /compact übergibt. Für auto ist custom_instructions leer.
PostCompact
Wird ausgeführt, nachdem Claude Code einen Komprimierungsvorgang abgeschlossen hat. Verwenden Sie dieses Ereignis, um auf den neuen komprimierten Zustand zu reagieren, zum Beispiel um die generierte Zusammenfassung zu protokollieren oder den externen Zustand zu aktualisieren. Die gleichen Matcher-Werte gelten wie fürPreCompact:
PostCompact-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PostCompact-Hookstrigger und compact_summary. Das Feld compact_summary enthält die Gesprächszusammenfassung, die durch den Komprimierungsvorgang generiert wurde.
SessionEnd
Wird ausgeführt, wenn eine Claude Code-Sitzung endet. Nützlich für Bereinigungsaufgaben, Protokollierung von Sitzungsstatistiken oder Speicherung des Sitzungsstatus. Unterstützt Matcher zum Filtern nach Ausstiegsgrund. Das Feldreason in der Hook-Eingabe gibt an, warum die Sitzung endete:
SessionEnd-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten SessionEnd-Hooks ein Feldreason, das angibt, warum die Sitzung endete. Siehe die Grundtabelle oben für alle Werte.
/clear und das Wechseln von Sitzungen über interaktives /resume. Wenn ein Hook mehr Zeit benötigt, setzen Sie eine Pro-Hook-timeout in der Hook-Konfiguration. Das Gesamtbudget wird automatisch auf das höchste Pro-Hook-Timeout erhöht, das in Einstellungsdateien konfiguriert ist, bis zu 60 Sekunden. Timeouts, die auf Plugin-bereitgestellten Hooks gesetzt sind, erhöhen das Budget nicht. Um das Budget explizit zu überschreiben, setzen Sie die Umgebungsvariable CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS in Millisekunden.
Elicitation
Wird ausgeführt, wenn ein MCP-Server Benutzereingaben während einer Aufgabe anfordert. Standardmäßig zeigt Claude Code einen interaktiven Dialog für die Benutzerantwort an. Hooks können diese Anfrage abfangen und programmatisch antworten, wodurch der Dialog vollständig übersprungen wird. Das Matcher-Feld passt auf den MCP-Server-Namen.Elicitation-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten Elicitation-Hooksmcp_server_name, message und optionale Felder mode, url, elicitation_id und requested_schema.
Für Form-Mode-Elicitation (der häufigste Fall):
Elicitation-Ausgabe
Um programmatisch ohne Anzeige des Dialogs zu antworten, geben Sie ein JSON-Objekt mithookSpecificOutput zurück:
Exit-Code 2 verweigert die Elicitation und zeigt stderr dem Benutzer an.
ElicitationResult
Wird ausgeführt, nachdem ein Benutzer auf eine MCP-Elicitation antwortet. Hooks können die Antwort beobachten, ändern oder blockieren, bevor sie an den MCP-Server zurückgesendet wird. Das Matcher-Feld passt auf den MCP-Server-Namen.ElicitationResult-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten ElicitationResult-Hooksmcp_server_name, action und optionale Felder mode, elicitation_id und content.
ElicitationResult-Ausgabe
Um die Antwort des Benutzers zu überschreiben, geben Sie ein JSON-Objekt mithookSpecificOutput zurück:
Exit-Code 2 blockiert die Antwort, wodurch die effektive Aktion zu
decline wird.
Prompt-basierte Hooks
Zusätzlich zu Command-, HTTP- und MCP-Tool-Hooks unterstützt Claude Code Prompt-basierte Hooks (type: "prompt"), die ein LLM verwenden, um zu evaluieren, ob eine Aktion zuzulassen oder zu blockieren ist, und Agent-Hooks (type: "agent"), die einen agentengesteuerten Verifizierer mit Tool-Zugriff spawnen. Nicht alle Ereignisse unterstützen jeden Hook-Typ.
Ereignisse, die alle fünf Hook-Typen unterstützen (command, http, mcp_tool, prompt und agent):
PermissionDeniedPermissionRequestPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
command, http und mcp_tool Hooks unterstützen, aber nicht prompt oder agent:
ConfigChangeCwdChangedElicitationElicitationResultFileChangedInstructionsLoadedNotificationPostCompactPreCompactSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart und Setup unterstützen command und mcp_tool Hooks. Sie unterstützen keine http, prompt oder agent Hooks.
Wie Prompt-basierte Hooks funktionieren
Anstatt einen Bash-Befehl auszuführen, Prompt-basierte Hooks:- Senden die Hook-Eingabe und Ihren Prompt an ein Claude-Modell, standardmäßig Haiku
- Das LLM antwortet mit strukturiertem JSON, das eine Entscheidung enthält
- Claude Code verarbeitet die Entscheidung automatisch
Prompt-Hook-Konfiguration
Setzen Sietype auf "prompt" und geben Sie eine prompt-Zeichenkette anstelle eines command an. Verwenden Sie den Platzhalter $ARGUMENTS, um die Hook-Eingabedaten in Ihren Prompt-Text einzufügen. Claude Code sendet den kombinierten Prompt und die Eingabe an ein schnelles Claude-Modell, das eine JSON-Entscheidung zurückgibt.
Dieser Stop-Hook fragt das LLM, ob Claude stoppen sollte, bevor Claude beendet wird:
Response-Schema
Das LLM muss mit JSON antworten, das Folgendes enthält:
Was bei
ok: false passiert, hängt vom Ereignis ab:
StopundSubagentStop: der Grund wird an Claude als nächste Anweisung zurückgegeben und der Turn wird fortgesetztPreToolUse: der Tool-Aufruf wird verweigert und der Grund wird an Claude als Tool-Fehler zurückgegeben, gleichbedeutend mit einem Command-Hook mitpermissionDecision: "deny"PostToolUse: standardmäßig endet der Turn und der Grund wird im Chat als Warnzeile angezeigt. Setzen SiecontinueOnBlock: true, um den Grund an Claude zurückzugeben und den Turn stattdessen fortzusetzenPostToolBatch,UserPromptSubmitundUserPromptExpansion: der Turn endet und der Grund wird als Warnzeile angezeigt. Diese Ereignisse beenden den Turn beidecision: "block"unabhängig voncontinuePostToolUseFailure,TaskCreatedundTaskCompleted: der Grund wird an Claude als Tool-Fehler zurückgegeben, ähnlich wiePreToolUseTeammateIdle: standardmäßig stoppt der Teammate und der Grund wird als Warnzeile angezeigt. Setzen SiecontinueOnBlock: true, um den Grund an den Teammate zurückzugeben und ihn stattdessen weiterarbeiten zu lassenPermissionRequest:ok: falsehat keine Auswirkung. Um eine Genehmigung von einem Hook zu verweigern, verwenden Sie einen Command-Hook mithookSpecificOutput.decision.behavior: "deny"PermissionDenied:ok: falsehat keine Auswirkung, da die Verweigerung bereits erfolgt ist. Die einzige Ausgabe, die dieses Ereignis liest, isthookSpecificOutput.retry, die Prompt- und Agent-Hooks nicht setzen können — sie werden bei diesem Ereignis ausgeführt, aber ihre Ausgabe wird verworfen. Verwenden Sie einen Command-Hook, umretryzurückzugeben
Mehrere Bedingungen vor dem Stoppen überprüfen
DieserStop-Hook verwendet einen detaillierten Prompt, um drei Bedingungen zu überprüfen, bevor Claude stoppen darf. SubagentStop-Hooks verwenden das gleiche Format, um zu evaluieren, ob ein Subagent stoppen sollte. Wenn "ok" false ist, setzt Claude die Arbeit mit dem bereitgestellten Grund als nächste Anweisung fort:
Agent-basierte Hooks
Agent-basierte Hooks (type: "agent") sind wie Prompt-basierte Hooks, aber mit Multi-Turn-Tool-Zugriff. Anstelle eines einzelnen LLM-Aufrufs spawnt ein Agent-Hook einen Subagenten, der Dateien lesen, Code durchsuchen und die Codebasis überprüfen kann, um Bedingungen zu überprüfen. Agent-Hooks unterstützen die gleichen Ereignisse wie Prompt-basierte Hooks.
Wie Agent-Hooks funktionieren
Wenn ein Agent-Hook ausgelöst wird:- Claude Code spawnt einen Subagenten mit Ihrem Prompt und der Hook-Eingabe
- Der Subagent kann Tools wie Read, Grep und Glob verwenden, um zu untersuchen
- Nach bis zu 50 Turns gibt der Subagent eine strukturierte
{ "ok": true/false }-Entscheidung zurück - Claude Code verarbeitet die Entscheidung auf die gleiche Weise wie ein Prompt-Hook
Agent-Hook-Konfiguration
Setzen Sietype auf "agent" und geben Sie eine prompt-Zeichenkette an. Die Konfigurationsfelder sind die gleichen wie Prompt-Hooks, mit einem längeren Standard-Timeout:
Das Response-Schema ist das gleiche wie Prompt-Hooks:
{ "ok": true } zum Zulassen oder { "ok": false, "reason": "..." } zum Blockieren.
Dieser Stop-Hook überprüft, dass alle Unit-Tests bestanden sind, bevor Claude fertig ist:
Hooks im Hintergrund ausführen
Standardmäßig blockieren Hooks die Ausführung von Claude, bis sie abgeschlossen sind. Für lang laufende Aufgaben wie Bereitstellungen, Test-Suites oder externe API-Aufrufe setzen Sie"async": true, um den Hook im Hintergrund auszuführen, während Claude weiterarbeitet. Asynchrone Hooks können nicht blockieren oder das Verhalten von Claude steuern: Response-Felder wie decision, permissionDecision und continue haben keine Auswirkung, da die Aktion, die sie steuern würden, bereits abgeschlossen ist.
Konfigurieren Sie einen asynchronen Hook
Fügen Sie"async": true zur Konfiguration eines Command-Hooks hinzu, um ihn im Hintergrund auszuführen, ohne Claude zu blockieren. Dieses Feld ist nur auf type: "command"-Hooks verfügbar.
Dieser Hook führt ein Test-Skript nach jedem Write-Tool-Aufruf aus. Claude arbeitet sofort weiter, während run-tests.sh bis zu 120 Sekunden ausgeführt wird. Wenn das Skript fertig ist, wird seine Ausgabe beim nächsten Gesprächsturn geliefert:
timeout setzt die maximale Zeit in Sekunden für den Hintergrund-Prozess. Wenn nicht angegeben, verwenden asynchrone Hooks das gleiche 10-Minuten-Standard wie synchrone Hooks.
Wie asynchrone Hooks ausgeführt werden
Wenn ein asynchroner Hook ausgelöst wird, startet Claude Code den Hook-Prozess und setzt sofort fort, ohne auf den Abschluss zu warten. Der Hook erhält die gleiche JSON-Eingabe über stdin wie ein synchroner Hook. Nachdem der Hintergrund-Prozess beendet ist, wenn der Hook eine JSON-Response mit einemadditionalContext-Feld erzeugt hat, wird dieser Inhalt Claude beim nächsten Gesprächsturn als Kontext geliefert. Ein systemMessage-Feld wird Ihnen angezeigt, nicht Claude.
Claude Code validiert diese JSON-Response gegen das gleiche Ausgabeschema wie synchrone Hooks und verwirft jedes Feld, dessen Wert den falschen Typ hat, wie z. B. eine systemMessage, die keine Zeichenkette ist, anstatt es zu liefern. Führen Sie mit --debug aus, um eine Warnung zu sehen, die jedes verworfene Feld benennt. Vor v2.1.202 konnte fehlerhafte JSON-Ausgabe von einem asynchronen Hook die Sitzung zum Absturz bringen, und der Absturz trat jedes Mal auf, wenn die Sitzung fortgesetzt wurde.
Benachrichtigungen über den Abschluss asynchroner Hooks werden standardmäßig unterdrückt. Um sie zu sehen, aktivieren Sie den ausführlichen Modus mit Ctrl+O oder starten Sie Claude Code mit --verbose.
Tests nach Dateiänderungen ausführen
Dieser Hook startet eine Test-Suite im Hintergrund, wenn Claude eine Datei schreibt, und meldet die Ergebnisse Claude, wenn die Tests fertig sind. Speichern Sie dieses Skript unter.claude/hooks/run-tests-async.sh in Ihrem Projekt und machen Sie es mit chmod +x ausführbar:
.claude/settings.json im Projekt-Root hinzu. Das Flag async: true ermöglicht es Claude, weiterarbeiten zu können, während Tests ausgeführt werden:
Einschränkungen
Asynchrone Hooks haben mehrere Einschränkungen im Vergleich zu synchronen Hooks:- Nur
type: "command"-Hooks unterstützenasync. Prompt-basierte Hooks können nicht asynchron ausgeführt werden. - Asynchrone Hooks können nicht blockieren oder Entscheidungen zurückgeben. Zu dem Zeitpunkt, an dem der Hook abgeschlossen ist, hat die auslösende Aktion bereits stattgefunden.
- Hook-Ausgabe wird beim nächsten Gesprächsturn geliefert. Wenn die Sitzung untätig ist, wartet die Response, bis die nächste Benutzerinteraktion erfolgt. Ausnahme: Ein
asyncRewake-Hook, der mit Code 2 beendet wird, weckt Claude sofort auf, auch wenn die Sitzung untätig ist. - Jede Ausführung erstellt einen separaten Hintergrund-Prozess. Es gibt keine Deduplizierung über mehrere Auslösungen des gleichen asynchronen Hooks.
Sicherheitsüberlegungen
Haftungsausschluss
Command-Hooks werden mit den vollständigen Berechtigungen Ihres System-Benutzers ausgeführt.Best Practices für Sicherheit
Beachten Sie diese Praktiken beim Schreiben von Hooks:- Validieren und bereinigen Sie Eingaben: Vertrauen Sie niemals blind auf Eingabedaten
- Zitieren Sie immer Shell-Variablen: Verwenden Sie
"$VAR"nicht$VAR - Blockieren Sie Pfad-Traversal: Prüfen Sie auf
..in Dateipfaden - Verwenden Sie absolute Pfade: Geben Sie vollständige Pfade für Skripte an. In der Exec-Form verwenden Sie
${CLAUDE_PROJECT_DIR}und der Pfad benötigt keine Anführungszeichen. In der Shell-Form wickeln Sie ihn in doppelte Anführungszeichen ein - Überspringen Sie sensible Dateien: Vermeiden Sie
.env,.git/, Schlüssel, etc.
Windows PowerShell-Tool
Unter Windows können Sie einzelne Hooks in PowerShell ausführen, indem Sie"shell": "powershell" auf einem Command-Hook setzen. Hooks spawnen PowerShell direkt, daher funktioniert dies unabhängig davon, ob CLAUDE_CODE_USE_POWERSHELL_TOOL gesetzt ist. Claude Code erkennt automatisch pwsh.exe, die PowerShell 7 und später ausführbare Datei, und fällt auf powershell.exe für Windows PowerShell 5.1 zurück.
${CLAUDE_PROJECT_DIR} oder $env:CLAUDE_PROJECT_DIR. Ab v2.1.198 schreibt Claude Code die Platzhalter ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} und ${CLAUDE_PLUGIN_DATA} in einem PowerShell-Shell-Form-Befehl in die PowerShell-Form ${env:NAME} um, unabhängig davon, ob der Hook in settings.json, einem Plugin oder einer Skill definiert ist. PowerShell löst dann den Wert aus der exportierten Umgebung nach dem Parsing auf, daher funktioniert der Platzhalter in doppelt angeführten Zeichenketten, aber nicht in einfach angeführten Zeichenketten, wo PowerShell niemals Variablen erweitert.
Vor v2.1.198 galt dieses Umschreiben nur für Plugin-Hooks. In früheren Versionen benötigt ein settings.json-Hook die Form $env: oder Exec-Form, wobei ${CLAUDE_PROJECT_DIR} in jedem args-Element ersetzt wird, unabhängig davon, wo der Hook definiert ist.
Schreiben Sie nicht die bloße Schreibweise $CLAUDE_PROJECT_DIR in einem PowerShell-Hook. PowerShell analysiert sie als undefinierte lokale Variable und löst sie zu $null auf, was den Skriptpfad ohne sein Projektverzeichnis-Präfix hinterlässt. Claude Code schreibt diese Form nicht um; stattdessen protokolliert es eine Warnung im Debug-Log.
Das folgende Beispiel zeigt einen settings.json-Hook, der ein Projektskript mit der Form $env: ausführt, die auf jeder Version funktioniert:
Debug-Hooks
Hook-Ausführungsdetails, einschließlich welche Hooks passten, ihre Exit-Codes und vollständige stdout und stderr, werden in die Debug-Log-Datei geschrieben. Starten Sie Claude Code mitclaude --debug-file <path>, um das Log in einen bekannten Speicherort zu schreiben, oder führen Sie claude --debug aus und lesen Sie das Log unter ~/.claude/debug/<session-id>.txt. Das Flag --debug gibt nicht auf dem Terminal aus.
CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose, um zusätzliche Log-Zeilen wie Hook-Matcher-Zählungen und Query-Matching zu sehen.
Zur Fehlerbehebung häufiger Probleme wie Hooks, die nicht ausgelöst werden, Stop-Hooks, die weiterhin blockieren, oder Konfigurationsfehler, siehe Einschränkungen und Fehlerbehebung in der Anleitung. Für eine umfassendere diagnostische Anleitung, die /context, /doctor und Einstellungspriorität abdeckt, siehe Debug your config.