Hooks-Referenz
Referenz für Claude Code Hook-Ereignisse, Konfigurationsschema, JSON-Ein-/Ausgabeformate, Exit-Codes, asynchrone Hooks, HTTP-Hooks, Prompt-Hooks und MCP-Tool-Hooks.
[!TIP]
Eine Schnellstartanleitung mit Beispielen finden Sie unter Hooks automatisieren.
Hooks sind benutzerdefinierte Shell-Befehle, HTTP-Endpunkte, MCP-Tool-Aufrufe, LLM-Prompts oder Subagenten, die automatisch an bestimmten Punkten im Lebenszyklus von Claude Code ausgeführt werden. Claude Code löst die gleichen Hook-Ereignisse überall aus, wo es läuft: Sitzungen im Terminal, IDE-Erweiterungen, die Desktop-App und Cloud-Sitzungen. Verwenden Sie diese Referenz, um Ereignisschemas, Konfigurationsoptionen, JSON-Ein-/Ausgabeformate und erweiterte Funktionen wie asynchrone Hooks, HTTP-Hooks und MCP-Tool-Hooks nachzuschlagen.
Ein Plugin kann auch Hooks als JavaScript-Funktionen registrieren, die Claude Code in seinem eigenen Prozess aufruft, die sowohl in der Benutzeroberfläche angezeigt als auch auf Ereignisse reagieren können. Ein Plugin, das dies tut, ist ein mod, und diese Funktions-Hooks werden in Auf Ereignisse reagieren behandelt, nicht hier. Die Hooks auf dieser Seite funktionieren weiterhin neben Mods.
Hook-Lebenszyklus
Claude Code führt Hooks an bestimmten Punkten während einer Sitzung aus. 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:
- pro Sitzung:
SessionStartundSessionEnd - pro Runde:
UserPromptSubmit,StopundStopFailure - bei jedem Tool-Aufruf innerhalb der agentengesteuerten Schleife:
PreToolUseundPostToolUse, außerEndConversation-Aufrufen, die beide überspringen
Die folgende Tabelle fasst zusammen, wann jedes Ereignis ausgelöst wird. Der Abschnitt Hook-Ereignisse dokumentiert das vollständige Eingabeschema und die Optionen zur Entscheidungskontrolle für jedes Ereignis.
| Ereignis | Wann es ausgelöst wird |
|---|---|
SessionStart |
Wenn eine Sitzung beginnt oder fortgesetzt wird |
Setup |
Wenn Sie Claude Code mit --init-only starten oder mit --init oder --maintenance im -p-Modus. Für einmalige Vorbereitung in CI oder Skripten |
UserPromptSubmit |
Wenn ein Prompt abgesendet wird, bevor Claude ihn verarbeitet. Wird auch bei Turns ausgelöst, die Claude Code selbst startet |
UserPromptExpansion |
Wenn ein von Ihnen eingegebener Befehl in eine Eingabeaufforderung erweitert wird, bevor sie Claude erreicht. Kann die Erweiterung blockieren |
PreToolUse |
Bevor ein Werkzeugaufruf ausgeführt wird. Kann ihn blockieren |
PermissionRequest |
Wenn ein Werkzeugaufruf eine Genehmigungsentscheidung benötigt |
PermissionDenied |
Wenn der automatische Modus einen Werkzeugaufruf ablehnt, einschließlich Ablehnungen ohne Klassifizierer-Urteil. Verwenden Sie JSON hookSpecificOutput.retry: true, um dem Modell mitzuteilen, dass es den abgelehnten Werkzeugaufruf möglicherweise erneut versuchen kann. Claude Code ignoriert retry, wenn der Klassifizierer kein Urteil gefällt hat |
PostToolUse |
Nach erfolgreichem Werkzeugaufruf |
PostToolUseFailure |
Nach fehlgeschlagenem Werkzeugaufruf |
PostToolBatch |
Nach Auflösung eines vollständigen Satzes paralleler Werkzeugaufrufe, bevor der nächste Modellaufruf erfolgt |
Notification |
Wenn Claude Code eine Benachrichtigung sendet |
MessageDisplay |
Während der Text der Assistentnachricht angezeigt wird |
SubagentStart |
Wenn ein Subagent erzeugt wird |
SubagentStop |
Wenn ein Subagent beendet wird |
TaskCreated |
Wenn eine Aufgabe über TaskCreate erstellt wird |
TaskCompleted |
Wenn eine Aufgabe als abgeschlossen markiert wird |
Stop |
Wenn Claude die Antwort beendet |
StopFailure |
Wenn die Runde aufgrund eines API-Fehlers endet |
TeammateIdle |
Wenn ein Agent-Team-Teamkollege im Begriff ist, untätig zu werden |
InstructionsLoaded |
Wenn eine CLAUDE.md- oder .claude/rules/*.md-Datei in den Kontext geladen wird. Wird beim Sitzungsstart und beim verzögerten Laden von Dateien während einer Sitzung ausgelöst |
ConfigChange |
Wenn sich eine Konfigurationsdatei während einer Sitzung ändert |
CwdChanged |
Wenn sich das Arbeitsverzeichnis ändert, z. B. wenn Claude einen cd-Befehl ausführt. Nützlich für reaktive Umgebungsverwaltung mit Tools wie direnv |
DirectoryAdded |
Wenn ein Arbeitsverzeichnis während einer Sitzung über /add-dir oder die SDK-Steueranforderung register_repo_root hinzugefügt wird |
FileChanged |
Wenn sich eine überwachte Datei auf der Festplatte ändert. Das Feld matcher gibt an, welche Dateinamen überwacht werden sollen |
WorktreeCreate |
Wenn ein Worktree über --worktree, isolation: "worktree" oder für eine Hintergrundsitzung erstellt wird. Ersetzt das Standard-Git-Verhalten |
WorktreeRemove |
Wenn ein Worktree, den ein WorktreeCreate-Hook erstellt hat, entfernt wird |
PreCompact |
Vor Kontextkomprimierung |
PostCompact |
Nach Abschluss der Kontextkomprimierung |
PreModelSwitch |
Bevor Claude Code einen Modellwechsel anwendet, den Sie oder ein Client angefordert haben. Kann den Wechsel blockieren |
PostModelSwitch |
Nach Änderung des Modells der Sitzung, einschließlich Änderungen, die Claude Code selbst vornimmt, z. B. Wiederherstellung des Modells beim Fortsetzen einer Sitzung |
Elicitation |
Wenn ein MCP-Server während eines Werkzeugaufrufs Benutzereingaben anfordert |
ElicitationResult |
Nachdem ein Benutzer auf eine MCP-Abfrage antwortet, bevor die Antwort an den Server zurückgesendet wird |
SessionEnd |
Wenn eine Sitzung beendet wird |
Wie ein Hook aufgelöst wird
Um zu sehen, wie das Ereignis, der Matcher und der Handler zusammenpassen, betrachten Sie diesen PreToolUse-Hook, der destruktive Shell-Befehle blockiert.
[tab] macOS/Linux
Der
matchergrenzt auf Bash-Tool-Aufrufe und Monitor-Befehle ein, und dieif-Bedingung grenzt weiter auf Bash-Unterbefehle ein, die mitrm *übereinstimmen, daher wirdblock-rm.shnur ausgeführt, wenn beide Filter passen:{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "if": "Bash(rm *)", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh", "args": [] } ] } ] } }Das Skript liest die JSON-Eingabe von stdin, extrahiert den Befehl und gibt eine
permissionDecisionvon"deny"zurück, wenn esrm -rfenthält. Speichern Sie es unter.claude/hooks/block-rm.shin Ihrem Projekt und machen Sie es mitchmod +x .claude/hooks/block-rm.shausführbar, damit Claude Code es ausführen kann:#!/bin/bash # .claude/hooks/block-rm.sh COMMAND=$(jq -r '.tool_input.command') if echo "$COMMAND" | grep -q 'rm -rf'; then jq -n '{ hookSpecificOutput: { hookEventName: "PreToolUse", permissionDecision: "deny", permissionDecisionReason: "Destructive command blocked by hook" } }' else exit 0 # no decision; normal permission flow applies fiDieses Skript verwendet wie die anderen Bash-Beispiele auf dieser Seite, die JSON-Eingabe analysieren,
jq, daher installieren Siejqund stellen Sie sicher, dass es sich in IhremPATHbefindet, bevor Sie sie versuchen.
[tab] Windows (PowerShell)
Der Matcher
Bash|PowerShelldeckt das PowerShell-Tool sowie Bash ab. Eine einzelneif-Regel benennt nur ein Tool, daher erhält jedes Tool seinen eigenen Handler: der erste grenzt auf Bash-Unterbefehle ein, die mitrm *übereinstimmen, der zweite auf PowerShell-Befehle, die mitRemove-Item *übereinstimmen. Beide führen das gleiche Skript überpowershell.exeaus:{ "hooks": { "PreToolUse": [ { "matcher": "Bash|PowerShell", "hooks": [ { "type": "command", "if": "Bash(rm *)", "command": "powershell.exe", "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1" ] }, { "type": "command", "if": "PowerShell(Remove-Item *)", "command": "powershell.exe", "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.ps1" ] } ] } ] } }Das Flag
-NoProfileüberspringt das Laden Ihres PowerShell-Profils, damit der Hook schnell startet, und-ExecutionPolicy Bypassermöglicht PowerShell, die lokale Skriptdatei auszuführen.Das Skript liest die JSON-Eingabe von stdin, extrahiert den Befehl und gibt eine
permissionDecisionvon"deny"zurück, wenn esrm -rfoderRemove-Itemgefolgt von-Recurseenthält. Speichern Sie es unter.claude/hooks/block-rm.ps1in Ihrem Projekt:# .claude/hooks/block-rm.ps1 $callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json $command = $callInput.tool_input.command if ($command -match 'rm -rf|Remove-Item.*-Recurse') { @{ hookSpecificOutput = @{ hookEventName = "PreToolUse" permissionDecision = "deny" permissionDecisionReason = "Destructive command blocked by hook" } } | ConvertTo-Json } else { exit 0 # no decision; normal permission flow applies }
Angenommen, Claude Code entscheidet sich, Bash "rm -rf /tmp/build" gegen die macOS/Linux-Konfiguration 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:
{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
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 rm -rf, daher gibt es eine Entscheidung auf stdout aus:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Destructive command blocked by hook"
}
}
Wenn der Befehl eine sicherere 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.
Der Abschnitt Konfiguration unten dokumentiert das vollständige Schema, und jeder Abschnitt Hook-Ereignis dokumentiert, welche Eingabe Ihr Befehl erhält und welche Ausgabe er zurückgeben kann.
Konfiguration
Hooks werden in JSON-Einstellungsdateien definiert. Die Konfiguration hat drei Verschachtelungsebenen:
- Wählen Sie ein Hook-Ereignis aus, auf das reagiert werden soll, wie
PreToolUseoderStop - Fügen Sie eine Matcher-Gruppe hinzu, um festzulegen, wann er ausgelöst wird, z. B. „für das Bash-Tool“
- Definieren Sie einen oder mehrere Hook-Handler, die ausgeführt werden, wenn eine Übereinstimmung gefunden wird
Siehe Wie ein Hook aufgelöst wird oben für eine vollständige Anleitung mit einem kommentierten Beispiel.
[!NOTE]
Diese Seite verwendet spezifische Begriffe für jede Ebene: Hook-Ereignis für den Lebenszykluspunkt, 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“ bezieht sich allein auf die allgemeine Funktion.
Hook-Speicherorte
Der Ort, an dem Sie einen Hook definieren, bestimmt seinen Umfang:
| Speicherort | Umfang | Freigegeben |
|---|---|---|
~/.claude/settings.json |
Alle Ihre Projekte | Nein, lokal auf Ihrem Computer |
.claude/settings.json |
Einzelnes Projekt | Ja, kann im Repository committed werden |
.claude/settings.local.json |
Einzelnes Projekt | Nein, gitignored, wenn Claude Code eine Einstellung darin speichert |
| Verwaltete Richtlinieneinstellungen | Organisationsweit | Ja, von Administrator kontrolliert |
Plugin hooks/hooks.json |
Wenn Plugin aktiviert ist | Ja, mit dem Plugin gebündelt |
| Skill Frontmatter | Der Rest der Sitzung, sobald der Skill aufgerufen wird. Siehe Hooks in Skills und Agents | Ja, in der Skill-Datei definiert |
| Subagent Frontmatter | Während dieser Subagent ausgeführt wird | Ja, in der Subagent-Datei definiert |
Cloud-Sitzungen lesen Ihre lokale ~/.claude/settings.json nicht. In einer selbstgehosteten Umgebung führt Claude Code auch die Hooks aus, die der Operator vom Host ~/.claude/ des Runners seeded hat, und führt die Hooks in der verwalteten Einstellungsdatei des Runner-Images aus, wenn diese Datei unter den verwalteten Quellen liegt, die Claude Code anwendet, was standardmäßig bedeutet, dass nur dann, wenn weder servergesteuerte Einstellungen noch eine von MDM bereitgestellte Claude Code-Richtlinie die verwaltete Ebene bereitstellt. Siehe was von Ihrem Setup übertragen wird für welche Einstellungsdateien und Plugins und somit welche Hooks eine Cloud-Sitzung erreichen.
Weitere Informationen zur Auflösung von Einstellungsdateien finden Sie unter Einstellungen.
Hooks aus Einstellungsdateien, verwalteten Richtlinieneinstellungen und Plugins werden auch in Subagents ausgeführt. Wenn ein Subagent ein Tool aufruft, werden Tool-Ereignisse wie PreToolUse und PostToolUse die gleichen konfigurierten Hooks wie im Hauptgespräch ausgelöst, und die Eingabe enthält die gemeinsamen Eingabefelder agent_id und agent_type, die den Subagent identifizieren.
Unternehmensadministratoren können allowManagedHooksOnly in verwalteten Einstellungen verwenden, um einzuschränken, welche Hooks ausgeführt werden:
- Ihre Benutzer-, Projekt-, lokalen und Plugin-Hooks werden blockiert. Hooks aus Plugins, die in verwalteten Einstellungen
enabledPluginserzwungen aktiviert sind, sind ausgenommen - Claude Code schränkt auch Ihre
statusLine,fileSuggestionundsubagentStatusLineEinstellungen auf verwaltete Einstellungen ein - Claude Code deaktiviert auch Plugins mit einer
command-Quelle, einschließlich Plugins, die in verwalteten EinstellungenenabledPluginserzwungen aktiviert sind, es sei denn,disableCommandPluginSourcesist explizit auffalsegesetzt.command-Quellen erfordern Claude Code v2.1.229 oder später - Claude Code blockiert auch Marketplace-
headersHelper-Befehle, es sei denn,disableCommandPluginSourcesist explizit auffalsegesetzt, außer für einen Marketplace, den verwaltete Einstellungen selbst deklarieren
Siehe was unter allowManagedHooksOnly ausgeführt wird.
Hook-Einträge werden über Einstellungsebenen hinweg zusammengeführt, anstatt sich gegenseitig zu ersetzen: Benutzer-, Projekt- und lokale Einstellungen fügen ihre eigenen Hooks hinzu, ohne verwaltete zu entfernen, und die Einstellung disableAllHooks kann verwaltete Hooks von außerhalb verwalteter Einstellungen nicht deaktivieren.
Die HTTP-Hook-Allowlists gelten für Hooks aus jeder Quelle, einschließlich verwalteter Richtlinieneinstellungen:
allowedHttpHookUrls: Wenn auf einer beliebigen Einstellungsebene definiert, führt Claude Code einen HTTP-Hook-Handler nur aus, wenn seine URL mit der zusammengeführten Allowlist übereinstimmthttpHookAllowedEnvVars: Wenn definiert, interpoliert Claude Code nur die Umgebungsvariablen auf dieser Liste in Hook-Header
Matcher-Muster
Das Feld matcher filtert, wann Hooks ausgelöst werden. Wie ein Matcher ausgewertet wird, hängt von den Zeichen ab, die er enthält:
| Matcher-Wert | Ausgewertet als | Beispiel |
|---|---|---|
"*", "" oder weggelassen |
Alle abgleichen | wird bei jedem Auftreten des Ereignisses ausgelöst |
Nur Buchstaben, Ziffern, _, -, Leerzeichen, , und | |
Exakte Zeichenkette oder Liste exakter Zeichenketten, getrennt durch | oder , mit optionalem umgebendem Leerzeichen |
Bash passt auf das Bash-Tool und bei PreToolUse und PermissionRequest auch auf Monitor-Befehle; Edit|Write und Edit, Write passen jeweils auf eines der beiden Tools genau; code-reviewer passt nur auf diesen Agent-Typ |
| Enthält ein anderes Zeichen | JavaScript-Regulärer Ausdruck, nicht verankert | ^Notebook passt auf jedes Tool, dessen Name mit Notebook beginnt; mcp__memory__.* passt auf jedes Tool vom memory-Server |
Ein Matcher auf dem Pfad des regulären Ausdrucks wird mit RegExp.prototype.test von JavaScript getestet, was bei einer Übereinstimmung an einer beliebigen Stelle im Wert erfolgreich ist. Edit.* passt sowohl auf Edit als auch auf NotebookEdit; umgeben Sie das Muster mit ^ und $, wie in ^Edit$, wenn Sie eine Übereinstimmung mit der gesamten Zeichenkette benötigen.
FileChanged und StopFailure verwenden einen engeren exakten Übereinstimmungssatz von nur Buchstaben, Ziffern, _ und |. Ein Bindestrich, Leerzeichen oder Komma in einem Matcher für diese beiden Ereignisse hält ihn auf dem Pfad des regulären Ausdrucks, 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 Beobachtungsliste erstellt. Siehe FileChanged.
Jeder Ereignistyp passt auf ein anderes Feld:
| Ereignis | Worauf der Matcher filtert | Beispiel-Matcher-Werte |
|---|---|---|
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied |
Tool-Name | Bash, Edit|Write, mcp__.* |
SessionStart |
wie die Sitzung gestartet wurde | startup, resume, clear, compact, fork |
Setup |
welches CLI-Flag Setup ausgelöst hat | init, maintenance |
SessionEnd |
warum die Sitzung endete | clear, resume, logout, prompt_input_exit, other |
Notification |
Benachrichtigungstyp | permission_prompt, idle_prompt, auth_success, auth_storage_failure, elicitation_dialog, elicitation_url_dialog, elicitation_complete, elicitation_response, agent_needs_input, agent_completed, quota_auto_resume_fired, quota_auto_resume_stale, quota_auto_resume_disabled, plugin_notification |
SubagentStart |
Agent-Typ | general-purpose, Explore, Plan, benutzerdefinierte Agent-Namen oder Plugin-bezogene Namen wie ^my-plugin:reviewer$ |
PreCompact, PostCompact |
was Komprimierung ausgelöst hat | manual, auto |
PreModelSwitch, PostModelSwitch |
kanonischer Name des Modells, zu dem die Sitzung wechselt, wie unter PreModelSwitch beschrieben | claude-opus-5, claude-opus-4-6|claude-opus-5, .*opus.* |
SubagentStop |
Agent-Typ | gleiche Werte wie SubagentStart |
ConfigChange |
Konfigurationsquelle | user_settings, project_settings, local_settings, policy_settings, skills |
CwdChanged |
keine Matcher-Unterstützung | wird immer bei jedem Auftreten ausgelöst |
DirectoryAdded |
wie das Verzeichnis hinzugefügt wurde | slash_command, register_repo_root |
FileChanged |
wörtliche Dateinamen zum Beobachten (siehe FileChanged) | .envrc|.env |
StopFailure |
Fehlertyp | rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error, unknown |
InstructionsLoaded |
Ladegrund | session_start, nested_traversal, path_glob_match, include, compact |
UserPromptExpansion |
Befehlsname | Ihre Skill- oder Befehlsnamen |
Elicitation |
MCP-Servername | Ihre konfigurierten MCP-Servernamen |
ElicitationResult |
MCP-Servername | gleiche Werte wie Elicitation |
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay |
keine Matcher-Unterstützung | wird immer bei jedem Auftreten ausgelöst |
Das Abgleichen von StopFailure auf cloud_credential_error erfordert Claude Code v2.1.267 oder später, die erste Version, die Fehler beim Laden von Anmeldedaten unter diesem Wert statt unter server_error oder unknown meldet.
Für die meisten Ereignisse wertet Claude Code den Matcher gegen ein Feld aus der JSON-Eingabe aus, die es Ihrem Hook auf stdin sendet. Für Tool-Ereignisse ist dieses Feld tool_name. Für PreModelSwitch und PostModelSwitch wertet Claude Code den Matcher gegen den kanonischen Namen aus, den es aus to_model ableitet, wie unter PreModelSwitch beschrieben. Jeder Hook-Ereignis-Abschnitt 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:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{
"type": "command",
"command": "/path/to/lint-check.sh"
}
]
}
]
}
}
Wenn Sie ein matcher-Feld zu einem Ereignis ohne Matcher-Unterstützung 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 Berechtigungsregelsyntax, um gegen den Tool-Namen und die Argumente zusammen abzugleichen, sodass "Bash(git *)" ausgeführt wird, wenn ein Bash-Eingabe-Subbefehl git * passt und "Edit(*.ts)" nur für TypeScript-Dateien ausgeführt wird.
MCP-Tools abgleichen
MCP-Server-Tools erscheinen als reguläre Tools in Tool-Ereignissen (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), sodass Sie sie auf die gleiche Weise abgleichen können wie jeden anderen Tool-Namen.
MCP-Tools folgen dem Benennungsmuster mcp__<server>__<tool>, zum Beispiel:
mcp__memory__create_entities: Entitäten-Tool des Memory-Servers erstellenmcp__filesystem__read_file: Datei-Lese-Tool des Filesystem-Serversmcp__github__search_repositories: Such-Tool des GitHub-Servers
Um jedes Tool von einem Server abzugleichen, hängen Sie .* an das Server-Präfix an. Das .* ist erforderlich: ein Matcher wie mcp__memory oder mcp__brave-search enthält nur exakte Übereinstimmungszeichen, sodass er als exakte Zeichenkette verglichen wird und kein Tool passt.
mcp__memory__.*passt auf alle Tools vommemory-Servermcp__brave-search__.*passt auf alle Tools von einem Server, dessen Name einen Bindestrich enthältmcp__.*__write.*passt auf jedes Tool, dessen Name mitwritebeginnt, von jedem Server
Tools von einem Plugin-gebündelten MCP-Server verwenden ein bereichsbezogenes 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 wird, wird nie 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, sodass der Matcher für jedes Tool von diesem Server mcp__plugin_my-plugin_db__.* ist. Verwenden Sie denselben bereichsbezogenen Tool-Namen im Feld if eines Handlers. Siehe Plugin-bereitgestellte MCP-Server für die Erstellung des bereichsbezogenen Namens.
Dieses Beispiel protokolliert alle Memory-Server-Operationen und validiert Schreibvorgänge von jedem MCP-Server:
{
"hooks": {
"PreToolUse": [
{
"matcher": "mcp__memory__.*",
"hooks": [
{
"type": "command",
"command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
}
]
},
{
"matcher": "mcp__.*__write.*",
"hooks": [
{
"type": "command",
"command": "/home/user/scripts/validate-mcp-write.py"
}
]
}
]
}
}
Monitor-Befehle abgleichen
Ein PreToolUse- oder PermissionRequest-Hook mit einem Bash-Matcher wird auch bei Shell-Befehlen ausgeführt, die Claude mit dem Monitor-Tool startet, aber sein allow und updatedInput haben keine Wirkung auf sie. Erfordert Claude Code v2.1.314 oder später. Matcher wie Bash|PowerShell, ^Bash$ und Bash.* sind ebenfalls Bash-Matcher. Ein Hook mit einem Bash-Matcher wird für eine WebSocket-Überwachung nicht ausgeführt, und ein PostToolUse-Hook mit einem solchen Matcher wird für Monitor nicht ausgeführt.
Ein Hook, der für einen Monitor-Befehl ausgeführt wird, kann ihn verweigern oder Claude Code veranlassen, Sie dazu um Bestätigung zu bitten. Um den Befehl zuzulassen oder umzuschreiben, wechseln Sie von einem Bash-Matcher zu einem Matcher, der Monitor einschließt:
- Matcher, der Monitor einschließt: Mit einem Matcher wie
Bash|Monitoroder.*oder ohne Matcher erhält der Hook die eigene Eingabe des Monitor-Aufrufs, undallowundupdatedInputgreifen. Nennen Sie inifMonitor, wie inMonitor(tail *), das nach den Regeln abgleicht, denenBash(tail *)folgt. Wenn seinifBash nennt, erhält der Hook trotzdem die Eingabe des Monitor-Aufrufs, aberallowundupdatedInputhaben keine Wirkung Bash-Matcher: Der Hook erhält einen Bash-Aufruf. Nennen Sie inifBash, wie inBash(tail *). Der Hook wird nicht ausgeführt, wenn seinifMonitor nennt
Hook-Handler-Felder
Jedes Objekt im inneren hooks-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 empfängt die JSON-Eingabe des Ereignisses auf stdin und kommuniziert Ergebnisse über Exit-Codes und stdout zurück. - HTTP-Hooks (
type: "http"): Senden Sie die JSON-Eingabe des Ereignisses als HTTP-POST-Anfrage an eine URL. Der Endpunkt kommuniziert Ergebnisse über den Antwortkörper mit dem gleichen JSON-Ausgabeformat wie Command-Hooks zurück. - MCP-Tool-Hooks (
type: "mcp_tool"): Rufen Sie ein Tool auf einem konfigurierten MCP-Server auf. Die Textausgabe des Tools wird wie Command-Hook-stdout behandelt. - Prompt-Hooks (
type: "prompt"): Senden Sie einen Prompt an ein Claude-Modell zur Einzelturn-Bewertung. Das Modell gibt seine Entscheidung als JSON zurück. Siehe Prompt-basierte Hooks. - Agent-Hooks (
type: "agent"): Spawnen Sie einen Subagent, der Tools wie Read, Grep und Glob verwenden kann, um Bedingungen zu überprüfen, bevor er eine Entscheidung zurückgibt. Agent-Hooks sind experimentell und können sich ändern. Siehe Agent-basierte Hooks.
Wenn ein Ereignis ausgelöst wird, werden seine passenden Hooks parallel ausgeführt. Wenn mehr als 12 davon Prompt- oder Agent-Hooks sind, laufen 12 davon gleichzeitig, und die übrigen starten, sobald laufende Hooks fertig sind. Der timeout jedes wartenden Hooks beginnt zu zählen, wenn dieser Hook startet.
Wenn Sie denselben Handler in mehr als einer Einstellungsdatei definieren, wird er einmal ausgeführt. Eine Kopie desselben Handlers eines Plugins oder Skills bleibt separat.
Handler werden im aktuellen Verzeichnis mit der Umgebung von Claude Code ausgeführt. Wenn das aktuelle Verzeichnis nicht mehr existiert, z. B. ein Worktree oder temporäres Verzeichnis, das eine andere Shell während der Sitzung gelöscht hat, führt Claude Code Command-Hooks aus dem ersten dieser Verzeichnisse aus, das noch existiert: das Verzeichnis, in dem die Sitzung gestartet wurde, das Projekt-Root, Ihr Home-Verzeichnis oder das System-Temp-Verzeichnis. Claude Code zeichnet eine Warnung auf, die das Fallback-Verzeichnis im Debug-Log benennt. Für eine Worktree-Sitzung, die Sie aus der Desktop-App starten, siehe Was Worktrees mit dem Haupt-Checkout teilen.
Die Umgebungsvariable $CLAUDE_CODE_REMOTE ist "true" in Remote-Web-Umgebungen und nicht gesetzt in der lokalen CLI. Claude Code v2.1.199 und später setzt $CLAUDE_CODE_BRIDGE_SESSION_ID auf die Remote Control-Sitzungs-ID, während die lokale Sitzung eine aktive Remote Control-Verbindung hat.
Gemeinsame Felder
Diese Felder gelten für alle Hook-Typen:
| Feld | Erforderlich | Beschreibung |
|---|---|---|
type |
ja | "command", "http", "mcp_tool", "prompt" oder "agent" |
if |
nein | Berechtigungsregelsyntax zum Filtern, wann dieser Hook ausgeführt wird, z. B. "Bash(git *)" oder "Edit(*.ts)". Der Hook-Befehl wird nur ausgeführt, wenn der Tool-Aufruf dem Muster entspricht. Siehe die Bash-Matching-Tabelle, wie Bash-Muster gegen Subbefehle, $() und Backticks ausgewertet werden. Nur auf Tool-Ereignissen ausgewertet: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest und PermissionDenied. Bei anderen Ereignissen wird ein Hook mit gesetztem if nie ausgeführt |
timeout |
nein | Sekunden vor dem Abbruch. Claude Code erzwingt es nicht auf einem Command-Hook, den Sie mit async: true ausführen. Standardwerte: 600 für command, http und mcp_tool; 30 für prompt; 60 für agent. Claude Code senkt den Standard für command, http und mcp_tool auf 30 bei UserPromptSubmit, PreModelSwitch und PostModelSwitch und auf 10 bei MessageDisplay. SessionEnd-Hooks teilen sich ein Budget von 1,5 Sekunden; wenn Ihre Einstellungen einen längeren Pro-Hook-timeout setzen, erhöht Claude Code das Budget, um zu entsprechen, bis zu 60 Sekunden |
statusMessage |
nein | Benutzerdefinierte Spinner-Nachricht, die angezeigt wird, während der Hook ausgeführt wird |
once |
nein | Wenn true, entfernt Claude Code den Hook nach seiner ersten erfolgreichen Ausführung. Eine Ausführung, die fehlschlägt, mit Exit-Code 2 blockiert oder das Timeout überschreitet, hinterlässt den Hook an Ort und Stelle, sodass er beim nächsten passenden Ereignis erneut ausgeführt wird. Wird nur für Hooks beachtet, die in Skill-Frontmatter deklariert sind; wird in Einstellungsdateien und Agent-Frontmatter ignoriert |
Das Feld if enthält genau eine Berechtigungsregel. Es gibt keine &&-, ||- oder Listsyntax zum Kombinieren von Regeln; um mehrere Bedingungen anzuwenden, definieren Sie einen separaten Hook-Handler für jede.
In einer if-Bedingung für ein Datei-Tool passt ein Verzeichnismuster mit einem Segment wie "Edit(src/**)" nur auf das src-Verzeichnis im Arbeitsverzeichnis und die Dateien darunter. Um ein Verzeichnis namens src in beliebiger Tiefe abzugleichen, schreiben Sie "Edit(**/src/**)".
Wie if-Muster Bash-Befehle abgleichen
Bei Bash-Mustern im if-Feld hängt es von der Form des Musters und dem Bash-Befehl, den Claude aufruft, ab, ob Ihr Hook-Befehl ausgeführt wird. Führende VAR=value-Zuweisungen werden vor dem Abgleich entfernt.
if-Muster |
Bash-Befehl | Hook wird ausgeführt? | Warum |
|---|---|---|---|
Bash(git *) |
FOO=bar git push |
ja | führende Zuweisungen werden entfernt; git push passt |
Bash(git *) |
npm test && git push |
ja | jeder Subbefehl wird überprüft; git push passt |
Bash(rm *) |
echo $(rm -rf /) |
ja | Befehle in $() und Backticks werden überprüft; rm -rf / passt |
Bash(rm *) |
echo $(date) |
nein | kein Subbefehl passt auf rm * |
Bash(git push *) |
echo $(date) |
ja | Muster, die mehr als den Befehlsnamen angeben, führen den Hook trotzdem bei $(), Backticks oder $VAR aus |
Wenn Claude Code nicht bestimmen kann, welche Befehle die Bash-Eingabe ausführt, führt es Ihren Hook unabhängig vom Muster aus. 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:
| Feld | Erforderlich | Beschreibung |
|---|---|---|
command |
ja | Shell-Befehl zum Ausführen. Mit args die Ausführungsdatei zum direkten Spawnen. Siehe Exec-Form und Shell-Form |
args |
nein | Argumentliste. Wenn vorhanden, wird command als Ausführungsdatei aufgelöst und direkt mit args als Argumentvektor gespawnt, ohne Shell. Siehe Exec-Form und Shell-Form |
async |
nein | Wenn true, wird im Hintergrund ohne Blockierung ausgeführt. Siehe Hooks im Hintergrund ausführen |
asyncRewake |
nein | Wenn true, wird im Hintergrund ausgeführt und weckt Claude bei Exit-Code 2 auf. Die stderr des Hooks oder stdout, wenn stderr leer ist, wird Claude als Systemerinnerung angezeigt, damit es auf einen langfristigen Hintergrund-Fehler reagieren kann |
shell |
nein | Shell, die für diesen Hook verwendet werden soll. Akzeptiert "bash" oder "powershell". Standardmäßig "bash" oder "powershell" unter Windows, wenn Git Bash nicht installiert ist. Das Setzen von "powershell" führt den Befehl über PowerShell unter Windows aus. Erfordert nicht CLAUDE_CODE_USE_POWERSHELL_TOOL, da Hooks PowerShell direkt spawnen. Wird ignoriert, wenn args gesetzt ist |
onFailure |
nein | Ob die Aktion, die den Hook ausgelöst hat, fortgesetzt wird, wenn der Hook fehlschlägt: "continue" oder "block". Siehe Aktion blockieren, wenn ein Hook fehlschlägt für "block" und für den Fall, dass das Feld nicht gesetzt ist. Erfordert Claude Code v2.1.295 oder später |
Exec-Form und Shell-Form
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 der beiden Bedenken zutrifft.
Exec-Form wird ausgeführt, wenn args vorhanden ist. Claude Code löst command als Ausführungsdatei auf PATH auf und spawnt es direkt mit args als Argumentvektor. Es gibt keine Shell, sodass jedes args-Element genau ein Argument ist, wie geschrieben, und Pfad-Platzhalter wie ${CLAUDE_PLUGIN_ROOT} werden in command und in jedes args-Element als einfache Zeichenketten ersetzt. Sonderzeichen wie Apostrophe, $ und Backticks werden wörtlich durchgeleitet, da es keine Shell gibt, um sie zu interpretieren. 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.
[!NOTE]
Unter Windows erfordert die Exec-Form, dass
commandsich zu einer echten Ausführungsdatei wie.exeauflöst. Die.cmd- und.bat-Shims, die npm, npx, eslint und andere Tools innode_modules/.bininstallieren, sind keine Ausführungsdateien und können ohne Shell nicht gespawnt werden. Um sie in Exec-Form auszuführen, rufen Sie das zugrunde liegende Skript direkt mitnodeauf, z. B."command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Das Musternodeplus Skriptpfad funktioniert auf jeder Plattform, danode.exeeine echte Binärdatei ist. Um einen.cmd- oder.bat-Shim nach Name auszuführen, verwenden Sie Shell-Form.
Dieses Beispiel führt ein Node-Skript aus, das mit einem Plugin gebündelt ist. Exec-Form übergibt den aufgelösten Skriptpfad als ein Argument ohne Anführungszeichen:
{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PLUGIN_ROOT}/scripts/format.js", "--fix"]
}
Die äquivalente Shell-Form benötigt Anführungszeichen, um Pfade mit Leerzeichen oder Sonderzeichen zu handhaben:
{
"type": "command",
"command": "node \"${CLAUDE_PLUGIN_ROOT}\"/scripts/format.js --fix"
}
Beide Formen unterstützen die gleichen Pfad-Platzhalter und exportieren sie beide als Umgebungsvariablen CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT und CLAUDE_PLUGIN_DATA auf dem gespawnten Prozess, sodass ein Skript process.env.CLAUDE_PLUGIN_ROOT unabhängig davon lesen kann, wie es gestartet wurde.
Plugin-Hooks ersetzen zusätzlich ${user_config.*}-Werte, nur in Exec-Form: Der Wert wird in command und in jedes args-Element als einfache Zeichenkette ersetzt, sodass die Shell ihn nicht erneut analysiert.
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>, z. B. $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.*}.
[!NOTE]
In Exec-Form ist
commandnur der Ausführungsdateiname oder -pfad. Wenncommandein bloßer Name ohne Pfad-Trennzeichen ist und Leerzeichen nebenargsenthält, protokolliert Claude Code eine Warnung, da das Spawn fehlschlägt: Es gibt keine Ausführungsdatei namensnode script.js. Verschieben Sie die zusätzlichen Token inargs. Absolute Pfade mit Leerzeichen, z. B.C:\Program Files\nodejs\node.exe, sind eine einzelne gültige Ausführungsdatei und lösen die Warnung nicht aus.
HTTP-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren HTTP-Hooks diese Felder:
| Feld | Erforderlich | Beschreibung |
|---|---|---|
url |
ja | URL zum Senden der POST-Anfrage an |
headers |
nein | Zusätzliche HTTP-Header als Schlüssel-Wert-Paare. Werte unterstützen Umgebungsvariablen-Interpolation mit $VAR_NAME oder ${VAR_NAME}-Syntax. Nur Variablen in allowedEnvVars werden aufgelöst |
allowedEnvVars |
nein | Liste von Umgebungsvariablennamen, die in Header-Werte interpoliert werden dürfen. Verweise auf nicht aufgelistete Variablen werden durch leere Zeichenketten ersetzt. Erforderlich für jede Umgebungsvariablen-Interpolation |
onFailure |
nein | Was mit der Aktion geschieht, wenn der Hook fehlschlägt: "continue", der Standardwert, oder "block". Siehe Aktion blockieren, wenn ein Hook fehlschlägt. Erfordert Claude Code v2.1.295 oder später |
Claude Code sendet die JSON-Eingabe des Hooks als POST-Anfragekörper mit Content-Type: application/json. Der Antwortkörper verwendet das gleiche JSON-Ausgabeformat wie Command-Hooks.
Die Fehlerbehandlung unterscheidet sich von Command-Hooks; siehe HTTP-Antwortbehandlung.
Dieses Beispiel sendet PreToolUse-Ereignisse an einen lokalen Validierungsdienst und authentifiziert sich mit einem Token aus der Umgebungsvariable MY_TOKEN:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}
MCP-Tool-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren MCP-Tool-Hooks diese Felder:
| Feld | Erforderlich | Beschreibung |
|---|---|---|
server |
ja | Name eines konfigurierten MCP-Servers. Für einen Plugin-gebündelten Server ist dies der bereichsbezogene Name plugin:<plugin-name>:<server-name>, z. B. plugin:my-plugin:db, nicht der bloße Server-Schlüssel |
tool |
ja | Name des Tools, das auf diesem Server aufgerufen werden soll |
input |
nein | Argumente, die an das Tool übergeben werden. Zeichenkettenwerte unterstützen ${path}-Ersetzung aus der JSON-Eingabe des Hooks, z. B. "${tool_input.file_path}" |
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:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "mcp_tool",
"server": "my_server",
"tool": "security_scan",
"input": { "file_path": "${tool_input.file_path}" }
}
]
}
]
}
}
Wie das Ergebnis des Tools gelesen wird
Claude Code liest den Textinhalt des Tools auf die gleiche Weise wie Command-Hook-stdout und folgt der Parsing-Regel unter Exit-Code 0. Wenn das Tool isError: true zurückgibt, erzeugt der Hook einen nicht blockierenden Fehler und die Ausführung wird fortgesetzt.
Wenn der Server noch verbunden wird
Bei Ereignissen, bei denen ein Hook blockieren oder das Ergebnis ändern kann, z. B. PreToolUse oder Stop, wartet Claude Code auf einen verbindenden Server, bevor es das Tool aufruft, für höchstens MCP_TIMEOUT und innerhalb des timeout des Hooks. Bei Beobachtungsereignissen, z. B. Notification oder SessionEnd, wartet es nicht.
Ein Server mit dem cached-Status verbindet sich, wenn der Hook sein Tool aufruft. Wenn der Server zu diesem Zeitpunkt nicht verbunden ist, erzeugt der Hook einen nicht blockierenden Fehler und die Ausführung wird fortgesetzt. Der Hook startet nie einen OAuth-Fluss, also authentifizieren Sie den Server von /mcp zuerst.
Ereignisse, die ausgelöst werden, bevor MCP-Server verfügbar sind
SessionStart beim Start, einschließlich mit --continue oder --resume, und jedes Setup-Ereignis werden ausgelöst, bevor die MCP-Server der Sitzung für Hooks verfügbar sind. Claude Code überspringt ihre mcp_tool-Hooks ohne Aufruf des Tools, und das Debug-Log zeichnet mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context) auf, oder die gleiche Nachricht, die Setup benennt. Wenn SessionStart später in der Sitzung erneut ausgelöst wird, nach /clear oder einer Komprimierung, werden seine mcp_tool-Hooks ausgeführt. Für alles, das die Sitzung beim Start benötigt, verwenden Sie stattdessen einen type: "command"-Hook auf SessionStart.
Prompt- und Agent-Hook-Felder
Zusätzlich zu den gemeinsamen Feldern akzeptieren Prompt- und Agent-Hooks diese Felder:
| Feld | Erforderlich | Beschreibung |
|---|---|---|
prompt |
ja | Prompt-Text zum Senden an das Modell. Verwenden Sie $ARGUMENTS als Platzhalter für die Hook-Eingabe-JSON. Mit einem Backslash escapen, um wörtlichen Text einzuschließen: \$1.00 wird als $1.00 gerendert |
model |
nein | Modell, das für die Bewertung verwendet werden soll. Standardmäßig das Modell, das Claude Code für Hintergrundfunktionalität verwendet |
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, wo die Sitzung gestartet wurde. Claude Code setzt diese Variable auch in der Umgebung von stdio MCP-Servern und Plugin-LSP-Servern.${CLAUDE_PLUGIN_ROOT}: das Plugin-Installationsverzeichnis für Skripte, die mit einem Plugin gebündelt sind. Siehe Plugin-Umgebungsvariablen für das Verhalten des Pfads über Updates hinweg.${CLAUDE_PLUGIN_DATA}: das persistente Datenverzeichnis des Plugins für Abhängigkeiten und Status, die Plugin-Updates überstehen sollten.
[!NOTE]
Worktrees sind anders. Wenn Claude während der Sitzung einen Worktree betritt, behält Claude Code
${CLAUDE_PROJECT_DIR}bei, wo es war, und übergibt den Worktree-Pfad Ihren Hooks auf andere Weise:
${CLAUDE_PROJECT_DIR}bleibt stehen: Es zeigt immer noch auf das Projekt-Root, wo die Sitzung gestartet wurde, sodass ein Befehl wie${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.shdas Skript immer noch im Haupt-Checkout ausführt.cwdfolgt Claude: Das Feldcwdin der Hook-Eingabe-JSON ist das Worktree-Root, nachdem Claude einen Worktree betritt, und das neue Verzeichnis, nachdem Claudecdausführt. Lesen Sie es, wenn ein Hook wissen muss, welches Verzeichnis Claude bearbeitet.Für eine Worktree-Sitzung, die Sie aus der Desktop-App starten, siehe Was Worktrees mit dem Haupt-Checkout teilen, worauf
${CLAUDE_PROJECT_DIR}zeigt.
Bevorzugen Sie Exec-Form für jeden Hook, der auf einen Pfad-Platzhalter verweist. In Shell-Form umgeben Sie jeden Platzhalter mit doppelten Anführungszeichen.
[tab] Projekt-Skripte
Dieses Beispiel verwendet
${CLAUDE_PROJECT_DIR}, um einen Style-Checker aus dem Verzeichnis.claude/hooks/des Projekts nach jedemWrite- oderEdit-Tool-Aufruf auszuführen:{ "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.sh", "args": [] } ] } ] } }
[tab] Plugin-Skripte
Definieren Sie Plugin-Hooks in
hooks/hooks.jsonmit einem optionalen Top-Level-Felddescription. Wenn ein Plugin aktiviert ist, werden seine Hooks mit Ihren Benutzer- und Projekt-Hooks zusammengeführt.Dieses Beispiel führt ein Formatierungsskript aus, das mit dem Plugin gebündelt ist:
{ "description": "Automatic code formatting", "hooks": { "PostToolUse": [ { "matcher": "Write|Edit", "hooks": [ { "type": "command", "command": "${CLAUDE_PLUGIN_ROOT}/scripts/format.sh", "args": [], "timeout": 30 } ] } ] } }Siehe die Plugin-Komponenten-Referenz für Details zum Erstellen von Plugin-Hooks.
Hooks in Skills und Agents
Zusätzlich zu Einstellungsdateien und Plugins können Hooks direkt in Skills und Subagents mit Frontmatter im gleichen Konfigurationsformat wie einstellungsbasierte Hooks definiert werden. Wie lange Claude Code sie registriert hält, hängt von der Komponente ab:
- Subagent-Hooks: Claude Code führt sie nur aus, während dieser Subagent ausgeführt wird, und entfernt sie, wenn er fertig ist. Claude Code konvertiert einen
Stop-Hook hier zuSubagentStop, dem Ereignis, das ausgelöst wird, wenn ein Subagent abgeschlossen ist. - Skill-Hooks: Claude Code registriert sie, wenn Sie oder Claude den Skill aufrufen, und führt sie für den Rest der Sitzung aus, auf Turns nach dem eigenen Turn des Skills auch. Um Claude Code stattdessen einen Hook nach seiner ersten erfolgreichen Ausführung zu entfernen, setzen Sie
once: truedarauf.
Dieser Skill definiert einen PreToolUse-Hook, der ein Sicherheitsvalidierungsskript vor jedem Bash-Befehl ausführt:
---
name: secure-operations
description: Perform operations with security checks
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./scripts/security-check.sh"
---
Subagents verwenden das gleiche Format in ihrem YAML-Frontmatter.
Frontmatter-Hooks in einem Projekt-Skill folgen der gleichen Workspace-Trust-Regel wie Hooks in Einstellungsdateien. Claude Code registriert sie, wenn Sie oder Claude den Skill aufrufen, auch in einem -p-Run in einem Ordner, dem Sie nicht vertraut haben.
Frontmatter-Hooks in einem Projekt-Subagent werden nur ausgeführt, nachdem Sie den Workspace-Trust-Dialog für den Ordner akzeptieren, aus dem die Agent-Datei stammt. Eine -p-Sitzung zählt nicht als Akzeptanz. Was vor dem Vertrauen in einen Ordner ausgeführt wird vergleicht dies mit der Einstellungsdatei-Regel, und die Subagents-Seite listet auf, welche Bereiche ausgenommen sind.
Limits für Skill-Hooks
Wenn die Frontmatter-Hooks eines Skills nicht ausgelöst werden, nachdem Sie oder Claude den Skill aufgerufen haben, führen Sie /hooks in dieser Sitzung aus. Wenn sie dort aufgelistet sind, siehe Hook wird nicht ausgelöst. Wenn keiner davon aufgelistet ist, unterliegt der Skill möglicherweise einer Plugin-only-Sperre oder hat ein Hook-Limit überschritten und wird ohne jeden seiner Frontmatter-Hooks ausgeführt. Prüfen Sie jeden Fall:
- Plugin-only-Sperre: Der Administrator Ihrer Organisation kann Hooks mit der verwalteten Einstellung
strictPluginOnlyCustomizationauf Plugins und verwaltete Einstellungen beschränken. Ein Skill behält seine Frontmatter-Hooks unter dieser Sperre nur, wenn es sich um einen Plugin-Skill, einen mitgelieferten Skill oder einen Skill im Verzeichnis der verwalteten Einstellungen handelt. Die Hooks in Ihren eigenen Einstellungsdateien werden ebenfalls nicht ausgeführt.- Prüfen: Geben Sie
/statusin einer Claude Code-Sitzung ein. Wenn die ZeileSetting sourcesEnterprise managed settingsenthält, fragen Sie Ihren Administrator, obstrictPluginOnlyCustomizationHooks sperrt. - Beheben: Verschieben Sie den Skill in ein Plugin, da Plugin-Skills ihre Frontmatter-Hooks behalten. Nehmen Sie das Plugin in einen Marketplace auf und installieren Sie es dann von dort. Wenn Claude Code den Marketplace mit
is blocked by enterprise policyablehnt, bitten Sie Ihren Administrator, diesen Marketplace zuzulassen.
- Prüfen: Geben Sie
- Hook-Limit: In Claude Code v2.1.304 oder später hat jede Quelle von Skills ein eigenes Limit pro Sitzung. Ihr Projekt, Ihr persönlicher Skills-Ordner und jedes Plugin zählen als separate Quellen. Die Skills aus einer Quelle können höchstens 1.024 Hooks registrieren. Die Matcher dieser Hooks dürfen insgesamt höchstens 16.384 Zeichen umfassen. Ein Matcher mit mehreren Hooks darunter zählt für jeden Hook einmal.
- Prüfen: Wiederholen Sie die Sitzung, in der der Hook nicht ausgelöst wurde, mit aktiviertem Debug-Logging:
- Starten Sie eine neue Sitzung aus Ihrer Shell mit
claude --debug. Wenn Sie Sitzungen nicht aus einer Shell starten, geben Sie stattdessen zuerst/debugin der neuen Sitzung ein. - Rufen Sie dieselben Skills in derselben Reihenfolge auf, einschließlich derer, die Claude aufgerufen hat, da sich beide Summen Skill für Skill aufbauen. Bitten Sie Claude, jeden Skill aufzurufen, den Sie nicht als Befehl eingeben können.
- Durchsuchen Sie das Debug-Log nach
registers none of its hooks. Eine Zeile mit diesem Text nennt den Skill, der das Limit überschritten hat.
- Starten Sie eine neue Sitzung aus Ihrer Shell mit
- Beheben: Deklarieren Sie weniger Hooks oder kürzere Matcher in diesem Skill oder in den anderen Skills aus seiner Quelle und starten Sie dann eine neue Sitzung. Wenn Sie diese Skills nicht selbst bearbeiten können, bitten Sie deren Autor um diese Änderung. Starten Sie bis dahin eine neue Sitzung und rufen Sie den benötigten Skill vor jedem anderen Skill aus seiner Quelle auf.
- Prüfen: Wiederholen Sie die Sitzung, in der der Hook nicht ausgelöst wurde, mit aktiviertem Debug-Logging:
Wenn keiner der beiden Fälle auf Sie zutrifft, prüfen Sie, ob Frontmatter nicht geparst wurde.
Das /hooks-Menü
Geben Sie /hooks in Claude Code ein, um eine nur lesende Ansicht Ihrer konfigurierten Hooks zu öffnen. Die Liste kennzeichnet jeden Hook mit seiner Herkunft, z. B. Benutzereinstellungen, Projekteinstellungen, lokale Einstellungen, ein Plugin oder die aktuelle Sitzung.
Wählen Sie einen Hook aus, um den vollständigen Text dessen zu sehen, was er ausführt, und wo er definiert ist, z. B. den Pfad seiner Einstellungsdatei oder den Namen seines Plugins.
Um alle Hook-Ereignisse zu durchsuchen, einschließlich solcher ohne konfigurierte Hooks, wählen Sie All events am Ende der Liste aus.
Hooks deaktivieren oder entfernen
Um einen in einer Einstellungsdatei definierten Hook zu entfernen, löschen Sie seinen Eintrag aus dieser Datei.
Um alle Hooks vorübergehend zu deaktivieren, ohne sie zu entfernen, setzen Sie "disableAllHooks": true in Ihrer Einstellungsdatei. Claude Code liest den Wert, der nach Einstellungspriorität bleibt, sodass ein "disableAllHooks": false in der .claude/settings.json eines Projekts ein true in Ihren Benutzereinstellungen überschreibt. Um Hooks für einen Run auszuschalten, unabhängig davon, was die Einstellungen des Projekts sagen, übergeben Sie --settings '{"disableAllHooks": true}', was Vorrang vor Projekt- und lokalen Einstellungen hat. 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. Für die vollständige Reichweite jeder Ebene siehe disableAllHooks.
Direkte Änderungen an Hooks in Einstellungsdateien werden normalerweise automatisch vom Datei-Watcher aufgegriffen.
Hook-Eingabe und -Ausgabe
Command Hooks empfangen JSON-Daten über stdin und teilen Ergebnisse über Exit-Codes, stdout und stderr mit. HTTP Hooks empfangen das gleiche JSON wie der POST-Request-Body und teilen Ergebnisse über den HTTP-Response-Body mit. Dieser Abschnitt behandelt Felder und Verhalten, die für alle Events gemeinsam sind. Jeder Event-Abschnitt unter Hook Events enthält sein spezifisches Input-Schema und Optionen zur Entscheidungskontrolle.
Auf macOS und Linux werden Command Hooks in ihrer eigenen Session 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-Schnittstelle senden. Windows hat kein /dev/tty.
Um eine Nachricht für den Benutzer auf jeder Plattform anzuzeigen, geben Sie systemMessage in der JSON-Ausgabe zurück. Einige Events verwerfen sie oder liefern sie an anderer Stelle, und jeder Event-Abschnitt gibt an, wo. Um eine Desktop-Benachrichtigung auszulösen, einen Fenstertitel zu setzen oder die Glocke zu läuten, geben Sie stattdessen terminalSequence zurück.
Allgemeine Eingabefelder
Hook Events empfangen diese Felder als JSON, zusätzlich zu Event-spezifischen Feldern, die in jedem Hook-Event-Abschnitt dokumentiert sind. Für Command Hooks kommt dieses JSON über stdin an. Für HTTP Hooks kommt es als POST-Request-Body an.
| Feld | Beschreibung |
|---|---|
session_id |
Aktuelle Session-Kennung |
prompt_id |
UUID, die den aktuell verarbeiteten Benutzer-Prompt identifiziert. Stimmt mit dem prompt.id-Attribut auf OpenTelemetry-Events überein, sodass Sie Hook-Ausgabe mit Telemetrie für einen einzelnen Prompt korrelieren können. Nicht vorhanden bis zur ersten Benutzereingabe |
transcript_path |
Pfad zur Konversations-JSON. Die Transkriptdatei wird asynchron geschrieben und kann der In-Memory-Konversation hinterherhinken, daher kann sie möglicherweise noch nicht die neuesten Nachrichten des aktuellen Turns enthalten, wenn ein Hook ausgelöst wird. Hooks, die den endgültigen Assistant-Text des aktuellen Turns benötigen, sollten last_assistant_message auf Stop und SubagentStop verwenden, anstatt das Transkript zu lesen |
cwd |
Aktuelles Arbeitsverzeichnis, wenn der Hook aufgerufen wird |
scratchpad_dir |
Pfad zum Scratchpad-Verzeichnis der Session, in dem Claude temporäre Arbeitsdateien speichert. Nicht vorhanden, wenn die Session kein Scratchpad hat oder das Temp-Verzeichnis nicht verfügbar ist. Erfordert Claude Code v2.1.257 oder später |
permission_mode |
Aktueller Berechtigungsmodus: "default", "plan", "acceptEdits", "auto", "dontAsk" oder "bypassPermissions". Der als Manuell bezeichnete Modus kommt als "default" an, nie als "manual", sodass Skripte, die "default" abgleichen, weiterhin funktionieren. Nicht alle Events erhalten dieses Feld. Überprüfen Sie das JSON-Beispiel in jedem Hook-Event-Abschnitt |
effort |
Objekt, dessen level die Einstellung der Effort-Stufe ist, wenn der Hook ausgeführt wird, einschließlich der Stufe eines Skills, eines Subagenten oder von ultrathink: "low", "medium", "high", "xhigh" oder "max". Vorhanden für PreToolUse, PostToolUse, Stop, SubagentStop und andere Events mit einem Tool-Use-Kontext, bei Modellen mit Effort. Wenn das Modell Ihre Stufe nicht unterstützt, ist level die Stufe, die ausgeführt wurde. Eine Stufe, die Sie geändert haben, kann hier erscheinen, bevor sie wirksam wird. $CLAUDE_EFFORT enthält dieselbe Stufe. |
hook_event_name |
Name des ausgelösten Events |
agent_id und agent_type teilen Ihrem Skript mit, in welchem Agenten ein Hook ausgelöst wurde, etwa in einem Subagenten, einem In-Process-Teammate oder dem Agenten, den Sie mit --agent ausgewählt haben:
| Feld | Beschreibung |
|---|---|
agent_id |
Eindeutige Kennung für den Subagenten oder In-Process-Teammate, in dem der Hook ausgelöst wird. |
agent_type |
Agent-Name (z. B. "Explore" oder "security-reviewer"). Vorhanden, wenn die Session --agent verwendet oder der Hook innerhalb eines Subagenten ausgelöst wird. Für Subagenten hat der Typ des Subagenten Vorrang vor dem --agent-Wert der Session. Siehe SubagentStart für die Werte, die benutzerdefinierte und Plugin-Subagenten melden, und wie man einen Matcher gegen einen Plugin-Scoped-Namen schreibt. |
Nur SessionStart-Hooks können ein model-Feld empfangen, und Claude Code fügt es nicht immer ein. PreModelSwitch- und PostModelSwitch-Hooks empfangen stattdessen from_model und to_model, verwenden Sie also einen PostModelSwitch-Hook, um das Modell zu verfolgen, während es sich während einer Session ändert.
Es gibt keine $CLAUDE_MODEL-Umgebungsvariable. Der Hook kann $ANTHROPIC_MODEL lesen, wenn Sie es in Ihrer Shell festlegen, aber dieser Wert ändert sich nicht, wenn Sie während einer Session mit /model Modelle wechseln.
Ein Hook-Prozess erbt die übergeordnete Umgebung, mit Ausnahme der OTEL_*-Exporter-Variablen, die Claude Code aus jedem Subprocess entfernt, den es spawnt, und, wenn CLAUDE_CODE_SUBPROCESS_ENV_SCRUB auf 1 gesetzt ist, die Variablen, die es entfernt. In einer Sitzung, die die HIPAA-Konfiguration erhält, entfernt Claude Code außerdem Anthropic-Anmeldedaten aus der Umgebung des Hooks.
Beispielsweise empfängt ein PreToolUse-Hook für einen Bash-Befehl dies auf stdin:
{
"session_id": "abc123",
"prompt_id": "550e8400-e29b-41d4-a716-446655440000",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"scratchpad_dir": "/tmp/claude-1000/-home-user-my-project/abc123/scratchpad",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite",
"timeout": 120000,
"run_in_background": false
},
"tool_use_id": "toolu_01ABC123..."
}
Die Felder tool_name, tool_input und tool_use_id sind Event-spezifisch. Jeder Hook-Event-Abschnitt dokumentiert die zusätzlichen Felder für diesen Event.
Exit-Code-Ausgabe
Wenn der Lauf Ihres Hooks abgeschlossen ist, entscheidet Claude Code anhand seines Exit-Codes, ob die Aktion, die ihn ausgelöst hat, etwa ein Tool-Aufruf oder ein Prompt, fortgesetzt wird:
- Erfolg: Ihr Hook wird mit 0 beendet. Claude Code wendet alle JSON-Ausgabe-Felder an, die Ihr Hook ausgegeben hat, und die Aktion wird fortgesetzt, sofern diese Felder sie nicht blockieren oder verweigern. Hooks, die einen Hintergrundprozess starten behandelt die Ausnahme.
- Blockierender Fehler: Ihr Hook wird mit 2 beendet. Bei Events, die blockieren können, stoppt Claude Code die Aktion.
- Nicht blockierender Fehler: Ihr Hook wird mit einem anderen Code beendet oder schlägt auf andere Weise fehl, etwa weil er nicht startet oder ungültiges JSON ausgibt. Die Aktion wird fortgesetzt, außer bei Hooks, deren Fehlschlag die Aktion blockiert, zum Beispiel einem Hook, bei dem
onFailureauf"block"gesetzt ist. Bei Events wiePreToolUsesehen Sie einen<hook name> hook error-Hinweis im Transkript.
Was Ihr Hook auf stdout ausgibt, kann das Ergebnis ändern. Wenn zum Beispiel ein PreToolUse-Hook mit 1 beendet wird, aber JSON ausgibt, das die Validierung besteht, kann dieses JSON trotzdem entscheiden, was passiert. Um das Ergebnis Ihres Hooks bei einem Event wie PreToolUse zu ermitteln, suchen Sie in der ersten Spalte, was er auf stdout ausgegeben hat, und oben seinen Exit-Code:
| Stdout | Exit 0 | Exit 2 | Jeder andere Exit-Code |
|---|---|---|---|
| JSON-Objekt, das die Schemavalidierung besteht | Erfolg. Die Felder werden angewendet | Blockierender Fehler. Claude Code liest die Felder trotzdem, aber sie können den Block nicht überschreiben | Erfolg. Claude Code ignoriert den Exit-Code, und allein die Felder entscheiden. Bei Hooks, deren Fehlschlag die Aktion blockiert, zählt der Lauf stattdessen als Fehlschlag |
| JSON, das nicht geparst werden kann oder die Schemavalidierung nicht besteht | Nicht blockierender Fehler. Der Hinweis enthält die Parse- oder Validierungsmeldung | Blockierender Fehler. Ihr stderr ist der Grund | Nicht blockierender Fehler. Der Hinweis enthält die Parse- oder Validierungsmeldung |
| Einfacher Text oder nichts | Erfolg | Blockierender Fehler. Ihr stderr ist der Grund | Nicht blockierender Fehler. Der Hinweis enthält die erste Zeile Ihres stderr |
Einige Events haben eigene Regeln:
WorktreeCreate: Jeder Exit-Code ungleich null lässt die Worktree-Erstellung fehlschlagen, unabhängig davon, was Ihr JSON sagt.WorktreeRemove: Jeder Exit-Code ungleich null lässt die Worktree-Entfernung fehlschlagen, wenn das Verzeichnis danach noch existiert.Stop,SubagentStop,TaskCompletedund derUserPromptSubmit-Hook eines Plugins: Wenn Ihr Hook mit 2 beendet wird, nichts auf stdout ausgibt und sein stderr meldet, dass eine Datei fehlt, etwaNo such file or directory, behandelt Claude Code den Lauf als nicht blockierenden Fehler.ElicitationundElicitationResult: Claude Code wendet IhrehookSpecificOutputan, wenn Ihr Hook mit 0 beendet wird, und ignoriert sie bei jedem anderen Exit-Code.- Events, die Hook-Ausgabe verwerfen, wie
StopFailure: Claude Code ignoriert Ihr JSON bei jedem Exit-Code, abgesehen von Nebeneffekt-Feldern wieterminalSequence, die trotzdem ausgelöst werden.
Was Exit-Code 2 bei Ihrem Event bewirkt, sehen Sie unter Exit-Code-2-Verhalten pro Event. Welche Entscheidungsfelder es berücksichtigt, sehen Sie unter Entscheidungskontrolle.
Exit-Code 0
Exit 0 bedeutet Erfolg und ist der beabsichtigte Exit-Code, wenn Sie JSON für strukturierte Kontrolle ausgeben. Wenn Ihr Hook mit 0 beendet wird, aber JSON ausgibt, das nicht geparst werden kann oder die Schemavalidierung nicht besteht, wird die Aktion fortgesetzt, sofern es sich nicht um einen Hook handelt, dessen Fehlschlag die Aktion blockiert. Statt eines Erfolgs mit angewendeten Feldern ist der Lauf ein nicht blockierender Fehler.
Um Ihre Ausgabe zu korrigieren, verwenden Sie die Parse- oder Validierungsmeldung, die der <hook name> hook error-Hinweis enthält. Bei einem Hook, der nicht async ist, zählt Ihr stdout nur dann als JSON-Ausgabe, wenn Sie ein einzelnes JSON-Objekt ausgeben, auf einer oder mehreren Zeilen, das nur von Whitespace umgeben ist. Jeden anderen stdout liest Claude Code als einfachen Text oder als Parse-Fehler:
- Ausgabe, die nicht mit
{beginnt, oder die mit{beginnt und nicht mit}endet: einfacher Text. Ein JSON-Array und ein in Anführungszeichen gesetzter JSON-String sind nach dieser Regel einfacher Text. - Zwei oder mehr Zeilen, die jeweils für sich als JSON geparst werden, wobei die erste mit
{beginnt und die letzte mit}endet: einfacher Text, wenn keine Zeile ein JSON-Ausgabe-Objekt ist, das ein Feld setzt. Ist eine Zeile ein solches Objekt, ist die Ausgabe ein Parse-Fehler; geben Sie dieses Objekt also ohne weiteren Inhalt auf stdout aus. - Alles andere, das mit
{beginnt und mit}endet, aber kein gültiges JSON ist: ein Parse-Fehler.
Für die meisten Events schreibt Claude Code stdout in das Debug-Log und zeigt es nicht im Transkript an. Bei diesen Events erreicht Ihr stdout Claude oder die Zusammenfassung der Komprimierung:
UserPromptSubmit,UserPromptExpansion,SessionStartundPostModelSwitch: Claude Code fügt einfachen Text-stdout als Kontext hinzu, den Claude sehen und bearbeiten kann. stdout, den es nicht parsen konnte, fügt es nicht hinzu.PreCompact: Ihr stdout wird zu Anweisungen für die Komprimierungszusammenfassung
Claude sieht stderr von einem Hook, der mit 0 beendet wird, nie. Um ihn bei Events wie PreToolUse selbst zu lesen, aktivieren Sie Debug-Logging. Um eine Warnung an Claude von einem PostToolUse- oder PostToolUseFailure-Hook zu übermitteln, beenden Sie stattdessen mit 2, damit Claude den stderr sieht, obwohl das Tool bereits ausgeführt wurde.
Exit-Code 2
Beenden Sie mit Code 2, um die Aktion zu blockieren. Bei Events, die blockieren können, stoppt Claude Code die Aktion: Ein PreToolUse-Hook blockiert zum Beispiel den Tool-Aufruf, und ein UserPromptSubmit-Hook lehnt den Prompt ab.
Die Meldung, die mit dem Block einhergeht, ist der stderr Ihres Hooks. Wenn Ihr Hook zusätzlich JSON ausgegeben hat, das eine blockierende Entscheidung trifft, verwendet Claude Code stattdessen den Grund dieser Entscheidung.
Exit 2 blockiert auch dann, wenn Ihr Hook JSON ausgibt:
- JSON, das die Schemavalidierung besteht: Claude Code liest die JSON-Ausgabe-Felder trotzdem, aber sie können den Block nicht überschreiben. Selbst eine
permissionDecisionvon"allow"lässt die Aktion nicht durch. BeiElicitationundElicitationResultwird diehookSpecificOutputeines Exit-2-Hooks ignoriert. - JSON, das die Schemavalidierung nicht besteht: Der Hook blockiert trotzdem. Claude Code verwendet Ihren stderr als Blockierungsgrund und zeichnet den Validierungsfehler im Debug-Log auf.
Dieses Skript blockiert rm-Befehle durch Beendigung mit 2 und lässt jeden anderen Befehl zum normalen Berechtigungsfluss:
#!/bin/bash
# Reads JSON input from stdin, checks the command
input=$(cat)
command=$(jq -r '.tool_input.command' <<<"$input")
if [[ "$command" == rm* ]]; then
echo "Blocked: rm commands are not allowed" >&2
exit 2 # Blocking error: tool call is prevented
fi
exit 0 # No decision: the normal permission flow applies
Wenn dieses Skript als PreToolUse-Hook für Bash registriert ist, wird ein Befehl, der mit rm beginnt, blockiert, und Claude erhält den stderr des Hooks als Fehler des Tools, mit dem Event-Namen, dem Tool-Namen und dem Befehl des Hooks als Präfix:
PreToolUse:Bash hook error: [${CLAUDE_PROJECT_DIR}/.claude/hooks/no-rm.sh]: Blocked: rm commands are not allowed
Andere Exit-Codes
Ein <hook name> hook error-Hinweis mit Failed with non-blocking status code: bedeutet, dass der Exit-Code Ihres Hooks weder 0 noch 2 war und sein stdout einfacher Text oder leer war. Sofern es sich nicht um einen Hook handelt, dessen Fehlschlag die Aktion blockiert, ist dies ein nicht blockierender Fehler, Ihr Hook hat die Aktion also nicht gestoppt. Um den vollständigen stderr Ihres Hooks statt der ersten Zeile, die der Hinweis zeigt, zu erfassen, aktivieren Sie Debug-Logging.
Wenn zum Beispiel ein PreToolUse-Hook für Bash something broke auf stderr ausgibt und mit 1 beendet wird, enthält der PreToolUse:Bash hook error-Hinweis diese Zeile:
Failed with non-blocking status code: something broke
Wenn der Hinweis die No such file or directory-Meldung der Shell enthält, wie in der folgenden Zeile, wurde der Hook nie ausgeführt, zum Beispiel weil sein Skriptpfad nicht existiert:
Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory
Wenn der Pfad falsch ist, korrigieren Sie ihn dort, wo der Hook konfiguriert ist. In Shell-Form beendet sich die Shell, wenn der Skriptpfad nicht existiert oder nicht ausführbar ist, mit einem Code wie 127, und der Hinweis enthält die Meldung des Interpreters. Sofern es sich nicht um einen Hook handelt, dessen Fehlschlag die Aktion blockiert, ist ein Hook, der nicht starten kann, ebenfalls ein nicht blockierender Fehler.
Wenn Sie einen Policy-Hook einrichten, achten Sie bei seiner ersten Ausführung auf diesen Hinweis, denn ein falsch geschriebener Pfad in settings.json bedeutet, dass der Hook nie ausgeführt wird. Um die Aktion zu blockieren, wenn der Hook nicht starten kann, setzen Sie onFailure: "block".
[!WARNING]
Exit-Code 1 ohne gültige JSON-Ausgabe ist ein nicht blockierender Fehler, sofern es sich nicht um einen Hook handelt, dessen Fehlschlag die Aktion blockiert, obwohl 1 der konventionelle Unix-Fehlercode ist. Wenn Ihr Hook eine Richtlinie durchsetzen soll, verwenden Sie
exit 2.
Timeouts
Abgesehen von einem Command Hook, den Sie mit async: true ausführen, bricht Claude Code einen command-, http- oder mcp_tool-Hook ab, der sein timeout erreicht, und verwirft die Hook-Ausgabe, sodass bei den meisten Events ein abgelaufener Hook keine Entscheidung trifft. Bei PreModelSwitch blockiert ein Hook, der bei seinem Timeout abgebrochen wird, den Modellwechsel.
Bei PreToolUse blockiert ein abgelaufener command-, http- oder mcp_tool-Hook den Tool-Aufruf nicht, außer bei Hooks, deren Fehlschlag die Aktion blockiert. Der Aufruf wird durch den normalen Berechtigungsfluss fortgesetzt, verlassen Sie sich also nicht auf einen steckengebliebenen Hook, um als Gate zu fungieren. Um den Aufruf zu blockieren, wenn ein command- oder http-Hook in ein Timeout läuft, setzen Sie onFailure: "block". Ein Agent SDK Callback Hook, der sein Timeout überschreitet, blockiert den Tool-Aufruf.
Hooks, die einen Hintergrundprozess starten
Wenn Ihr Command Hook seine Entscheidung in einem Hintergrundprozess trifft und sich dann beendet, blockiert eine Verweigerung, die dieser Prozess ausgibt, nachdem Claude Code das Lesen beendet hat, den Tool-Aufruf nicht. Bei einem Hook, der nicht async ist, beendet Claude Code das Lesen der Hook-Ausgabe kurz nachdem der eigene Prozess des Hooks beendet wurde.
Geben Sie die Entscheidung aus, bevor Sie einen Hintergrundprozess starten, und leiten Sie stdout und stderr dieses Prozesses um. Wenn Ihr Skript eine Erlaubnis ausgibt und dann einen Hintergrundprozess startet, ohne dessen stdout und stderr umzuleiten, blockiert Claude Code den Tool-Aufruf. Der Hook-Fehler enthält refusing to honor the suspect capture.
Dieses PreToolUse-Hook-Skript gibt die Entscheidung mit policy-check.sh aus und lädt dann Metriken im Hintergrund hoch:
#!/bin/bash
"$CLAUDE_PROJECT_DIR"/.claude/hooks/policy-check.sh || exit $?
"$CLAUDE_PROJECT_DIR"/.claude/hooks/upload-metrics.sh >/dev/null 2>&1 &
Der Rest des Skripts bewirkt Folgendes:
|| exit $?: macht den Exit-Code der Prüfung zum eigenen Exit-Code des Hooks, sodass eine Prüfung, die zum Verweigern mit 2 beendet wird, trotzdem blockiert. Andernfalls wird der Hook mit dem Code der letzten Zeile beendet, also 0.>/dev/null 2>&1 &: führtupload-metrics.shim Hintergrund mit umgeleiteter Ausgabe aus, sodass es die Ausgabe des Hooks nicht offen hält.
Um das Skript zu testen, lassen Sie policy-check.sh mit 2 beenden und lösen Sie dann einen Tool-Aufruf aus, auf den der Hook zutrifft. Der Tool-Aufruf wird blockiert.
Die Aktion blockieren, wenn ein Hook fehlschlägt
Bei den meisten Events führt Claude Code die Aktion trotzdem aus, wenn ein Hook fehlschlägt oder in ein Timeout läuft. Ein Policy-Hook mit falschem Pfad oder einem abstürzenden Skript lässt dann alles durch. Um die Aktion stattdessen zu blockieren, setzen Sie "onFailure": "block" bei einem command- oder http-Hook. Erfordert Claude Code v2.1.295 oder später. Für Agent SDK Callback Hooks siehe Die Aktion blockieren, wenn ein Callback fehlschlägt.
[!NOTE]
Unter Windows ohne Git Bash kann ein
PreToolUse- oderPermissionRequest-Command-Hook in Shell-Form, der ohne gesetztesonFailurefehlschlägt, stattdessen den Tool-Aufruf blockieren.
Dieser PreToolUse-Hook in .claude/settings.json führt vor jedem Bash-Befehl ein Projektskript aus und blockiert den Befehl, wenn das Skript fehlschlägt:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "node",
"args": ["${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js"],
"onFailure": "block"
}
]
}
]
}
}
Um ihn zu testen, lassen Sie check-command.js fehlen und bitten Sie Claude, einen Bash-Befehl wie ls auszuführen. Claude Code blockiert den Aufruf, und der Fehler enthält failed; blocking because onFailure is "block", gefolgt von der eigenen Fehlerausgabe von node, hier auf eine Zeile gekürzt:
PreToolUse:Bash hook error: [node ${CLAUDE_PROJECT_DIR}/.claude/hooks/check-command.js]: failed; blocking because onFailure is "block"
Error: Cannot find module '/path/to/project/.claude/hooks/check-command.js'
Nach einem Timeout lautet die Meldung timed out statt failed. Ohne gesetztes onFailure ist dasselbe fehlende Skript ein nicht blockierender Fehler, und ls wird ausgeführt.
Jeder der folgenden Fälle zählt als Fehlschlag:
- Kann nicht starten: Ein Command Hook startet nicht, zum Beispiel weil das Skript oder die ausführbare Datei nicht existiert
- Exit-Code außer 0 oder 2: zählt bei einem Command Hook auch dann, wenn er JSON ausgegeben hat, das die Aktion erlaubt, etwa
permissionDecision: "allow". Um eine JSON-Entscheidung zurückzugeben, beenden Sie mit 0 - HTTP-Fehler: Die Verbindung eines HTTP Hooks schlägt fehl, oder der Response-Status ist nicht 2xx
- Timeout: Der Hook erreicht sein
timeout - Ungültige Ausgabe: Die JSON-Ausgabe kann nicht geparst werden oder besteht die Schemavalidierung nicht. Bei einem HTTP Hook zählt auch ein 2xx-Body, der weder leer noch ein JSON-Objekt ist. Einfacher Text-stdout eines Command Hooks ist kein Fehlschlag
Mit gesetztem "block" bewirkt ein Fehlschlag dasselbe wie Exit-Code 2 bei diesem Event, außer bei PermissionRequest, wo er die Anfrage verweigert. Zum Beispiel blockiert ein PreToolUse-Fehlschlag den Tool-Aufruf und ein UserPromptSubmit-Fehlschlag den Prompt.
Das Feld hat bei diesen Hooks keine Wirkung:
Stop-,SubagentStop-,TaskCompleted- undTeammateIdle-Hooks: Exit-Code 2 schickt Claude bei diesen Events zurück an die Arbeit, und Claude kann einen Hook, der nicht läuft, nicht reparieren- Hintergrund-Command-Hooks: Command Hooks, die
asyncoderasyncRewakesetzen
Fehlgeschlagene Hooks unter Windows ohne Git Bash
Wenn der Fehler bei einem blockierten Tool-Aufruf diesen Text enthält, ist ein PreToolUse- oder PermissionRequest-Command-Hook fehlgeschlagen oder in ein Timeout gelaufen und hat den Aufruf blockiert:
blocking because Git Bash wasn't found, so only exit code 0 or 2 counts as this hook's verdict.
Wenn der Fehler außerdem PowerShell started no program enthält, beginnen Sie mit den Lösungen für einen command, der nur aus Strings in Anführungszeichen besteht, die PowerShell ausgibt, statt sie auszuführen.
Ohne Git Bash zählt jeder Exit-Code außer 0 oder 2 von diesem Hook als Fehlschlag, auch wenn er JSON ausgibt, das den Aufruf nicht verweigert. Um herauszufinden, warum der Hook fehlgeschlagen ist, prüfen Sie das Debug-Log. Wählen Sie dann die Lösung, die zu Ihrem Hook passt:
- Der Aufruf soll fortgesetzt werden, wenn der Hook fehlschlägt: Fügen Sie dem Hook
"onFailure": "continue"hinzu - Der Hook wird mit 1 beendet, um zu warnen, muss aber blockieren, wenn er abstürzt: Mit
"onFailure": "continue"wird der Aufruf bei Exit-Code 1 fortgesetzt, ob Warnung oder Absturz. Fügen Sie stattdessen"onFailure": "block"hinzu und beenden Sie das Skript an den Stellen, an denen es warnt, mit Exit-Code 0, damit diese Aufrufe fortgesetzt werden. Um die Warnung beiPreToolUsetrotzdem anzuzeigen, geben Sie sie auf stdout im FeldsystemMessageeines einzelnen JSON-Objekts aus. - Der Hook ist für Bash geschrieben: Ohne Git Bash wird ein Hook in Shell-Form ohne gesetztes
shellin PowerShell ausgeführt, wo er fehlschlagen kann. Installieren Sie Git for Windows oder setzen SieCLAUDE_CODE_GIT_BASH_PATH, wenn Git Bash an einem Ort installiert ist, an dem Claude Code es nicht findet. Starten Sie dann eine neue Sitzung. - Der Hook führt eine einzelne
.exeaus, keine.cmd- oder.bat-Datei: Tragen Sie das Programm incommandund seine Argumente inargsein, um die Exec-Form zu verwenden, und fügen Sie"onFailure": "block"hinzu. Claude Code startet das Programm dann ohne Shell. Ohne"onFailure": "block"kann der Aufruf fortgesetzt werden, wenn das Programm fehlschlägt, fehlt oder in ein Timeout läuft - Ein PowerShell-Guard-Hook, der bei einem Fehlschlag blockieren muss: Ohne gesetztes
onFailuretut er das nicht mehr, sobald Git for Windows installiert ist und Claude Code Git Bash findet. Fügen Sie"onFailure": "block"hinzu, damit ein Fehlschlag oder ein Timeout den Aufruf mit oder ohne Git Bash blockiert. Setzen Sie außerdem"shell": "powershell", damit der Hook von da an weiterhin in PowerShell statt in Git Bash ausgeführt wird
Vor v2.1.309 lautete die Meldung blocking because Git Bash wasn't found and this hook doesn't set "shell" to "powershell", so only exit code 0 or 2 counts as its verdict.
PowerShell started no program
Ein Hook mit diesem Fehler wurde in PowerShell ausgeführt, die einen command, der nur aus Strings in Anführungszeichen besteht, etwa "command": "\"C:\\Tools\\guard.exe\"", ausgibt, statt ihn auszuführen.
- Um Text wie JSON auszugeben: Setzen Sie
"shell": "powershell", und der Hook wird mit 0 beendet - Um eine
.ps1-,.cmd- oder.bat-Datei oder eine.exe, deren Pfad eine Variable enthält, auszuführen: Setzen Sie"shell": "powershell"und stellen Sie&vor den Pfad in Anführungszeichen. Lassen Sie$HOMEunverändert, und schreiben Sie Umgebungsvariablen so, wie dascheck.ps1-Beispiel$env:CLAUDE_PROJECT_DIRschreibt - Um eine einzelne
.exean einem Pfad ohne$auszuführen: Entfernen Sie die inneren Anführungszeichen auscommandund wenden Sie dann die Lösung für einen Hook, der eine einzelne.exeausführt, an, wobei Sie"args": []schreiben, wenn das Programm keine Argumente erwartet - Um ein für Bash geschriebenes Skript auszuführen: Wenden Sie die Lösung für einen für Bash geschriebenen Hook an
Exit-Code-2-Verhalten pro Event
Exit-Code 2 ist die Art, wie ein Hook signalisiert „Stopp, mach das nicht.“ Die Auswirkung hängt vom Event ab, da einige Events 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.
| Hook-Event | Kann blockieren? | Was passiert bei Exit 2 |
|---|---|---|
PreToolUse |
Ja | Blockiert den Tool-Aufruf |
PermissionRequest |
Nein | Exit-Code 2 wird für diesen Event nicht berücksichtigt und der Berechtigungsfluss wird unverändert fortgesetzt. Verweigern Sie stattdessen durch das decision-Objekt |
UserPromptSubmit |
Ja | Blockiert den Prompt, sodass er Claude nie erreicht. Siehe Was ein blockierter Prompt hinterlässt |
UserPromptExpansion |
Ja | Blockiert die Erweiterung |
Stop |
Ja | Verhindert, dass Claude stoppt, setzt die Konversation fort |
SubagentStop |
Ja | Verhindert, dass der Subagent stoppt |
TeammateIdle |
Ja | Verhindert, dass der Teammate untätig wird, sodass er weiterarbeitet |
TaskCreated |
Ja | Rollback der Task-Erstellung |
TaskCompleted |
Ja | Verhindert, dass die Task als abgeschlossen markiert wird |
ConfigChange |
Ja | Blockiert die Konfigurationsänderung von der Wirksamkeit (außer policy_settings) |
StopFailure |
Nein | Ausgabe und Exit-Code werden ignoriert, außer terminalSequence |
PostToolUse |
Nein | Zeigt stderr Claude; das Tool ist bereits ausgeführt |
PostToolUseFailure |
Nein | Zeigt stderr Claude; das Tool ist bereits fehlgeschlagen |
PostToolBatch |
Ja | Stoppt die Agentenschleife vor dem nächsten Modellaufruf |
PermissionDenied |
Nein | Exit-Code und stderr werden ignoriert, da die Verweigerung bereits aufgetreten ist. Verwenden Sie JSON hookSpecificOutput.retry: true, um dem Modell zu sagen, dass es möglicherweise erneut versuchen kann; Claude Code ignoriert retry: true für No-Verdict-Verweigerungen |
Notification |
Nein | Exit-Code und stderr werden ignoriert |
SubagentStart |
Nein | Zeigt stderr nur dem Benutzer |
SessionStart |
Nein | Zeigt stderr nur dem Benutzer |
Setup |
Nein | Exit-Code und stderr werden ignoriert |
SessionEnd |
Nein | Zeigt stderr nur dem Benutzer |
CwdChanged |
Nein | Zeigt stderr nur dem Benutzer |
DirectoryAdded |
Nein | Stderr geht in das Debug-Protokoll; das Verzeichnis ist bereits hinzugefügt |
FileChanged |
Nein | Zeigt stderr nur dem Benutzer |
PreCompact |
Ja | Blockiert die Komprimierung |
PostCompact |
Nein | Zeigt stderr nur dem Benutzer |
PreModelSwitch |
Ja | Blockiert den Modellwechsel und zeigt stderr dem Benutzer |
PostModelSwitch |
Nein | Zeigt stderr nur dem Benutzer; das Modell ist bereits gewechselt |
Elicitation |
Ja | Lehnt die Anfrage ab, und es erscheint kein Dialog |
ElicitationResult |
Ja | Blockiert die Antwort (Aktion wird Ablehnung) |
WorktreeCreate |
Ja | Jeder Nonzero-Exit-Code führt dazu, dass die Worktree-Erstellung fehlschlägt |
WorktreeRemove |
Ja | Jeder Nonzero-Exit-Code führt dazu, dass die Worktree-Entfernung fehlschlägt, wenn das Verzeichnis danach noch existiert. Siehe WorktreeRemove für das, was mit dem Verzeichnis passiert |
InstructionsLoaded |
Nein | Exit-Code wird ignoriert |
MessageDisplay |
Nein | Der ursprüngliche Text wird angezeigt |
Für SessionStart, SubagentStart und PostModelSwitch rendert Claude Code den Exit-Code-2-stderr im Transkript als <hook name> hook error-Hinweis, auf die gleiche Weise wie es einen nicht blockierenden Fehler rendert. Claude sieht es nicht, und die Session oder der Subagent wird fortgesetzt. Für SubagentStart erscheint der Hinweis im eigenen Transkript des Subagenten, nicht in der übergeordneten Konversation.
HTTP-Response-Handling
HTTP Hooks verwenden HTTP-Statuscodes und Response-Bodies anstelle von Exit-Codes und stdout. Um die Aktion zu blockieren, wenn die Anfrage fehlschlägt oder einen Nicht-2xx-Status zurückgibt, setzen Sie onFailure: "block". Um einen Tool-Aufruf zu blockieren oder eine Berechtigung zu verweigern, geben Sie eine 2xx-Antwort mit einem JSON-Body zurück, der die entsprechenden Entscheidungsfelder enthält.
Nach einem nicht blockierenden Fehler wird die Aktion fortgesetzt, sofern es sich nicht um einen Hook handelt, dessen Fehlschlag die Aktion blockiert. Außer bei Events mit eigenem Fehlervertrag in der Pro-Event-Tabelle, wie WorktreeCreate, erhalten Sie für jede Antwort bzw. jeden Fehlschlag diese Ergebnisse:
- 2xx mit leerem Body: Erfolg, äquivalent zu Exit-Code 0 ohne Ausgabe
- 2xx mit JSON-Objekt-Body: geparst mit dem gleichen JSON-Ausgabe-Schema wie Command Hooks. Ein Body, der die Schema-Validierung nicht besteht, ist ein nicht blockierender Fehler
- 2xx mit jedem anderen Body, wie einfacher Text: nicht blockierender Fehler, behandelt wie ein Nicht-2xx-Status. Claude Code fügt den Text nicht zu Claudes Kontext hinzu
- Nicht-2xx-Status: nicht blockierender Fehler
- Verbindungsfehler: nicht blockierender Fehler
- Timeout: der Hook wird abgebrochen, wie unter Timeouts beschrieben
JSON-Ausgabe
Exit-Codes lassen Sie nur blockieren oder schweigen, aber JSON-Ausgabe gibt Ihnen feinere Kontrolle. Anstatt mit Code 2 zu beenden, um zu blockieren, beenden Sie mit 0 und drucken Sie ein JSON-Objekt auf stdout. Claude Code liest spezifische Felder aus diesem JSON, um Verhalten zu steuern, einschließlich Entscheidungskontrolle zum Blockieren, Zulassen oder Eskalieren an den Benutzer.
[!NOTE]
Wählen Sie einen Ansatz pro Hook: Verwenden Sie entweder Exit-Codes allein zum Signalisieren oder beenden Sie mit 0 und drucken Sie JSON für strukturierte Kontrolle. Wenn Sie sie mischen, behält Exit 2 seine blockierende Auswirkung, und Claude Code liest immer noch die JSON-Felder, mit der einen Elicitation-Ausnahme, die unter Exit-Code 2 notiert ist.
Geben Sie auf stdout nichts außer dem JSON-Objekt aus. Bei einem Hook, der nicht async ist, verhindert anderer Text dort, etwa eine Zeile, die Ihr Shell-Profil beim Start ausgibt, dass Claude Code das Objekt als JSON liest, und Hook JSON hat keine Auswirkung zeigt, wie Sie diesen Text finden und unterdrücken.
Die Strings additionalContext, systemMessage und initialUserMessage eines Hooks sowie sein einfacher stdout sind auf 10.000 Zeichen begrenzt:
- Umfang: Claude Code misst jeden String für sich, auch wenn mehrere Hooks für den gleichen Event ausgeführt werden. Für JSON-Ausgabe wird jedes Feld separat gemessen; einfacher stdout wird als Ganzes gemessen.
- Über dem Limit: Außer bei PreCompact-stdout speichert Claude Code die Ausgabe in einer Datei im Session-Verzeichnis. Es ersetzt die Ausgabe durch den Dateipfad und eine Vorschau von bis zu den ersten 2.000 Zeichen. Ein großes gültiges Bash-Ergebnis wird auf die gleiche Weise behandelt, beschrieben unter Ausgabelimits. Im Gegensatz zu dieser Bash-Obergrenze hat diese Obergrenze keine Einstellung oder Umgebungsvariable, um sie zu erhöhen.
- Datei lesen: Claude Code bittet Claude nicht, die Datei zu lesen, daher halten Sie alles, das Claude immer sehen muss, innerhalb der Obergrenze.
Das JSON-Objekt unterstützt drei Arten von Feldern:
- Universelle Felder wie
continuesind in der folgenden Tabelle aufgelistet. Jeder Event akzeptiert sie, aber einige Events verwerfen sie oder liefernsystemMessagean anderer Stelle als dem Transkript. Jeder Event-Abschnitt sagt so.terminalSequencefunktioniert auch auf diesen Events, mit den Ausnahmen, die unter Terminal-Benachrichtigungen ausgeben aufgelistet sind. - Top-Level
decisionundreasonwerden von einigen Events verwendet, um zu blockieren oder Feedback zu geben. hookSpecificOutputist ein verschachteltes Objekt für Events, die reichere Kontrolle benötigen. Es erfordert einhookEventName-Feld, das auf den Event-Namen gesetzt ist.
| Feld | Standard | Beschreibung |
|---|---|---|
continue |
true |
Wenn false, stoppt Claude die Verarbeitung vollständig, nachdem der Hook ausgeführt wird. Hat Vorrang vor allen Event-spezifischen Entscheidungsfeldern |
stopReason |
keine | Meldung, die dem Benutzer angezeigt wird, wenn continue false ist. Sie bleibt in der Konversation, sodass Claude sie sieht, wenn die Konversation fortgesetzt wird |
suppressOutput |
false |
Hat keine Auswirkung: Claude Code akzeptiert das Feld, aber handelt nicht danach. Der stdout eines erfolgreichen Hooks wird nie im Transkript angezeigt und wird im Debug-Protokoll aufgezeichnet |
systemMessage |
keine | Warnmeldung, die dem Benutzer angezeigt wird. In Agent SDK und --output-format stream-json-Ausgabe kann es als SDKInformationalMessage ankommen |
terminalSequence |
keine | Eine Terminal-Escape-Sequenz, die Claude Code in Ihrem Namen ausgeben soll, wie eine Desktop-Benachrichtigung, einen Fenstertitel oder eine Glocke. Beschränkt auf OSC 0/1/2/9/99/777 und BEL. Wenn der Wert etwas außerhalb der Allowlist enthält, wird das Feld ignoriert. Verwenden Sie dies anstelle des Schreibens zu /dev/tty, das für Hooks nicht verfügbar ist |
Um Claude vollständig zu stoppen:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }
Für PreToolUse- und PostToolUse-Hooks gilt der Stop auch, wenn der Tool-Aufruf fehlschlägt oder abgeschlossen wird, während Claude immer noch eine Antwort streamt.
Terminal-Benachrichtigungen ausgeben
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 terminalSequence-Feld zurück und Claude Code gibt sie für Sie über seinen eigenen Terminal-Schreibpfad aus. Dies ist race-frei und funktioniert unter Windows, wo es kein /dev/tty gibt.
Das Feld akzeptiert einen String von einer oder mehreren zugelassenen Escape-Sequenzen:
- OSC
0,1,2: Fenster- und Icon-Titel - 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
Sequenzen können mit BEL oder mit ST beendet werden. Alles außerhalb der Allowlist, einschließlich CSI-Cursor- und Farbsequenzen, OSC-Palettensequenzen, OSC-8-Hyperlinks, OSC-52-Clipboard-Schreibvorgänge und OSC 1337, wird abgelehnt und das Feld wird ignoriert.
Claude Code schreibt die Sequenz selbst, wenn es Ihre Hook-Ausgabe verarbeitet, sodass das Feld bei Events funktioniert, die systemMessage und continue verwerfen, wie Notification und StopFailure. Es gibt einige Einschränkungen:
- Claude Code schreibt die Sequenz nur in einer interaktiven Session und nur, während seine Oberfläche geöffnet ist. Im nicht interaktiven Modus mit dem
-p-Flag und im Agent SDK ignoriert es das Feld. - Das Feld funktioniert nur bei einem Hook, der nicht im Hintergrund ausgeführt wird, lassen Sie
asyncundasyncRewakealso deaktiviert. - Ein
WorktreeCreate-Command-Hook kann kein JSON zurückgeben, da Claude Code seinen stdout als Worktree-Pfad liest. Ein HTTP-WorktreeCreate-Hook gibt JSON zurück und kann das Feld enthalten.
Wenn Sie tmux oder GNU screen verwenden, schließen Sie zuerst die Einrichtung dafür ab. Das folgende Beispiel löst eine Desktop-Benachrichtigung von einem Notification-Hook in einem Terminal aus, das OSC 777 liest, etwa Ghostty oder Warp. Der Aufruf jq -nc --arg des Skripts erstellt die JSON-Ausgabe und escaped alle Anführungszeichen, Backslashes und Zeilenumbrüche in der Benachrichtigungsmeldung:
#!/bin/bash
# Notification hook: ping the desktop when Claude Code needs attention.
input=$(cat)
title="Claude Code"
body=$(jq -r '.message // "Needs your attention"' <<<"$input")
seq=$(printf '\033]777;notify;%s;%s\007' "$title" "$body")
jq -nc --arg seq "$seq" '{terminalSequence: $seq}'
Die { "terminalSequence": "..." }-Form ist die gleiche aus jeder Shell oder Sprache.
Terminal-Benachrichtigungen innerhalb von tmux oder GNU screen erhalten
Wenn Claude Code innerhalb von tmux oder GNU screen läuft, benötigen OSC-Sequenzen Folgendes, um Ihre Terminal-Anwendung zu erreichen:
- tmux 3.3 oder später: Fügen Sie
set -g allow-passthrough onzu~/.tmux.confhinzu und führen Sie danntmux source-file ~/.tmux.confin einer Shell aus. Die Option gilt für jedes Programm in diesen Panes, nicht nur für Claude Code. Mitonkönnen nur Panes, die tmux gerade anzeigt, die Sequenzen senden. Damit auch andere Panes in einer Sitzung, mit der Sie verbunden sind, sie senden können, verwenden Sie ab tmux 3.4allanstelle vonon. - GNU screen: Lassen Sie das Fenster von Claude Code in screen angezeigt, da Sequenzen aus einem Hintergrundfenster Ihre Terminal-Anwendung nicht erreichen
Kontext für Claude hinzufügen
Das additionalContext-Feld übergibt einen String von Ihrem Hook in Claudes Kontextfenster. Claude Code umhüllt den String in eine System-Erinnerung und fügt ihn in die Konversation an dem Punkt ein, an dem der Hook ausgelöst wurde. Claude liest die Erinnerung bei der nächsten Modellanfrage, aber sie erscheint nicht als Chat-Nachricht in der Schnittstelle.
Geben Sie additionalContext innerhalb von hookSpecificOutput neben dem Event-Namen zurück:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "This file is generated. Edit src/schema.ts and run `bun generate` instead."
}
}
Wo die Erinnerung erscheint, hängt vom Event ab:
- SessionStart und SubagentStart: am Anfang der Konversation, vor dem ersten Prompt
- UserPromptSubmit und UserPromptExpansion: neben dem eingereichten Prompt
- PreToolUse, PostToolUse, PostToolUseFailure und PostToolBatch: neben dem Tool-Ergebnis
- Stop und SubagentStop: am Ende des Turns. Die Konversation wird fortgesetzt, sodass Claude auf das Feedback reagieren kann. Siehe Stop-Entscheidungskontrolle
- PostModelSwitch: mit der nächsten Anfrage nach dem Wechsel. Siehe PostModelSwitch-Entscheidungskontrolle für Timing
Wenn mehrere Hooks additionalContext für den gleichen Event zurückgeben, empfängt Claude alle Werte.
Wenn Ihr String ein <system-reminder>- oder </system-reminder>-Tag enthält, erhält Claude den String, wobei das < dieses Tags durch < ersetzt ist.
Wenn ein Wert 10.000 Zeichen überschreitet, schreibt Claude Code den Text in eine Datei im Session-Verzeichnis und übergibt Claude stattdessen den Dateipfad mit einer Vorschau von bis zu den ersten 2.000 Zeichen. Claude kann die Datei lesen, aber Claude Code bittet nicht darum.
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, Deployment-Ziel 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 Issues, die Ihnen zugewiesen sind, aktuelle CI-Ergebnisse, Inhalte, die von einem internen Service abgerufen wurden
Für Anweisungen, die sich nie ändern, bevorzugen Sie CLAUDE.md. Es wird ohne Ausführung eines Skripts geladen und ist der Standard-Ort für statische Projektkonventionen.
Schreiben Sie den Text als sachliche Aussagen statt imperativer Systembefehle. Formulierungen wie „Das Deployment-Ziel ist Produktion“ oder „Dieses Repo verwendet 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 übermittelt, anstatt ihn als Kontext zu behandeln.
Claude Code speichert den eingefügten Text im Session-Transkript. Für Mid-Session-Events wie PostToolUse oder UserPromptSubmit, wenn Sie mit --continue oder --resume fortfahren, spielt Claude Code den gespeicherten Text erneut ab, anstatt den Hook für vergangene Turns erneut auszuführen, sodass Werte wie Zeitstempel oder Commit-SHAs veraltet werden. SessionStart-Hooks werden bei Wiederaufnahme mit source auf "resume" oder "fork" gesetzt, wenn Sie --fork-session hinzugefügt haben, erneut ausgeführt, sodass sie ihren Kontext aktualisieren können.
Entscheidungskontrolle
Nicht jeder Event unterstützt Blockierung oder Verhaltenskontrolle durch JSON. Die Events, 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:
| Events | Entscheidungsmuster | Schlüsselfelder |
|---|---|---|
| UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact | Top-Level decision |
decision: "block", reason. Stop und SubagentStop akzeptieren auch hookSpecificOutput.additionalContext für nicht-fehlerhafte Rückmeldung, die die Konversation fortsetzt |
| TeammateIdle, TaskCompleted | Exit-Code oder continue: false |
Exit-Code 2 blockiert die Aktion mit stderr-Rückmeldung. JSON {"continue": false, "stopReason": "..."} stoppt auch den Teammate vollständig, was dem Stop-Hook-Verhalten entspricht; TaskCompleted ignoriert es, wenn das TaskUpdate-Tool den Event ausgelöst hat |
| TaskCreated | Exit-Code oder Top-Level decision |
Exit-Code 2 oder decision: "block" bricht die Task ab und gibt die Meldung an Claude zurück. continue: false wird ignoriert |
| PreToolUse | hookSpecificOutput |
permissionDecision (allow/deny/ask/defer), permissionDecisionReason |
| PreModelSwitch | hookSpecificOutput oder Top-Level decision |
permissionDecision (allow/deny/ask), permissionDecisionReason. decision: "block" bricht auch den Wechsel ab |
| PermissionRequest | hookSpecificOutput |
decision.behavior (allow/deny) |
| PermissionDenied | hookSpecificOutput |
retry: true teilt dem Modell mit, dass es den verweigerten Tool-Aufruf möglicherweise erneut versuchen kann; Claude Code ignoriert es für No-Verdict-Verweigerungen |
| WorktreeCreate | Pfad-Rückgabe | Command Hook druckt Pfad auf stdout; HTTP Hook gibt hookSpecificOutput.worktreePath zurück. Hook-Fehler oder fehlender Pfad schlägt die Erstellung fehl |
| WorktreeRemove | Exit-Code | Jeder Nonzero-Exit-Code lässt die Entfernung fehlschlagen, wenn das Verzeichnis danach noch existiert. JSON-Ausgabe wird verworfen |
| Elicitation, ElicitationResult | hookSpecificOutput oder Top-Level decision |
action (accept/decline/cancel), content (Formularfeldwerte). decision: "block" lehnt ebenfalls ab |
| MessageDisplay | hookSpecificOutput |
displayContent ersetzt den angezeigten Text auf dem Bildschirm. Nur Anzeige: das Transkript und das, was Claude sieht, behalten das Original |
| SessionStart, SubagentStart, PostModelSwitch | Nur Kontext | hookSpecificOutput.additionalContext fügt Kontext für Claude hinzu. SessionStart akzeptiert auch initialUserMessage, watchPaths, sessionTitle und reloadSkills. Keine Blockierung oder Entscheidungskontrolle |
| Setup, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, DirectoryAdded, FileChanged | Keine | Keine Entscheidungskontrolle. Wird für Nebeneffekte wie Protokollierung oder Bereinigung verwendet |
Einige Events können auch Inhalte umschreiben, anstatt nur zu erlauben 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 Tool-Ergebnis. Siehe PostToolUse-EntscheidungskontrolleUserPromptSubmit: kann den Prompt nicht ersetzen; es injiziert nuradditionalContextdaneben
Für Redaktions- oder Transformationsfälle, fangen Sie bei PreToolUse für ausgehende Tool-Eingaben und PostToolUse für eingehende Tool-Ergebnisse ab.
Hier sind Beispiele für jedes Muster in Aktion:
[tab] Top-level decision
Der einzige Wert für
decisionist"block". Um die Aktion fortzusetzen, lassen Siedecisionaus Ihrem JSON weg oder beenden Sie mit 0 ohne JSON:{ "decision": "block", "reason": "Test suite must pass before proceeding" }
[tab] PreToolUse
Verwendet
hookSpecificOutputfür reichere Kontrolle: erlauben, verweigern oder eskalieren an den Benutzer. Sie können auch Tool-Eingabe vor der Ausführung ändern oder zusätzlichen Kontext für Claude injizieren. Siehe PreToolUse-Entscheidungskontrolle für den vollständigen Satz von Optionen.{ "hookSpecificOutput": { "hookEventName": "PreToolUse", "permissionDecision": "deny", "permissionDecisionReason": "Database writes are not allowed" } }
[tab] PermissionRequest
Verwendet
hookSpecificOutput, um eine Berechtigungsanfrage im Namen des Benutzers zu erlauben oder zu verweigern. Beim Erlauben können Sie auch die Tool-Eingabe ändern oder Berechtigungsregeln anwenden, sodass der Benutzer nicht erneut aufgefordert wird. Siehe PermissionRequest-Entscheidungskontrolle für den vollständigen Satz von Optionen.{ "hookSpecificOutput": { "hookEventName": "PermissionRequest", "decision": { "behavior": "allow", "updatedInput": { "command": "npm run lint" } } } }
Für erweiterte Beispiele einschließlich Bash-Befehlsvalidierung, Prompt-Filterung und Auto-Approval-Skripte, siehe Was Sie automatisieren können im Leitfaden und die Bash-Befehlsvalidierungs-Referenzimplementierung.
Hook-Events
Jedes Event entspricht einem Punkt im Lebenszyklus von Claude Code, an dem Hooks ausgeführt werden können. Die folgenden Abschnitte sind nach diesem Lebenszyklus geordnet: von der Einrichtung der Sitzung über die Agentenschleife bis zum Ende der Sitzung. Jeder Abschnitt beschreibt, wann das Event ausgelöst wird, welche Matcher es unterstützt, welche JSON-Eingabe es erhält und wie Sie das Verhalten über die Ausgabe steuern.
SessionStart
Wird ausgeführt, wenn Claude Code eine neue Sitzung startet oder eine bestehende Sitzung fortsetzt. Nützlich, um Entwicklungskontext wie bestehende Issues oder aktuelle Änderungen an Ihrer Codebasis zu laden oder Umgebungsvariablen einzurichten. Für statischen Kontext, der kein Skript erfordert, verwenden Sie stattdessen CLAUDE.md.
SessionStart wird bei jeder Sitzung ausgeführt, halten Sie diese Hooks daher schnell. Nur Hooks vom Typ type: "command" und type: "mcp_tool" werden unterstützt. Unter Felder für MCP-Tool-Hooks erfahren Sie, wann mcp_tool-Hooks ausgeführt werden.
Der Matcher-Wert entspricht der Art, wie die Sitzung gestartet wurde:
| Matcher | Wann er ausgelöst wird |
|---|---|
startup |
Neue Sitzung |
resume |
--resume, --continue oder /resume |
clear |
/clear |
compact |
Automatische oder manuelle Komprimierung |
fork |
Eine neue Sitzung, die von einer bestehenden abgezweigt wurde: --fork-session mit --resume oder --continue, die Hintergrundkopie von /fork, /branch oder eine Konversation, die Sie in den Hintergrund verschieben |
Wenn Sie eine interaktive Sitzung starten, beim Start eine Konversation mit --continue oder --resume fortsetzen oder /clear ausführen, laufen SessionStart-Hooks im Hintergrund. Sie können sofort tippen, und eine fortgesetzte Konversation erscheint, ohne auf die Hooks zu warten. Die erste Antwort von Claude wartet dennoch, bis die Hooks fertig sind, damit ihr Kontext Claude erreicht.
Wenn Sie innerhalb einer Sitzung mit /resume die Konversation wechseln, wartet der Wechsel stattdessen, bis die Hooks fertig sind. Wenn Sie /clear ausführen oder zu einer anderen Konversation wechseln, während Hintergrund-Hooks noch laufen, wird nichts von dem, was sie zurückgeben, auf die Sitzung angewendet.
Dasselbe Warten gilt beim Start, auch bei einer fortgesetzten Sitzung: Ein Prompt, den Sie senden, während SessionStart-Hooks noch laufen, erreicht Claude erst, wenn diese fertig sind.
Während beider Wartezeiten können Sie Esc drücken, um den Prompt zurück in die Eingabe zu holen, ohne ihn zu senden. Die Hooks laufen weiter.
SessionStart-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten SessionStart-Hooks source und optional model, agent_type und session_title:
| Feld | Beschreibung |
|---|---|
source |
Wie die Sitzung gestartet wurde: "startup" für neue Sitzungen, "resume" für fortgesetzte Sitzungen, "clear" nach /clear, "compact" nach einer Komprimierung oder "fork" für eine neue Sitzung, die von einer bestehenden abgezweigt wurde |
model |
Die aktive Modellkennung. Sie kann fehlen, zum Beispiel nach /clear oder wenn eine Sitzung über die Konversationswiederherstellung wiederhergestellt wird. Prüfen Sie daher, ob das Feld vorhanden ist, bevor Sie es lesen |
agent_type |
Der Agentenname, vorhanden, wenn Sie Claude Code mit claude --agent <name> starten |
session_title |
Der benutzerdefinierte Titel der Sitzung, vorhanden, wenn einer gesetzt ist, zum Beispiel mit --name, /rename, der sessionTitle-Ausgabe eines Hooks oder renameSession() aus dem Agent SDK. Ein Hook, der sessionTitle ausgibt, kann zuerst dieses Feld prüfen, um einen bestehenden benutzerdefinierten Titel nicht zu überschreiben |
Eine Sitzung, die Sie nicht benannt haben, kann dennoch einen generierten Titel haben. Dieser Titel ist kein benutzerdefinierter Titel und erscheint nicht in session_title.
Wenn source den Wert "resume" oder "fork" hat und das Transkript mindestens eine Antwort von Claude enthält, erhalten SessionStart-Hooks zusätzlich die vier folgenden Felder. Ihr Hook kann damit vor der ersten Anfrage melden, was das Fortsetzen einer veralteten Konversation kostet, zum Beispiel in einer systemMessage. Diese Felder erfordern Claude Code v2.1.251 oder höher.
| Feld | Beschreibung |
|---|---|
seconds_since_last_response |
Echtzeit in Sekunden seit der letzten Antwort im fortgesetzten Transkript |
context_tokens |
Token, die die erste Anfrage der fortgesetzten Sitzung erneut als Prompt sendet |
prompt_cache_likely_expired |
true, wenn die letzte Antwort älter ist als die Lebensdauer des Prompt-Cache der Sitzung oder eine spätere Komprimierung die zwischengespeicherte Konversation ersetzt hat |
estimated_cache_write_usd |
Geschätzte Kosten in US-Dollar für das Schreiben von context_tokens in den Prompt-Cache mit dem Modell der Sitzung, ohne die Antwort |
Dieses Beispiel zeigt die Eingabe für eine Sitzung, die 90 Minuten nach ihrer letzten Antwort fortgesetzt wurde:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SessionStart",
"source": "resume",
"model": "claude-opus-5",
"seconds_since_last_response": 5400,
"context_tokens": 182340,
"prompt_cache_likely_expired": true,
"estimated_cache_write_usd": 1.1396
}
SessionStart-Entscheidungssteuerung
Ein SessionStart-Hook kann Kontext für Claude hinzufügen, die erste Benutzernachricht liefern, den Sitzungstitel setzen, Dateien überwachen und Skills neu laden. Geben Sie dazu jeweils das entsprechende Feld zurück, zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen:
| Feld | Beschreibung |
|---|---|
additionalContext |
String, der zu Beginn der Konversation vor dem ersten Prompt zum Kontext von Claude hinzugefügt wird. Unter Kontext für Claude hinzufügen erfahren Sie, wie der Text übermittelt wird und was Sie hineinschreiben sollten |
initialUserMessage |
String, der im nicht interaktiven Modus mit dem Flag -p als erste Benutzernachricht der Sitzung verwendet wird. Er wird zum ersten Turn, auch wenn Sie keinen Prompt übergeben. Ein Prompt, den Sie übergeben, folgt als nächster Turn |
sessionTitle |
Setzt den Sitzungstitel, mit derselben Wirkung wie /rename. Gilt, wenn source den Wert "startup", "resume" oder "fork" hat |
watchPaths |
Array absoluter Pfade, die während dieser Sitzung auf FileChanged-Events überwacht werden |
reloadSkills |
Boolean. Bei true durchsucht Claude Code die Skill- und Befehlsverzeichnisse erneut, nachdem die SessionStart-Hooks abgeschlossen sind. Siehe Skills neu laden, die ein Hook installiert |
Diese Ausgabe fügt Kontext hinzu und benennt die Sitzung:
{
"hookSpecificOutput": {
"hookEventName": "SessionStart",
"additionalContext": "Current branch: feat/auth-refactor\nUncommitted changes: src/auth.ts, src/login.tsx\nActive issue: #4211 Migrate to OAuth2",
"sessionTitle": "auth-refactor"
}
}
Ein Hook, der nur Kontext hinzufügt, kann diesen ausgeben, ohne JSON zu erstellen, da Claude Code die Klartext-Ausgabe auf stdout eines SessionStart-Hooks zum Kontext von Claude hinzufügt.
Wenn der SessionStart-Hook Ihres Plugins initialUserMessage oder sessionTitle liefert, installieren Sie das Plugin, bevor die Sitzung startet. Claude Code ignoriert beide Felder von einem Plugin, dessen Installation erst abgeschlossen wird, nachdem die SessionStart-Hooks gelaufen sind.
Skills neu laden, die ein Hook installiert
Damit Skills, die ein SessionStart-Hook installiert, in derselben Sitzung verfügbar sind, geben Sie reloadSkills zurück. Die Skill-Erkennung läuft normalerweise, bevor die SessionStart-Hooks fertig sind. Ohne dieses Feld können daher Dateien, die ein Hook in ~/.claude/skills/ oder .claude/skills/ schreibt, fehlen, wenn der erste Prompt ausgeführt wird.
Dieses Beispiel synchronisiert ein gemeinsames Skills-Repository und fordert die erneute Durchsuchung an:
#!/bin/bash
git -C ~/.claude/skills/team-skills pull --quiet 2>/dev/null || \
git clone --quiet https://git.example.com/your-org/team-skills.git ~/.claude/skills/team-skills
echo '{"hookSpecificOutput": {"hookEventName": "SessionStart", "reloadSkills": true}}'
Die Repository-URL ist ein Platzhalter. Ersetzen Sie sie durch Ihr eigenes Skills-Repository.
Umgebungsvariablen dauerhaft setzen
SessionStart-Hooks haben Zugriff auf die Umgebungsvariable CLAUDE_ENV_FILE, die einen Dateipfad bereitstellt, in dem Sie Umgebungsvariablen für die Shell-Befehle dauerhaft speichern können, die Claude später in der Sitzung ausführt.
Um einzelne Umgebungsvariablen zu setzen, schreiben Sie export-Anweisungen in CLAUDE_ENV_FILE. Verwenden Sie Anhängen (>>), um Variablen zu erhalten, die von anderen Hooks gesetzt wurden:
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi
exit 0
Um alle Umgebungsänderungen aus Einrichtungsbefehlen zu erfassen, vergleichen Sie die exportierten Variablen vorher und nachher:
#!/bin/bash
ENV_BEFORE=$(export -p | sort)
# Run your setup commands that modify the environment
source ~/.nvm/nvm.sh
nvm use 20
if [ -n "$CLAUDE_ENV_FILE" ]; then
ENV_AFTER=$(export -p | sort)
comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi
exit 0
Jeder Bash-Befehl führt den Inhalt der Datei vor dem eigentlichen Befehl als Shell-Code aus. Eine Zeile darin kann daher alles verwenden, was Bash auswertet, etwa den $PATH-Verweis in export PATH="$PATH:./node_modules/.bin".
Dauerhaft gesetzte Variablen in PowerShell-Befehlen
PowerShell-Befehle erhalten die Variablen aus CLAUDE_ENV_FILE ebenfalls, in Claude Code v2.1.296 oder höher, aber PowerShell führt die Datei nie aus. Stattdessen liest Claude Code die Zuweisungen aus der Datei aus und kopiert sie in die Umgebung des PowerShell-Befehls. Das geschieht nur, wenn jede Zeile einer der folgenden Arten entspricht, und zwar über alles hinweg, was jeder Hook in dieser Sitzung geschrieben hat, sowie über jedes Skript, auf das Sie CLAUDE_ENV_FILE vor dem Start gesetzt haben:
- Eine Leerzeile oder ein
#-Kommentar - Eine einzelne Zuweisung am Zeilenanfang, geschrieben als
export NAME=value,declare -x NAME=valueoderNAME=value, mit einem Wert, den Bash exakt so verwenden würde, wie er geschrieben ist, bestehend aus einer beliebigen Kombination dieser Bestandteile: Text ohne Anführungszeichen, der nur Buchstaben, Ziffern und die Zeichen_ @ % + = : , . / -verwendet, Text in einfachen Anführungszeichen und Text in doppelten Anführungszeichen, in dem jedes$, jeder Backtick und jedes"mit einem Backslash maskiert ist
Wenn irgendeine Zeile von anderer Art ist, etwa export PATH="$PATH:./node_modules/.bin" mit seinem nicht maskierten $PATH, ein source-Befehl oder die $'...'-Strings, die direnv export bash ausgibt, erhalten PowerShell-Befehle keine der Variablen, und claude --debug protokolliert Session environment is not all plain assignments. Bash-Befehle erhalten weiterhin alle Variablen. Unter Windows erhalten PowerShell-Befehle außerdem keine Variable, deren Wert / oder \ enthält, weil Git Bash und Windows Pfade unterschiedlich schreiben. Ein PowerShell-Befehl, der in einer Sandbox ausgeführt wird, erhält keine der Variablen.
[!NOTE]
CLAUDE_ENV_FILEist für SessionStart-, Setup-, CwdChanged- und FileChanged-Hooks verfügbar. Andere Hook-Events haben keinen Zugriff auf diese Variable, ebenso wenig ein Hook, der in PowerShell läuft, sei es über"shell": "powershell"oder standardmäßig unter Windows ohne Git Bash.
Setup
Wird nur ausgelöst, wenn Sie Claude Code mit --init-only starten oder mit --init oder --maintenance im nicht interaktiven Modus mit dem Flag -p. Beim normalen Start wird es nicht ausgelöst. Verwenden Sie es für die einmalige Installation von Abhängigkeiten oder für geplante Bereinigungen, die Sie explizit aus CI oder Skripten auslösen, getrennt vom normalen Sitzungsstart. Für die Initialisierung pro Sitzung verwenden Sie stattdessen SessionStart.
Der Matcher-Wert entspricht dem CLI-Flag, das den Hook ausgelöst hat:
| Matcher | Wann er ausgelöst wird |
|---|---|
init |
claude --init-only oder claude -p --init |
maintenance |
claude -p --maintenance |
Wenn Sie claude --init-only ausführen, führt Claude Code Setup-Hooks und SessionStart-Hooks mit dem Matcher startup aus und beendet sich dann, ohne eine Konversation zu starten.
Wenn Sie mit -p eine Konversation starten oder fortsetzen, müssen Sie auch einen Prompt angeben, als Argument oder über stdin weitergeleitet. Sie können den Prompt weglassen, wenn ein SessionStart-Hook initialUserMessage liefert oder wenn Sie eine Sitzung mit einem zurückgestellten Tool-Aufruf fortsetzen.
Bei Erfolg gibt --init-only nichts im Terminal aus. Um zu bestätigen, dass die Hooks gelaufen sind, starten Sie mit claude --debug-file <path> --init-only, ersetzen <path> durch den Speicherort einer Logdatei und prüfen das Log auf die Einträge der Setup- und SessionStart-Hooks.
Da Setup nicht bei jedem Start ausgelöst wird, kann sich ein Plugin, das eine installierte Abhängigkeit benötigt, nicht allein auf Setup verlassen. Das praktikable Muster besteht darin, bei der ersten Verwendung auf die Abhängigkeit zu prüfen und sie bei Fehlen zu installieren, zum Beispiel mit einem Hook oder Skill, der auf ${CLAUDE_PLUGIN_DATA}/node_modules prüft und bei Fehlen npm install ausführt. Unter persistentes Datenverzeichnis erfahren Sie, wo Sie installierte Abhängigkeiten speichern. Wenn Sie Ihr Plugin über einen Marketplace verteilen, benötigen Sie dieses Muster möglicherweise nicht: Claude Code installiert berechtigte Node.js-Paketabhängigkeiten automatisch, wenn es das Plugin zwischenspeichert.
Setup-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten Setup-Hooks ein Feld trigger, das entweder auf "init" oder "maintenance" gesetzt ist:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Setup",
"trigger": "init"
}
Setup-Entscheidungssteuerung
Setup-Hooks können nicht blockieren; die Ausführung wird bei jedem Exit-Code fortgesetzt. Bei jedem Exit-Code verwirft Claude Code die JSON-Ausgabefelder eines Setup-Hooks, etwa systemMessage, continue und hookSpecificOutput.additionalContext. Mit -p erscheinen stdout, stderr und Exit-Code eines Setup-Hooks in der Ausgabe des Laufs nur als hook_response-Events, wenn Sie mit --output-format stream-json --verbose starten.
Setup-Hooks haben Zugriff auf CLAUDE_ENV_FILE. Wenn Ihr Hook Variablen in diese Datei schreibt, bleiben sie für nachfolgende Bash-Befehle der Sitzung erhalten, wie bei SessionStart-Hooks. Bei Setup werden nur Hooks vom Typ type: "command" ausgeführt. Ein type: "mcp_tool"-Hook bei Setup wird immer übersprungen, wie unter MCP-Tool-Hook-Felder beschrieben.
InstructionsLoaded
Wird ausgelöst, wenn eine CLAUDE.md- oder .claude/rules/*.md-Datei in den Kontext geladen wird. Dieses Event wird beim Sitzungsstart für sofort geladene Dateien ausgelöst und später erneut, wenn Dateien verzögert geladen werden, zum Beispiel wenn Claude auf ein Unterverzeichnis zugreift, das eine verschachtelte CLAUDE.md enthält, oder wenn bedingte Regeln mit paths:-Frontmatter zutreffen. Der Hook unterstützt weder Blockieren noch Entscheidungssteuerung. Er läuft asynchron zu Beobachtungszwecken.
Dieses Event wird nicht ausgelöst, wenn Claude AGENTS.md direkt liest, über die Einstellung Project instructions. Es wird ausgelöst, wenn eine CLAUDE.md Ihre AGENTS.md importiert, mit load_reason auf include gesetzt wie bei jeder anderen importierten Datei, und wenn CLAUDE.md ein Symlink darauf ist, als normaler Ladevorgang von CLAUDE.md.
Der Matcher wird gegen load_reason ausgewertet. Verwenden Sie zum Beispiel "matcher": "session_start", um nur für Dateien auszulösen, die beim Sitzungsstart geladen werden, oder "matcher": "path_glob_match|nested_traversal", um nur bei verzögertem Laden auszulösen.
InstructionsLoaded-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten InstructionsLoaded-Hooks diese Felder:
| Feld | Beschreibung |
|---|---|
file_path |
Absoluter Pfad zur geladenen Anweisungsdatei |
memory_type |
Geltungsbereich der Datei: "User", "Project", "Local" oder "Managed" |
load_reason |
Warum die Datei geladen wurde: "session_start", "nested_traversal", "path_glob_match", "include" oder "compact". Der Wert "compact" wird ausgelöst, wenn Anweisungsdateien nach einer Komprimierung erneut geladen werden |
globs |
Pfad-Glob-Muster aus dem paths:-Frontmatter der Datei, falls vorhanden. Nur bei path_glob_match-Ladevorgängen vorhanden |
trigger_file_path |
Pfad zu der Datei, deren Zugriff diesen Ladevorgang ausgelöst hat, bei verzögertem Laden |
parent_file_path |
Pfad zur übergeordneten Anweisungsdatei, die diese eingebunden hat, bei include-Ladevorgängen |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "InstructionsLoaded",
"file_path": "/Users/my-project/CLAUDE.md",
"memory_type": "Project",
"load_reason": "session_start"
}
InstructionsLoaded-Entscheidungssteuerung
InstructionsLoaded-Hooks haben keine Entscheidungssteuerung. Sie können das Laden von Anweisungen weder blockieren noch ändern. Claude Code verwirft ihre JSON-Ausgabefelder, etwa systemMessage und continue. Verwenden Sie dieses Event für Audit-Logs, Compliance-Nachverfolgung oder Beobachtbarkeit.
UserPromptSubmit
Wird ausgeführt, wenn ein Prompt abgesendet wird, bevor Claude ihn verarbeitet. Damit können Sie basierend auf dem Prompt bzw. der Konversation zusätzlichen Kontext hinzufügen, Prompts validieren oder bestimmte Arten von Prompts blockieren.
UserPromptSubmit-Hooks werden nicht nur bei Prompts ausgelöst, die Sie eingeben. Claude Code führt sie auch aus, wenn:
- Eine sitzungsbezogene geplante Aufgabe wird ausgelöst, einschließlich einer
/loop-Iteration - ein Subagent im Hintergrund an die Sitzung zurückmeldet, die ihn gestartet hat
- eine andere Sitzung eine Nachricht an Ihre Hauptkonversation sendet
UserPromptSubmit-Hooks haben für die Typen command, http und mcp_tool einen Standard-Timeout von 30 Sekunden, kürzer als der Standard von 600 Sekunden für diese Typen bei den meisten anderen Events. Da dieser Hook vor jedem Prompt läuft und die Modellverarbeitung blockiert, bis er abgeschlossen ist, bringt ein hängender Hook die Sitzung zum Stillstand. Wenn Ihr Hook mehr Zeit benötigt, setzen Sie das Feld timeout im Hook-Eintrag.
Abgesehen von einem Befehls-Hook, den Sie mit async: true ausführen, wird ein UserPromptSubmit-Befehls-, HTTP- oder MCP-Tool-Hook, der seinen Timeout erreicht, abgebrochen, und seine Ausgabe, einschließlich eines etwaigen additionalContext, wird verworfen. Der Prompt erreicht Claude trotzdem, ohne diesen Kontext. Um den Prompt stattdessen zu blockieren, setzen Sie onFailure: "block" bei einem Befehls- oder HTTP-Hook. Das Transkript zeigt einen Hinweis, der den Hook und den ausgelösten Timeout nennt und angibt, dass die Ausgabe verworfen wurde.
Ein Agent-SDK-Callback-Hook bei UserPromptSubmit, der seinen Timeout erreicht, blockiert den Prompt mit einer Meldung, die den Hook und den Timeout nennt, da ein Callback dort als Richtlinien-Gate fungieren kann, das nicht offen fehlschlagen darf. Die Sitzung wird fortgesetzt.
Esc während einer Prompt-Prüfung
Wenn Sie Esc drücken oder die Sitzung unterbrechen, während Hooks noch einen Prompt prüfen, wird die Prüfung beendet und der Prompt erreicht Claude nicht. Text, den Sie im Terminal eingegeben haben, kehrt ins Prompt-Eingabefeld zurück, und die Hooks prüfen ihn erneut, wenn Sie ihn noch einmal senden.
Ein Hook kann den Prompt genau in dem Moment blockieren, in dem Sie unterbrechen. Der Prompt bleibt dann blockiert: Sie sehen die Blockierungsnachricht dieses Hooks, und der Text kehrt nicht ins Eingabefeld zurück.
UserPromptSubmit-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten UserPromptSubmit-Hooks das Feld prompt mit dem abgesendeten Text. Eingefügter Inhalt, der zu einem Platzhalter [Pasted text #N] zusammengeklappt wurde, kommt an Ort und Stelle erweitert an. In Sitzungen, in denen Claude Code eingefügten Text für Claude kennzeichnet, steht dieser erweiterte Inhalt zwischen einer Zeile <pasted_content id="…"> und einer Zeile </pasted_content id="…">. Berücksichtigen Sie diese Zeilen, wenn Ihr Hook den Prompt parst.
UserPromptSubmit-Hooks erhalten außerdem session_title, wenn die Sitzung einen benutzerdefinierten Titel hat, mit derselben Bedeutung wie das SessionStart-Feld session_title.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptSubmit",
"prompt": "Write a function to calculate the factorial of a number"
}
Wenn der Prompt ein Skill oder ein benutzerdefinierter Befehl ist, den Sie eingegeben haben, etwa /deploy staging, erhält der Hook diese Zeile so, wie Sie sie eingegeben haben, und nicht den erweiterten Skill-Inhalt. Wenn der Hook den Prompt blockiert, wird der Befehl nicht ausgeführt, sodass auch Shell-Befehle, die der Skill mit !`<command>` einfügt, nicht ausgeführt werden. Um stattdessen auf den Namen oder die Argumente des Befehls abzugleichen, verwenden Sie UserPromptExpansion.
UserPromptSubmit-Entscheidungssteuerung
UserPromptSubmit-Hooks können steuern, ob ein abgesendeter Prompt verarbeitet wird, und Kontext hinzufügen. Alle JSON-Ausgabefelder sind verfügbar.
Es gibt zwei Möglichkeiten, bei Exit-Code 0 Kontext zur Konversation hinzuzufügen:
- Klartext auf stdout: Claude Code fügt stdout, das es als Klartext behandelt, zum Kontext von Claude hinzu
- JSON mit
additionalContext: Verwenden Sie das unten stehende JSON-Format für mehr Kontrolle. Das FeldadditionalContextwird als Kontext hinzugefügt
Keiner der beiden Kanäle erzeugt einen sichtbaren Eintrag im Transkript. Reines stdout und der Wert von additionalContext werden jeweils als System-Erinnerung eingefügt, die mit dem Namen des Hooks beginnt; Claude liest beide. Um die Übermittlung zu bestätigen, prüfen Sie das Debug-Log.
Um einen Prompt zu blockieren, geben Sie ein JSON-Objekt zurück, bei dem decision auf "block" gesetzt ist:
| Feld | Beschreibung |
|---|---|
decision |
"block" stoppt den Prompt, bevor er Claude erreicht. Lassen Sie es weg, damit der Prompt fortgesetzt wird |
reason |
Wird dem Benutzer angezeigt, wenn decision den Wert "block" hat. Wird nicht zum Kontext hinzugefügt |
additionalContext |
String, der zusammen mit dem abgesendeten Prompt zum Kontext von Claude hinzugefügt wird. Siehe Kontext für Claude hinzufügen |
sessionTitle |
Setzt den Sitzungstitel. Verwenden Sie es, um Sitzungen automatisch anhand des Prompt-Inhalts zu benennen |
suppressOriginalPrompt |
Bei true lässt es den Prompt-Text aus der Blockierungsmeldung weg, wenn der Hook den Prompt blockiert. Siehe Was ein blockierter Prompt hinterlässt |
Ein Hook, der durch Beenden mit Exit-Code 2 blockiert, wird genauso behandelt wie reason: Die Blockierungsmeldung zeigt dem Benutzer den stderr-Text, und dieser wird nicht zum Kontext hinzugefügt.
{
"decision": "block",
"reason": "Explanation for decision",
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"additionalContext": "My additional context here",
"sessionTitle": "My session title",
"suppressOriginalPrompt": true
}
}
Was ein blockierter Prompt hinterlässt
Ein blockierter Prompt erreicht Claude nie, aber sein Text wird nicht überall entfernt. Standardmäßig endet die dem Benutzer angezeigte Blockierungsmeldung mit Original prompt:, gefolgt vom abgesendeten Text, und Claude Code schreibt diese Meldung in die Transkriptdatei der Sitzung auf der Festplatte. Um den Text aus der Meldung wegzulassen, geben Sie JSON mit "suppressOriginalPrompt": true innerhalb von hookSpecificOutput aus. Das funktioniert unabhängig davon, ob der Hook mit decision: "block" oder durch Beenden mit Exit-Code 2 blockiert.
suppressOriginalPrompt ändert nur die Blockierungsmeldung. Der abgesendete Text kann weiterhin in lokalen Dateien wie dem Sitzungstranskript und Ihrem Prompt-Verlauf erscheinen. Ein blockierender Hook ist daher kein Weg, ein Geheimnis von der Festplatte fernzuhalten. Um diese Dateien zu begrenzen oder zu entfernen, siehe Klartextspeicherung und Lokale Daten löschen.
UserPromptExpansion
Wird ausgeführt, wenn ein vom Benutzer eingegebener Befehl zu einem Prompt erweitert wird, bevor er Claude erreicht. Verwenden Sie dies, um bestimmte Befehle vom direkten Aufruf auszuschließen, Kontext für einen bestimmten Skill einzufügen oder zu protokollieren, welche Befehle Benutzer aufrufen. Zum Beispiel kann ein Hook, der auf deploy zutrifft, /deploy blockieren, sofern keine Genehmigungsdatei vorhanden ist, oder ein Hook, der auf einen Review-Skill zutrifft, die Review-Checkliste des Teams als additionalContext anhängen.
Dieses Event deckt den Weg ab, den PreToolUse nicht abdeckt: Ein PreToolUse-Hook, der auf das Tool Skill zutrifft, wird nur ausgelöst, wenn Claude das Tool aufruft, aber das direkte Eingeben von /skillname umgeht PreToolUse. UserPromptExpansion wird auf diesem direkten Weg ausgelöst.
Matcht auf command_name. Lassen Sie den Matcher leer, um bei jedem Befehl vom Typ Prompt auszulösen.
UserPromptExpansion-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten UserPromptExpansion-Hooks expansion_type, command_name, command_args, command_source und den ursprünglichen prompt-String. Das Feld expansion_type hat den Wert slash_command für Skills und benutzerdefinierte Befehle oder mcp_prompt für Prompts von MCP-Servern.
{
"session_id": "abc123",
"transcript_path": "/Users/.../00893aaf.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "UserPromptExpansion",
"expansion_type": "slash_command",
"command_name": "example-skill",
"command_args": "arg1 arg2",
"command_source": "plugin",
"prompt": "/example-skill arg1 arg2"
}
UserPromptExpansion-Entscheidungssteuerung
UserPromptExpansion-Hooks können die Erweiterung blockieren oder Kontext hinzufügen. Alle JSON-Ausgabefelder sind verfügbar.
| Feld | Beschreibung |
|---|---|
decision |
"block" verhindert, dass der Befehl erweitert wird. Lassen Sie es weg, damit er fortgesetzt wird |
reason |
Wird dem Benutzer angezeigt, wenn decision den Wert "block" hat |
additionalContext |
String, der zusammen mit dem erweiterten Prompt zum Kontext von Claude hinzugefügt wird. Siehe Kontext für Claude hinzufügen |
Ein Hook, der durch Beenden mit Exit-Code 2 blockiert, wird genauso behandelt wie reason: Die Blockierungsmeldung zeigt dem Benutzer den stderr-Text.
{
"decision": "block",
"reason": "This slash command is not available",
"hookSpecificOutput": {
"hookEventName": "UserPromptExpansion",
"additionalContext": "Additional context for this expansion"
}
}
MessageDisplay
Wird ausgeführt, während eine Assistentennachricht auf den Bildschirm gestreamt wird. Claude Code zeigt die Nachricht schrittweise an: Jedes Mal, wenn ein Stapel neu abgeschlossener Zeilen zur Darstellung bereit ist, läuft der Hook einmal mit diesen Zeilen, und Claude Code stellt an ihrer Stelle den Ersatztext des Hooks dar. Eine lange Nachricht erzeugt mehrere Aufrufe; eine kurze Nachricht erzeugt möglicherweise nur einen.
Verwenden Sie MessageDisplay, um:
- Markdown für eine minimalistische Anzeige zu entfernen
- den Text umzuwandeln, den eine Agent SDK-Anwendung ihren Benutzern anzeigt
- API-Schlüssel oder interne Hostnamen aus den Antworten von Claude zu schwärzen
Claude Code hält jeden Stapel zurück, bis Ihr Hook zurückkehrt, halten Sie den Hook daher schnell. Wenn der Hook fehlschlägt oder eine Zeitüberschreitung auftritt, zeigt Claude Code den ursprünglichen Text an. Der Standard-Timeout für dieses Event beträgt 10 Sekunden; wenn Ihr Hook mehr Zeit benötigt, setzen Sie das Feld timeout im Hook-Eintrag.
MessageDisplay dient nur der Anzeige: Der Ersatztext ändert nur, was auf dem Bildschirm dargestellt wird. Das Transkript und das, was Claude sieht, behalten den ursprünglichen Text. Claude sieht den Ersatz also nie, und der ausführliche Modus zeigt das Original. Der Hook erhält nur den Text von Assistentennachrichten, daher werden Tool-Ergebnisse und der von Ihnen eingegebene Text unverändert dargestellt.
MessageDisplay unterstützt keine Matcher und wird für jede Assistentennachricht ausgelöst, die Text streamt; Nachrichten ohne Text, etwa Antworten, die nur aus Tool-Aufrufen bestehen, lösen es nicht aus.
In nicht interaktiven Läufen, einschließlich Agent SDK-Abfragen und claude -p, läuft MessageDisplay einmal pro Assistentennachricht statt einmal pro Zeilenstapel. Der einzelne Aufruf erfolgt, nachdem die Nachricht abgeschlossen ist, und enthält den vollständigen Nachrichtentext: index ist 0, final ist true, und delta enthält die gesamte Nachricht. Ein Hook, der den delta-Text jeder Nachricht sammelt, erhält in beiden Modi denselben Gesamttext.
MessageDisplay-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten MessageDisplay-Hooks Kennungen für den Turn und die Nachricht, die Position dieses Aufrufs innerhalb der Nachricht und den neuen Text in delta. Die Stapelgrenzen hängen davon ab, wie der Text gestreamt wird. Verwenden Sie daher index und final, um den Fortschritt innerhalb einer Nachricht zu verfolgen, statt eine bestimmte Gruppierung der Zeilen zu erwarten.
| Feld | Beschreibung |
|---|---|
turn_id |
UUID des aktuellen Turns |
message_id |
UUID der angezeigten Assistentennachricht. Über alle Stapel derselben Nachricht hinweg stabil. Dies ist nicht die msg_…-ID der API und kann daher nicht mit Nachrichten-IDs im Transkript abgeglichen werden |
index |
Nullbasierter Index dieses Stapels innerhalb der Nachricht |
final |
true beim letzten Stapel der Nachricht. Jede Nachricht hat genau einen letzten Stapel |
delta |
Die seit dem vorherigen Stapel neu abgeschlossenen Zeilen, einschließlich abschließender Zeilenumbrüche. Immer vollständige Zeilen, außer beim letzten Stapel, der mitten in einer Zeile enden kann. In interaktiven Läufen ist das Delta des letzten Stapels leer, wenn die Nachricht mit einem Zeilenumbruch endet. Behandeln Sie daher final und nicht ein nicht leeres Delta als Signal für das Nachrichtenende. In Agent SDK- und claude -p-Läufen enthält der einzelne Aufruf die gesamte Nachricht |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "MessageDisplay",
"turn_id": "0c9e6a2f-7d41-4f4e-9a15-3f4f7c2b8d10",
"message_id": "5b2a9c8e-1f63-4d8a-b7c4-9e0d2a6f1c3b",
"index": 0,
"final": false,
"delta": "Here is the plan:\n"
}
MessageDisplay-Ausgabe
Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, können MessageDisplay-Hooks displayContent zurückgeben, um das Delta auf dem Bildschirm zu ersetzen:
| Feld | Beschreibung |
|---|---|
displayContent |
Text, der anstelle des Deltas angezeigt wird. Lassen Sie es weg, um das Original anzuzeigen |
MessageDisplay-Hooks haben keine Entscheidungssteuerung. Sie können die Nachricht nicht blockieren und nicht ändern, was im Transkript gespeichert oder an Claude gesendet wird. Claude Code verarbeitet displayContent aus ihrer JSON-Ausgabe und verwirft systemMessage und continue.
Dieses Beispiel entfernt Markdown-Formatierung aus den Antworten von Claude für eine Klartextanzeige. Das Skript liest jeden Stapel von stdin, entfernt Fettdruck-Markierungen und Backticks für Inline-Code aus delta und gibt das Ergebnis als displayContent zurück.
[tab] macOS/Linux
Registrieren Sie in Ihrer Einstellungsdatei einen Befehls-Hook für das Event:
{ "hooks": { "MessageDisplay": [ { "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.sh", "args": [] } ] } ] } }Speichern Sie dieses Skript als
.claude/hooks/plain-display.shin Ihrem Projekt und machen Sie es mitchmod +xausführbar:#!/bin/bash jq '{hookSpecificOutput: {hookEventName: "MessageDisplay", displayContent: (.delta | gsub("\\*\\*"; "") | gsub("`"; ""))}}'
[tab] Windows (PowerShell)
Registrieren Sie einen Befehls-Hook, der das Skript über PowerShell ausführt:
{ "hooks": { "MessageDisplay": [ { "hooks": [ { "type": "command", "command": "powershell.exe", "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/plain-display.ps1" ] } ] } ] } }Das Flag
-NoProfileüberspringt das Laden Ihres PowerShell-Profils, damit der Hook schnell startet, und-ExecutionPolicy Bypasserlaubt PowerShell, die lokale Skriptdatei auszuführen.Speichern Sie dieses Skript als
.claude/hooks/plain-display.ps1in Ihrem Projekt:$batch = [Console]::In.ReadToEnd() | ConvertFrom-Json $text = $batch.delta -replace '\*\*', '' -replace '`', '' @{ hookSpecificOutput = @{ hookEventName = "MessageDisplay" displayContent = $text } } | ConvertTo-Json
Stapel ohne Markdown werden unverändert durchgereicht. Wenn das Skript fehlschlägt, zum Beispiel weil jq fehlt, zeigt Claude Code den ursprünglichen Text an und vermerkt 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. Matcht auf jeden Tool-Namen außer EndConversation: integrierte Tools wie Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion und ExitPlanMode sowie beliebige MCP-Tool-Namen.
Um einen Hook auszuführen, wenn sich eine bestimmte Datei auf der Festplatte ändert, unabhängig davon, wer sie geschrieben hat, verwenden Sie FileChanged, statt dateibearbeitende Tools nach Namen zu matchen. Anders als PreToolUse führt Claude Code FileChanged-Hooks nach der Änderung aus, und sie haben keine Entscheidungssteuerung, können den Schreibvorgang also nicht blockieren.
[!WARNING]
PreToolUse wird nur ausgeführt, wenn Claude ein Tool aufruft. Dateien, die Sie mit
@in Ihrem Prompt referenzieren, werden standardmäßig ohne Tool-Aufruf hinzugefügt. Claude Code fügt ihren Inhalt beim Erstellen des Prompts ein, sodass für sie kein PreToolUse-Hook ausgelöst wird, auch keine Hooks, die aufReadmatchen. PreToolUse wird außerdem nicht fürEndConversationausgelöst.Sie können bestimmte Pfade für
@-Referenzen sperren oder das Einfügen abschalten:
- Bestimmte Pfade sperren: Fügen Sie eine
Read-deny-Regel für sie hinzu- Das Einfügen abschalten: Setzen Sie
CLAUDE_CODE_DISABLE_FILE_MENTIONS=1in Ihrer Shell, Ihren Benutzereinstellungen oder verwalteten Einstellungen. Claude erhält dann die erwähnten Pfade mit dem Hinweis, sie mit seinen Datei-Tools zu lesen. Tut es das, werden Ihre passenden PreToolUse-Hooks ausgeführt. Die Variable erfordert Claude Code v2.1.299 oder höher, und Claude Code ignoriert sie in Projekt- und lokalen Einstellungen.
Verwenden Sie die PreToolUse-Entscheidungssteuerung, um den Tool-Aufruf zu erlauben, abzulehnen, nachzufragen oder zurückzustellen.
Ein Agent-SDK-Callback-Hook bei PreToolUse, der seinen Timeout überschreitet, blockiert den Tool-Aufruf.
PreToolUse-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PreToolUse-Hooks tool_name, tool_input und tool_use_id.
Bei einem MCP-Tool enthält die Eingabe außerdem mcp_server, ein Objekt mit dem name des Servers und einer source, die angibt, woher die Definition des Servers stammt. Zu den source-Werten gehören plugin, sdk und Konfigurations-Geltungsbereiche wie user und project. McpServerProvenance in der Agent SDK-Referenz listet alle Werte auf und beschreibt, wie Sie mit einem Wert umgehen, den Sie nicht kennen. Stützen Sie Vertrauensentscheidungen auf source und nicht auf name oder das Tool-Namenspräfix mcp__<server>__. Das Feld mcp_server erfordert Claude Code v2.1.274 oder höher.
Bei den Datei-Tools Write, Edit und Read ist tool_input.file_path immer absolut:
- Claude Code erweitert
~und relative Pfade, bevor Hooks laufen, sodass ein Hook, der auf Pfade matcht, nicht über~oder eine relative Schreibweise desselben Pfads umgangen werden kann - Unter Windows kommt der Pfad mit Backslashes als Trennzeichen an, selbst wenn Ihr Hook unter Git Bash läuft, wo
$PWDwie/c/projectaussieht - Ein Vergleich mit Schrägstrichen, etwa eine Prüfung auf
/src/, trifft nie auf einen Pfad mit Backslashes zu, und der Tool-Aufruf wird fortgesetzt, als hätte der Hook nichts zu blockieren gehabt - Normalisieren Sie die Trennzeichen vor dem Vergleich:
FILE_PATH="${FILE_PATH//\\//}"in Bash oderfile_path.replace("\\", "/")in Python, und matchen Sie dann auf ein Pfadsegment wie/src/, statt mit^zu verankern, da der Pfad absolut ist
Ein Write-Aufruf unter Windows liefert:
{
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "C:\\project\\src\\index.ts",
"content": "..."
},
...
}
Die tool_input-Felder hängen vom Tool ab:
Bash
Führt Shell-Befehle aus.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
command |
string | "npm test" |
Der auszuführende Shell-Befehl |
description |
string | "Run test suite" |
Optionale Beschreibung dessen, was der Befehl tut |
timeout |
number | 120000 |
Optionaler Timeout in Millisekunden. Werte über dem Maximum werden auf das Maximum reduziert statt abgelehnt |
run_in_background |
boolean | false |
Ob der Befehl im Hintergrund ausgeführt werden soll |
Wenn ein Bash-Befehl Dateien in einem Git-Repository ändert, kann Claude Code aufzeichnen, was sich geändert hat. Es zeichnet die Änderungen in jedem Berechtigungsmodus auf, wenn die Einstellung bashEditDiffEnabled die Aufzeichnung einschaltet; der Eintrag dieser Einstellung gibt an, welche Dateien sie setzen können. Andernfalls zeichnet es sie nur im Auto-Modus und im Modus bypassPermissions auf, und nur, wenn Claude Code Claude anweist, Dateien über Bash zu bearbeiten. Setzen Sie bashEditDiffEnabled auf false, um die Aufzeichnung auszuschalten. Hintergrundbefehle und nur lesende Befehle enthalten keinen Diff.
Ihr PostToolUse-Hook erhält die geänderten Dateien dann in tool_response.bashEditDiff. Die Liste umfasst, was sich während der Ausführung des Befehls im Repository geändert hat. Dateien, die Git ignoriert, und Dateien in Submodulen werden nicht aufgeführt. Erfordert Claude Code v2.1.269 oder höher.
[!NOTE]
Die Liste wird nach bestem Bemühen erstellt und ist als öffentliche Beta verfügbar. Claude Code kann eine Änderung übersehen, eine Datei aufnehmen, die gleichzeitig ein anderer Prozess geändert hat, oder an seinen Größenlimits abbrechen. Die Struktur des Felds kann sich ändern. Verwenden Sie die Liste, um herauszufinden, was Sie prüfen sollten, nicht um eine Richtlinie durchzusetzen.
changedFiles und files listen auf, was der Befehl geändert hat; die übrigen Felder geben an, wie vollständig und wie zuverlässig diese Liste ist.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
changedFiles |
array | ["/path/to/src/app.ts"] |
Absolute Pfade der Dateien, die der Befehl geändert hat, höchstens 200. Vorhanden, wann immer files einen Diff enthält oder moreFiles größer als null ist |
files |
array | [{"filePath": "/path/to/src/app.ts", "hunks": [...]}] |
Diffs von bis zu 5 geänderten Dateien, zur Anzeige. created oder deleted ist true für eine Datei, die der Befehl hinzugefügt oder entfernt hat |
moreFiles |
number | 2 |
Anzahl der geänderten Dateien ohne Diff in files |
unavailable |
boolean | true |
Gesetzt, wenn der Diff unvollständig ist oder nicht erstellt werden konnte |
skipped |
boolean | true |
Gesetzt bei einem Git-Befehl, der den Arbeitsbaum verschiebt, etwa git checkout oder git stash, sodass Claude Code keinen Diff erstellt |
shared |
boolean | true |
Gesetzt, wenn ein anderer Bash-Tool-Aufruf, etwa der eines Subagenten, gleichzeitig im selben Repository lief, sodass einige aufgeführte Änderungen von diesem Befehl stammen können |
PowerShell
Führt PowerShell-Befehle aus. Zur Verfügbarkeit nach Plattform siehe das PowerShell-Tool.
Die Felder entsprechen denen des Bash-Tools, mit dem Befehlsstring in command:
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
command |
string | "Get-ChildItem -Recurse" |
Der auszuführende PowerShell-Befehl |
description |
string | "List files recursively" |
Optionale Beschreibung dessen, was der Befehl tut |
timeout |
number | 120000 |
Optionaler Timeout in Millisekunden |
run_in_background |
boolean | false |
Ob der Befehl im Hintergrund ausgeführt werden soll |
Matchen Sie in Hooks, die Shell-Befehle prüfen, auf Bash|PowerShell, damit sie beide Tools abdecken:
- Unter Windows behandelt Claude PowerShell überall dort, wo das PowerShell-Tool aktiviert ist, als primäre Shell und leitet Shell-Befehle darüber.
- Unter Windows ohne Git Bash wird das Tool automatisch aktiviert, und Claude Code registriert das Bash-Tool überhaupt nicht.
- Ein Hook, der nur auf
Bashmatcht, wird dort nie ausgelöst.
Write
Erstellt oder überschreibt eine Datei.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
file_path |
string | "/path/to/file.txt" |
Absoluter Pfad zur zu schreibenden Datei |
content |
string | "file content" |
In die Datei zu schreibender Inhalt |
Edit
Ersetzt einen String in einer bestehenden Datei.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
file_path |
string | "/path/to/file.txt" |
Absoluter Pfad zur zu bearbeitenden Datei |
old_string |
string | "original text" |
Zu suchender und zu ersetzender Text |
new_string |
string | "replacement text" |
Ersatztext |
replace_all |
boolean | false |
Ob alle Vorkommen ersetzt werden sollen |
Read
Liest Dateiinhalte.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
file_path |
string | "/path/to/file.txt" |
Absoluter Pfad zur zu lesenden Datei |
offset |
number | 10 |
Optionale Zeilennummer, ab der gelesen wird |
limit |
number | 50 |
Optionale Anzahl zu lesender Zeilen |
Glob
Findet Dateien, die einem Glob-Muster entsprechen.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
pattern |
string | "**/*.ts" |
Glob-Muster, gegen das Dateien abgeglichen werden |
path |
string | "/path/to/dir" |
Optionales Verzeichnis, in dem gesucht wird. Standardmäßig das aktuelle Arbeitsverzeichnis |
Grep
Durchsucht Dateiinhalte mit regulären Ausdrücken.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
pattern |
string | "TODO.*fix" |
Muster eines regulären Ausdrucks, nach dem gesucht wird |
path |
string | "/path/to/dir" |
Optionale Datei oder optionales Verzeichnis, in dem gesucht wird |
glob |
string | "*.ts" |
Optionales Glob-Muster zum Filtern von Dateien |
output_mode |
string | "content" |
"content", "files_with_matches" oder "count". Standardmäßig "files_with_matches" |
-i |
boolean | true |
Suche ohne Beachtung der Groß-/Kleinschreibung |
multiline |
boolean | false |
Mehrzeiliges Matching aktivieren |
WebFetch
Ruft Webinhalte ab und verarbeitet sie.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
url |
string | "https://example.com/api" |
URL, von der Inhalte abgerufen werden |
prompt |
string | "Extract the API endpoints" |
Prompt, der auf den abgerufenen Inhalt angewendet wird |
offset |
number | 100000 |
Optionale Anzahl von Zeichen, die vom Anfang der Seite übersprungen werden. Claude setzt dies, um eine lange Seite weiterzulesen. Erfordert Claude Code v2.1.290 oder höher |
WebSearch
Durchsucht das Web.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
query |
string | "react hooks best practices" |
Suchanfrage |
allowed_domains |
array | ["docs.example.com"] |
Optional: nur Ergebnisse von diesen Domains einbeziehen |
blocked_domains |
array | ["spam.example.com"] |
Optional: Ergebnisse von diesen Domains ausschließen |
mode |
string | "extended" |
Optional: Suchtyp. "standard" fordert eine leichtere Suche an und "extended" die vollständige Suche. Manche Sitzungen senden mode nicht, und ein Aufruf ohne dieses Feld führt die vollständige Suche aus. Erfordert Claude Code v2.1.282 oder höher |
Agent
Startet einen Subagenten.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
prompt |
string | "Find all API endpoints" |
Die Aufgabe, die der Agent ausführen soll |
description |
string | "Find API endpoints" |
Kurze Beschreibung der Aufgabe |
subagent_type |
string | "Explore" |
Typ des zu verwendenden spezialisierten Agenten |
model |
string | "sonnet" |
Optionaler Modell-Alias, der den Standard überschreibt |
Wenn ein Agent-Aufruf im Vordergrund abgeschlossen ist, erhält Ihr PostToolUse-Hook das Ergebnis des Subagenten und die Telemetrie des Laufs in tool_response. Lesen Sie diese Felder, um den Lauf zu prüfen; für Token- und Kostenzusammenfassungen über Subagenten hinweg verwenden Sie die Token- und Kostenzähler, gefiltert auf query_source "subagent", da totalTokens und usage nur die letzte Anfrage abdecken:
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
status |
string | "completed" |
"completed" für Subagenten im Vordergrund, "async_launched" für Subagenten im Hintergrund. Welche Subagenten auf welche Weise laufen, erfahren Sie unter Subagenten im Vordergrund oder Hintergrund ausführen |
agentId |
string | "a4d2c8f1e0b3a297" |
Kennung des Subagenten-Laufs |
content |
array | [{"type": "text", "text": "Found 12 endpoints..."}] |
Die abschließenden Textblöcke des Subagenten oder, bei einem Subagenten, dessen Bericht über SubagentHandback läuft, an ihrer Stelle ein kurzer Hinweis auf diese Übergabe |
resolvedModel |
string | "claude-sonnet-4-5" |
Modell, mit dem der Subagent gestartet ist und das vom angeforderten Modell abweichen kann |
modelsUsed |
array | ["claude-sonnet-4-5", "claude-haiku-4-5"] |
Verwendete Modelle in Reihenfolge, aufeinanderfolgende Wiederholungen zusammengefasst; nur gesetzt, wenn das Modell während des Laufs gewechselt wurde |
totalTokens |
number | 12450 |
Token-Anzahl aus der letzten API-Anfrage des Subagenten: Eingabe-, Ausgabe- und Cache-Token zusammen. Dies ist keine Gesamtzahl über den ganzen Lauf |
totalDurationMs |
number | 48211 |
Echtzeitdauer des Subagenten-Laufs |
totalToolUseCount |
number | 7 |
Anzahl der Tool-Aufrufe, die der Subagent ausgeführt hat |
usage |
object | {"input_tokens": 8320, ...} |
Aufschlüsselung der Token nach Typ für die letzte API-Anfrage: input_tokens, output_tokens, cache_creation_input_tokens, cache_read_input_tokens |
Ab Claude Code v2.1.271 übermittelt ein Subagent, der mit dem Tool SubagentHandback läuft, das Claude Code im Auto-Modus bereitstellt, seinen Bericht über dieses Tool, statt ihn als Text zurückzugeben. Das Feld content seines completed-Ergebnisses enthält dann einen kurzen Hinweis auf diese Übergabe statt des Berichts selbst. Um den Bericht zu lesen, matchen Sie einen PreToolUse- oder PostToolUse-Hook auf SubagentHandback und lesen tool_input.message.
Bei Subagenten im Hintergrund kehrt das Tool zurück, wenn die Aufgabe in den Hintergrund wechselt, daher enthält tool_response keine Nutzungsfelder: Ein Start im Hintergrund kehrt sofort zurück, und eine Vordergrundaufgabe, die Claude Code während des Laufs in den Hintergrund verschiebt, kehrt bei diesem Übergang zurück. Die Antwort enthält status: "async_launched", agentId, description, prompt, outputFile und resolvedModel.
Bei einer completed-Antwort nennt resolvedModel das Modell, mit dem der Subagent gestartet ist; es kann vom model-Wert in tool_input abweichen, etwa wenn availableModels oder eine andere Überschreibung greift. Bei einer async_launched-Antwort nennt resolvedModel das Modell, das verwendet wurde, als der Agent in den Hintergrund wechselte, sodass ein Wechsel vor dem Verschieben in den Hintergrund dort berücksichtigt ist.
AskUserQuestion
Stellt dem Benutzer eine bis vier Multiple-Choice-Fragen.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
questions |
array | [{"question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false}] |
Zu stellende Fragen, jeweils mit einem question-String, einem kurzen header, einem options-Array und einem optionalen multiSelect-Flag |
answers |
object | {"Which framework?": "React"} |
Optional. Ordnet Fragetexte der Bezeichnung der gewählten Option zu. Antworten bei Mehrfachauswahl verbinden die Bezeichnungen mit Kommas. Claude setzt dieses Feld nicht; liefern Sie es über updatedInput, um programmatisch zu antworten |
ExitPlanMode
Präsentiert einen Plan und bittet den Benutzer um Genehmigung, bevor Claude den Plan-Modus verlässt. Claude schreibt den Plan vor dem Aufruf des Tools in eine Datei auf der Festplatte, daher ist das wörtliche tool_input vom Modell typischerweise leer. Claude Code fügt den Planinhalt und den Dateipfad ein, bevor die Eingabe an Hooks übergeben wird.
| Feld | Typ | Beispiel | Beschreibung |
|---|---|---|---|
plan |
string | "## Refactor auth\n1. Extract..." |
Planinhalt in Markdown. Aus der Plandatei auf der Festplatte eingefügt |
planFilePath |
string | "/Users/.../plans/refactor-auth.md" |
Pfad zur Plandatei. Eingefügt |
allowedPrompts |
array | [{"tool": "Bash", "prompt": "run tests"}] |
Veraltet. Claude Code akzeptiert das Feld, ignoriert es aber. Vor v2.1.205 enthielt es Prompt-basierte Berechtigungen, die Claude zur Umsetzung des Plans angefordert hat |
In PostToolUse ist tool_response ein Objekt mit den Feldern plan und filePath, die den genehmigten Plan enthalten, sowie internen Status-Flags. Lesen Sie tool_response.plan für den Planinhalt, statt die Datei erneut von der Festplatte zu lesen.
PreToolUse-Entscheidungssteuerung
PreToolUse-Hooks können steuern, ob ein Tool-Aufruf fortgesetzt wird. Anders als andere Hooks, die ein Feld decision auf oberster Ebene verwenden, gibt PreToolUse seine Entscheidung innerhalb eines Objekts hookSpecificOutput zurück. Das ermöglicht eine umfassendere Steuerung: vier Ergebnisse (allow, deny, ask oder defer) sowie die Möglichkeit, die Tool-Eingabe vor der Ausführung zu ändern. Wie Sie einen Monitor-Befehl erlauben oder umschreiben, erfahren Sie unter Monitor-Befehle matchen.
| Feld | Beschreibung |
|---|---|
permissionDecision |
"allow" überspringt die Berechtigungsabfrage, außer bei Aktionen, die kein Modus automatisch genehmigt, und bei Lesezugriffen auf Netzwerkpfade. Auch die Genehmigung für Aufrufe von Tools, die eine Agent SDK-App als always_ask kennzeichnet, wird nicht übersprungen, es sei denn, der Berechtigungsmodus genehmigt den Aufruf. AskUserQuestion und ExitPlanMode benötigen updatedInput zusammen mit "allow". Für den Plan-Modus siehe Hook-Genehmigungen im Plan-Modus. "deny" verhindert den Tool-Aufruf. "ask" fordert den Benutzer zur Bestätigung auf. "defer" beendet den Turn, sodass das Tool später fortgesetzt werden kann, und erfordert den nicht interaktiven Modus mit dem Flag -p. deny- und ask-Regeln werden unabhängig davon ausgewertet, was der Hook zurückgibt |
permissionDecisionReason |
Bei "ask" wird es dem Benutzer in der Berechtigungsabfrage angezeigt. Wenn Claude Code in einem -p-Lauf, in dem niemand die Abfrage beantworten kann, den Aufruf ablehnt, liest Claude den Grund stattdessen im Tool-Ergebnis. Bei "deny" wird es Claude angezeigt. Bei "allow" und "defer" wird es nur in das Debug-Log geschrieben |
updatedInput |
Ändert die Eingabeparameter des Tools vor der Ausführung. Ersetzt das gesamte Eingabeobjekt, nehmen Sie daher unveränderte Felder zusammen mit geänderten auf. Claude Code wertet Berechtigungsregeln und die Eignung eines Bash-Befehls für die automatische Verschiebung in den Hintergrund anhand der Eingabe aus, die Ihr Hook zurückgibt, nicht anhand der Eingabe, die Claude gesendet hat. Kombinieren Sie es mit "allow" zur automatischen Genehmigung oder mit "ask", um dem Benutzer die geänderte Eingabe anzuzeigen. Bei "defer" wird es ignoriert. Ohne permissionDecision kann der Auto-Modus den geänderten Aufruf ablehnen |
additionalContext |
String, der zusammen mit dem Tool-Ergebnis zum Kontext von Claude hinzugefügt wird. Wird ignoriert, wenn permissionDecision den Wert "defer" hat. Siehe Kontext für Claude hinzufügen |
Wenn mehrere PreToolUse-Hooks unterschiedliche Entscheidungen zurückgeben, gilt die Rangfolge deny > defer > ask > allow.
Ein Hook, der durch Beenden mit Exit-Code 2 blockiert, wird genauso behandelt wie "deny": Claude sieht die stderr-Meldung als Ablehnungsgrund.
Wenn ein Hook "ask" zurückgibt, enthält die dem Benutzer angezeigte Berechtigungsabfrage eine Bezeichnung, die angibt, woher der Hook stammt: [settings] für einen Hook aus einer beliebigen Einstellungsdatei oder aus Agenten-Frontmatter, [plugin:<name>] für den Hook eines Plugins oder [skill] für einen Hook aus Skill-Frontmatter. So können Benutzer nachvollziehen, welche Konfigurationsquelle die Bestätigung anfordert.
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "My reason here",
"updatedInput": {
"field_to_modify": "new value"
},
"additionalContext": "Current environment: production. Proceed with caution."
}
}
[!NOTE]
PreToolUse verwendete früher die Felder
decisionundreasonauf oberster Ebene, diese sind für dieses Event jedoch veraltet. Verwenden Sie stattdessenhookSpecificOutput.permissionDecisionundhookSpecificOutput.permissionDecisionReason. Die veralteten Werte"approve"und"block"entsprechen"allow"bzw."deny". Andere Events wie PostToolUse und Stop verwenden weiterhindecisionundreasonauf oberster Ebene als aktuelles Format.
PreToolUse-updatedInput im Auto-Modus
Wenn Ihr Hook im Auto-Modus updatedInput ohne permissionDecision zurückgibt, kann Claude Code den geänderten Aufruf ablehnen. Der Server hat die Eingabe geprüft, die Claude geschrieben hat, nicht die Eingabe, die Ihr Hook zurückgegeben hat. In interaktiven Terminal-Sitzungen und VS-Code-Sitzungen mit direkter Verbindung zur Anthropic API enthält die Ablehnung diesen Text:
a hook changed this call's input after auto mode reviewed it
Nach einer Ablehnung mit diesem Text kann Claude den geänderten Aufruf erneut absetzen, damit der Server ihn prüft. Wenn Claude die geänderte Eingabe in seiner nächsten Antwort exakt wiederholt, läuft Ihr Hook trotzdem:
- Er kann den Aufruf ablehnen oder an die Berechtigungsabfrage weiterleiten
- Jedes
updatedInput, das er zurückgibt, wird verworfen, sodass das ausgeführt wird, was der Server geprüft hat
Sie müssen Ihren Hook wegen dieser Ablehnung nicht ändern, es sei denn, Claude teilt Ihnen in seiner Antwort mit, dass der Auto-Modus den Aufruf nicht prüfen konnte.
In den Sitzungen, die diesen Text anzeigen, kann ein command-Hook in den Einstellungen oder in der Hooks-Datei eines Plugins Bash-, PowerShell-, Monitor-, Edit-, Write- und NotebookEdit-Aufrufe ohne diese Ablehnung ändern. Die Ablehnung kann dennoch auftreten, etwa wenn ein Hook einen früheren Aufruf in derselben Antwort von Claude geändert hat oder ein Bash-Befehl verlangt, außerhalb der Sandbox ausgeführt zu werden.
PreToolUse-ask-Entscheidungen im Auto-Modus
Im Auto-Modus leitet ein "ask" eines PreToolUse-Hooks den Tool-Aufruf normalerweise an die Berechtigungsabfrage weiter, und der Klassifikator entscheidet nicht darüber. Manche Aufrufe nehmen einen anderen Weg:
- Aufrufe, die auch von den eigenen Prüfungen von Claude Code gekennzeichnet werden: Dazu gehören Schreibvorgänge in geschützte Pfade und Befehle, die anfordern, außerhalb der Sandbox ausgeführt zu werden. Der Klassifikator kann einen dieser Aufrufe verweigern, bevor eine Abfrage erscheint. Genehmigt er ihn, geht der Aufruf normalerweise dennoch an die Abfrage.
SubagentHandback-Aufrufe: Dieses Tool liefert den Bericht eines Subagenten. Dafür erscheint keine Abfrage, und der Aufruf wird ausgeführt, selbst wenn der Klassifikator Einwände hat.
Um einen SubagentHandback-Aufruf zu stoppen, geben Sie "deny" zurück. Claude Code teilt dem Subagenten dann bis zu dreimal mit, dass sein Bericht nicht übermittelt wurde und er das Tool erneut aufrufen soll. Wenn Ihr Hook weiterhin verweigert, endet der Subagent, ohne über dieses Tool einen Bericht zu liefern, und die Konversation, die darauf wartet, erhält einen entsprechenden Hinweis.
Tools, die eine Benutzerinteraktion erfordern
AskUserQuestion und ExitPlanMode erfordern eine Benutzerinteraktion. Im nicht interaktiven Modus mit dem Flag -p bietet Claude Code sie nur an, wenn der Lauf einen Berechtigungs-Host hat, der die Abfrage entgegennimmt, etwa einen canUseTool-Callback des Agent SDK.
Ein PreToolUse-Hook erfüllt diese Anforderung, wenn er Folgendes tut:
- Er liest die Eingabe des Tools von stdin
- Er sammelt die Antwort über Ihre eigene Benutzeroberfläche
- Er gibt
permissionDecision: "allow"zusammen mit einemupdatedInputzurück, das die Antwort enthält, sodass das Tool ohne Abfrage ausgeführt wird
Die alleinige Rückgabe von "allow" reicht für diese Tools nicht aus.
Geben Sie bei AskUserQuestion das ursprüngliche questions-Array zurück und fügen Sie ein answers-Objekt hinzu, das den Text jeder Frage der gewählten Antwort zuordnet. Diese Ausgabe beantwortet eine Frage mit React:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": {
"questions": [
{
"question": "Which framework?",
"header": "Framework",
"options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}],
"multiSelect": false
}
],
"answers": {"Which framework?": "React"}
}
}
}
Ein MCP-Tool, das sein Server mit _meta["anthropic/requiresUserInteraction"] kennzeichnet, ist strenger: Ein Hook kann dessen Genehmigungsabfrage nicht mit "allow" überspringen, weder mit noch ohne updatedInput, da Claude Code nicht bestätigen kann, dass der Hook die vom Tool benötigte Interaktion eingeholt hat.
Einen Tool-Aufruf zurückstellen
"defer" ist für Integrationen gedacht, die claude -p als Unterprozess ausführen und dessen JSON-Ausgabe lesen, etwa eine Agent-SDK-App oder eine eigene Benutzeroberfläche, die auf Claude Code aufsetzt. Damit kann der aufrufende Prozess Claude bei einem Tool-Aufruf anhalten, Eingaben über seine eigene Oberfläche erfassen und an der Stelle fortfahren, an der er aufgehört hat.
Claude Code berücksichtigt "defer" nur im nicht interaktiven Modus mit dem Flag -p und nur dann, wenn Claude im Turn einen einzigen Tool-Aufruf ausführt:
- In interaktiven Sitzungen: Claude Code protokolliert eine Warnung und ignoriert das Hook-Ergebnis
- Mehrere Tool-Aufrufe gleichzeitig:
"defer"wird mit einer Warnung ignoriert, und das Tool durchläuft den normalen Berechtigungsablauf
Das Tool AskUserQuestion ist der typische Fall: Claude möchte dem Benutzer eine Frage stellen, aber es gibt kein Terminal, in dem geantwortet werden könnte. Ein -p-Lauf bietet AskUserQuestion nur an, wenn er einen Berechtigungs-Host hat, etwa ein MCP-Tool, das Sie mit --permission-prompt-tool übergeben. Starten Sie den Lauf daher mit einem solchen. Der Ablauf funktioniert so:
- Claude ruft
AskUserQuestionauf. DerPreToolUse-Hook wird ausgelöst. - Der Hook gibt
permissionDecision: "defer"zurück. Das Tool wird nicht ausgeführt. Der Turn endet mit einem Ergebnis, dasstop_reason: "tool_deferred"enthält, und der ausstehende Tool-Aufruf bleibt im Transkript. - 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>mit demselben Berechtigungs-Host aus. Derselbe Tool-Aufruf löstPreToolUseerneut aus. - Der Hook gibt
permissionDecision: "allow"mit der Antwort inupdatedInputzurück. Das Tool wird ausgeführt, und Claude fährt fort.
Das Feld deferred_tool_use enthält id, name und input des Tools:
{
"type": "result",
"subtype": "success",
"stop_reason": "tool_deferred",
"session_id": "abc123",
"deferred_tool_use": {
"id": "toolu_01abc",
"name": "AskUserQuestion",
"input": { "questions": [{ "question": "Which framework?", "header": "Framework", "options": [{"label": "React", "description": "Component library"}, {"label": "Vue", "description": "Progressive framework"}], "multiSelect": false }] }
}
}
Es gibt keinen Timeout und keine Begrenzung für Wiederholungsversuche. Die Sitzung bleibt auf der Festplatte, bis die Aufbewahrungsbereinigung gemäß cleanupPeriodDays sie löscht, standardmäßig nach 30 Tagen.
Wenn die Antwort beim Fortsetzen noch nicht bereitsteht, kann der Hook erneut "defer" zurückgeben. Wenn Sie beim Fortsetzen einen Prompt mitgesendet haben, beantwortet Claude ihn ohne das Ergebnis des Tools und führt den Aufruf nicht aus. Setzen Sie ohne Prompt fort, bis der Hook "allow" oder "deny" zurückgibt.
[!NOTE]
Um eine zurückgestellte Sitzung im Plan-Modus fortzusetzen, übergeben Sie
--permission-prompt-toolzusammen mit--resume, damit Claude Code den Plan zur Genehmigung vorlegen kann. Die weiteren Bedingungen finden Sie unter Im Plan-Modus mit-pfortsetzen. Erfordert Claude Code v2.1.246 oder höher.Wenn Sie mit
-pfortsetzen, stellt Claude Code keinen anderen gespeicherten Berechtigungsmodus wieder her. Der Lauf startet in dem Berechtigungsmodus, in dem ein neuerclaude -p-Lauf starten würde. Übergeben Sie daher--permission-modeoder--dangerously-skip-permissionserneut, wenn die zurückgestellte Sitzung eine dieser Optionen verwendet hat.
Fortsetzen, wenn ein zurückgestelltes Tool nicht verfügbar ist
Ein Ergebnis mit stop_reason: "tool_deferred_unavailable" bedeutet, dass das Tool eines zurückgestellten Aufrufs in der fortgesetzten Sitzung nicht verfügbar ist, zum Beispiel weil sein MCP-Server nicht verbunden ist. Ihr PreToolUse-Hook wird nicht ausgelöst, und das Feld deferred_tool_use des Ergebnisses nennt das Tool.
Solange der Aufruf aussteht, erreicht kein von Ihnen gesendeter Prompt Claude, und der Lauf endet mit Exit-Code 1. Das Feld result beginnt mit <tool> isn't available, so its deferred call didn't run. Führen Sie den Aufruf aus, oder verwerfen Sie ihn, damit er nicht ausgeführt wird:
- Um den Aufruf auszuführen: Machen Sie das Tool verfügbar und setzen Sie erneut fort.
- Um den Aufruf zu verwerfen: Setzen Sie mit einem Prompt fort und setzen Sie
CLAUDE_CODE_DROP_UNAVAILABLE_DEFERRED_TOOLauf1. Claude erhält einen Fehler als Ergebnis des Aufrufs und beantwortet Ihren Prompt. Wenn Sie später fortsetzen, steht kein Aufruf mehr aus.
Wenn Sie den Aufruf verwerfen, schreibt der Lauf das Ergebnis tool_deferred_unavailable mit einem leeren Feld result und danach Claudes Antwort in einem zweiten Ergebnis. Er endet mit Exit-Code 0, sofern diese Antwort kein Fehler ist. Wenn Sie die Variable setzen und keinen Prompt senden, verwirft der Lauf den Aufruf trotzdem und endet mit Exit-Code 1.
Vor v2.1.319 beantwortete Claude Code einen Prompt ohne das Ergebnis des Tools und verwarf den Aufruf. Senden Sie bei diesen Versionen einen neuen Prompt, sobald das Tool verfügbar ist.
PermissionRequest
Wird ausgeführt, wenn Claude Code Sie gleich um Erlaubnis zur Verwendung eines Tools bitten wird. In Sitzungen, die keine Abfrage anzeigen können, etwa Subagenten im Hintergrund im nicht interaktiven Modus, führt Claude Code diese Hooks dennoch aus, und wenn kein Hook eine Entscheidung zurückgibt, lehnt es den Tool-Aufruf ab. Bei einem Aufruf, der ein --permission-prompt-tool oder den canUseTool-Callback des Agent SDK erreicht, laufen die Hooks parallel zu Ihrem Host, und die Entscheidung, die zuerst getroffen wird, gilt.
Verwenden Sie die PermissionRequest-Entscheidungssteuerung, um im Namen des Benutzers zu erlauben oder abzulehnen.
Verwenden Sie dieses Event, wenn Sie in dem Moment ein Signal benötigen, in dem Claude um Erlaubnis zur Verwendung eines Tools bittet. Claude Code führt einen Notification-Hook mit dem Typ permission_prompt erst aus, nachdem die Abfrage etwa sechs Sekunden gewartet hat.
Claude Code führt keine PermissionRequest-Hooks für die Netzwerkanfrage eines in einer Sandbox ausgeführten Befehls aus. Um für diese Abfrage ein Signal zu erhalten, verwenden Sie den Benachrichtigungstyp permission_prompt.
Matcht auf den Tool-Namen, mit denselben Werten wie PreToolUse.
PermissionRequest-Eingabe
PermissionRequest-Hooks erhalten wie PreToolUse-Hooks die Felder tool_name und tool_input, jedoch ohne tool_use_id. Bei einem MCP-Tool erhalten sie außerdem das Objekt mcp_server. Ein optionales Array permission_suggestions enthält die Berechtigungsaktualisierungen, die Claude Code für diese Anfrage vorschlägt, etwa das Hinzufügen einer allow-Regel oder das Ändern des Berechtigungsmodus.
Das Array permission_suggestions ist keine exakte Liste der Optionen, die Sie sehen, da jeder Berechtigungsdialog seine eigenen Optionen erstellt. Manche Dialoge, etwa der für Dateibearbeitungen, lesen das Array gar nicht und leiten ihre Optionen aus der Anfrage selbst ab. Ein Dialog, der es liest, kann dennoch eine Option zurückhalten, deren Vorschlag im Array verbleibt, beispielsweise wenn allowManagedPermissionRulesOnly Optionen zum Speichern von Regeln ausblendet. Er kann auch Optionen anbieten, für die es keinen Vorschlagseintrag gibt, etwa Yes, and switch to auto mode, das den Berechtigungsmodus direkt ändert statt über eine Berechtigungsaktualisierung.
PreToolUse-Hooks laufen vor jedem Tool-Aufruf, unabhängig davon, ob er eine Berechtigung benötigt. PermissionRequest-Hooks laufen nur, wenn Claude Code Sie gleich um eine Berechtigung bitten würde oder andernfalls einen Aufruf, der keine Abfrage anzeigen kann, automatisch ablehnen würde. Keines der beiden Ereignisse wird für EndConversation ausgelöst.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PermissionRequest",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf node_modules",
"description": "Remove node_modules directory"
},
"permission_suggestions": [
{
"type": "addRules",
"rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
"behavior": "allow",
"destination": "localSettings"
}
]
}
PermissionRequest-Entscheidungssteuerung
PermissionRequest-Hooks können Berechtigungsanfragen erlauben oder ablehnen. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, kann Ihr Hook-Skript ein decision-Objekt mit diesen ereignisspezifischen Feldern zurückgeben:
| Feld | Beschreibung |
|---|---|
behavior |
"allow" erteilt die Berechtigung, "deny" lehnt sie ab. deny- und ask-Regeln werden weiterhin ausgewertet, sodass ein Hook, der "allow" zurückgibt, eine passende deny-Regel nicht überschreibt |
updatedInput |
Nur für "allow": ändert die Eingabeparameter des Tools vor der Ausführung. Ersetzt das gesamte Eingabeobjekt, also nehmen Sie unveränderte Felder zusammen mit den geänderten auf. Die geänderte Eingabe wird erneut gegen deny- und ask-Regeln ausgewertet. AskUserQuestion und ExitPlanMode benötigen dieses Feld |
updatedPermissions |
Nur für "allow": Array von anzuwendenden Einträgen zur Berechtigungsaktualisierung, etwa zum Hinzufügen einer allow-Regel oder zum Ändern des Berechtigungsmodus der Sitzung |
message |
Nur für "deny": Teilt Claude mit, warum die Berechtigung abgelehnt wurde |
interrupt |
Nur für "deny": Bei true wird Claude gestoppt |
Informationen zum Erlauben oder Umschreiben eines Monitor-Befehls finden Sie unter Monitor-Befehle abgleichen.
Ein Hook, der ohne decision-Objekt mit Exit-Code 2 beendet wird, lässt den Berechtigungsablauf unverändert, und seine stderr-Ausgabe wird verworfen. Um die Anfrage zu erlauben oder abzulehnen, geben Sie das decision-Objekt zurück.
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": {
"command": "npm run lint"
}
}
}
}
Einträge zur Berechtigungsaktualisierung
Das Ausgabefeld updatedPermissions und das Eingabefeld permission_suggestions verwenden beide dasselbe Array von Eintragsobjekten. Jeder Eintrag hat einen type, der seine übrigen Felder bestimmt, und eine destination, die festlegt, wohin die Änderung geschrieben wird.
type |
Felder | Wirkung |
|---|---|---|
addRules |
rules, behavior, destination |
Fügt Berechtigungsregeln hinzu. rules ist ein Array von {toolName, ruleContent?}-Objekten. Lassen Sie ruleContent weg, um das gesamte Tool abzudecken. behavior ist "allow", "deny" oder "ask" |
replaceRules |
rules, behavior, destination |
Ersetzt alle Regeln des angegebenen behavior an der destination durch die angegebenen rules |
removeRules |
rules, behavior, destination |
Entfernt passende Regeln des angegebenen behavior |
setMode |
mode, destination |
Ändert den Berechtigungsmodus. Gültige Modi sind default, auto, acceptEdits, dontAsk, bypassPermissions, plan sowie manual als Alias für default |
addDirectories |
directories, destination |
Fügt Arbeitsverzeichnisse hinzu. directories ist ein Array von Pfad-Strings |
removeDirectories |
directories, destination |
Entfernt Arbeitsverzeichnisse |
[!NOTE]
setModemitbypassPermissionswirkt nur, wenn Sie die Sitzung mit bereits verfügbarem Bypass-Modus gestartet haben:--dangerously-skip-permissions,--permission-mode bypassPermissions,--allow-dangerously-skip-permissionsoderpermissions.defaultMode: "bypassPermissions"in Benutzereinstellungen,--settingsoder verwalteten Einstellungen. Andernfalls hat die Aktualisierung keine Wirkung. Die Aktualisierung hat ebenfalls keine Wirkung, wennpermissions.disableBypassPermissionsModeden Modus deaktiviert oder die Sitzung im eingeschränkten Modus startet.
bypassPermissionswird unabhängig vondestinationnie alsdefaultModegespeichert.
Das Feld destination jedes Eintrags bestimmt, ob die Änderung im Arbeitsspeicher bleibt oder in einer Einstellungsdatei gespeichert wird.
destination |
Schreibt nach |
|---|---|
session |
nur im Arbeitsspeicher, wird beim Ende der Sitzung verworfen |
localSettings |
.claude/settings.local.json |
projectSettings |
.claude/settings.json |
userSettings |
~/.claude/settings.json |
Ein Hook kann einen der erhaltenen permission_suggestions als eigene updatedPermissions-Ausgabe zurückgeben.
PostToolUse
Läuft unmittelbar, nachdem ein Tool erfolgreich abgeschlossen wurde.
Matcht auf den Tool-Namen, mit denselben Werten wie PreToolUse.
Matchen Sie breiter, wenn der Tool-Name nicht der richtige Filter ist:
- Um einen Hook nach jedem erfolgreich abgeschlossenen Tool auszuführen, lassen Sie den
matcherweg oder setzen Sie ihn auf"*". Ihr Hook kann dann selbst ermitteln, was sich geändert hat, etwa durch Ausführen vongit status --porcelain, das auch nicht verfolgte Dateien auflistet, diegit diffübersieht. Für fehlgeschlagene Tool-Aufrufe fügen Sie denselben Hook unter PostToolUseFailure hinzu. - Um einen Hook auszuführen, wenn sich eine bestimmte Datei auf der Festplatte ändert, unabhängig davon, wer sie geschrieben hat, verwenden Sie FileChanged. Claude Code führt keinen
PostToolUse-Hook mit dem MatcherEdit|Writeaus, wenn einBash-Befehl oder ein Prozess außerhalb von Claude Code dieselbe Datei neu schreibt.
PostToolUse nach einer Unterbrechung
Wenn ein PostToolUse-Hook vorzeitig endet, nachdem Sie während eines Tool-Aufrufs eine Nachricht gesendet oder unterbrochen haben, prüfen Sie, ob Sie Ctrl+Enter, Enter oder Esc gedrückt haben:
Ctrl+Enter: Wenn das Drücken den Turn beendet, während ein Tool läuft, und das Tool abgeschlossen wird, laufen IhrePostToolUse-Hooks auf dessen Ergebnis und haben 10 Sekunden Zeit, um fertig zu werden. Die Taste wirkt wieEsc, wenn Sie vor Ihren Nachrichten einen!-Befehl in die Warteschlange gestellt haben oder Hintergrundaufgaben deaktiviert sind, zum Beispiel mitCLAUDE_CODE_DISABLE_BACKGROUND_TASKS.Enter: Eine Nachricht, die Sie mitEnterin die Warteschlange stellen, unterbricht den Turn nicht, sodass die Hooks ihren üblichentimeoutbehalten.Esc: Sie brechen die Hooks sofort ab. Senden Sie die Nachricht daher mitEnteroderCtrl+Enter, wenn die Hooks laufen müssen.
Wenn die 10 Sekunden abgelaufen sind, beendet Claude Code einen noch laufenden Befehls-Hook, unabhängig von seinem timeout. Damit ein länger laufender Befehls-Hook fertig werden kann, führen Sie ihn mit async: true aus. Das bedeutet, dass der Hook das Ergebnis weder blockieren noch umschreiben kann.
Vor v2.1.319 starteten die Hooks nicht, nachdem Sie den Turn mit einer Nachricht unterbrochen hatten, und ein laufender Hook wurde angehalten.
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 zurückgegebene Ergebnis. Das genaue Schema beider hängt vom Tool ab. Pfade in tool_input von Datei-Tools kommen im selben Format an wie bei PreToolUse: immer absolut, mit den nativen Trennzeichen der Plattform, unter Windows also mit Backslashes. Bei einem MCP-Tool enthält die Eingabe außerdem das Objekt mcp_server.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/path/to/file.txt",
"content": "file content"
},
"tool_response": {
"filePath": "/path/to/file.txt",
"type": "create"
},
"tool_use_id": "toolu_01ABC123...",
"duration_ms": 12
}
| Feld | Beschreibung |
|---|---|
duration_ms |
Optional. Ausführungszeit des Tools in Millisekunden. Ohne die Zeit in Berechtigungsabfragen und PreToolUse-Hooks |
PostToolUse-Entscheidungssteuerung
PostToolUse-Hooks können Claude nach der Tool-Ausführung Feedback geben. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
| Feld | Beschreibung |
|---|---|
decision |
"block" fügt den reason neben dem Tool-Ergebnis hinzu. Claude sieht weiterhin die ursprüngliche Ausgabe; um sie zu ersetzen, verwenden Sie updatedToolOutput |
reason |
Erklärung, die Claude angezeigt wird, wenn decision den Wert "block" hat |
additionalContext |
String, der Claudes Kontext zusammen mit dem Tool-Ergebnis hinzugefügt wird. Siehe Kontext für Claude hinzufügen |
classifierContext |
Kurzer Hinweis zum Ergebnis dieses Aufrufs für den Klassifikator des Auto-Modus statt für Claude. Siehe Ein Ergebnis für den Klassifikator des Auto-Modus annotieren. Erfordert Claude Code v2.1.236 oder höher |
updatedToolOutput |
Ersetzt die Ausgabe des Tools durch den angegebenen Wert, bevor sie an Claude gesendet wird. Der Wert muss der Ausgabestruktur des Tools entsprechen |
updatedMCPToolOutput |
Ersetzt die Ausgabe nur für MCP-Tools. Verwenden Sie bevorzugt updatedToolOutput, das für alle Tools funktioniert |
Das folgende Beispiel ersetzt die Ausgabe eines Bash-Aufrufs. Der Ersatzwert entspricht der Ausgabestruktur des Bash-Tools:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"additionalContext": "Additional information for Claude",
"updatedToolOutput": {
"stdout": "[redacted]",
"stderr": "",
"interrupted": false,
"isImage": false
}
}
}
[!WARNING]
updatedToolOutputändert nur, was Claude sieht. Das Tool wurde bereits ausgeführt, wenn der Hook ausgelöst wird, sodass geschriebene Dateien, ausgeführte Befehle oder gesendete Netzwerkanfragen bereits wirksam sind. Telemetrie wie OpenTelemetry-Tool-Spans und Analyseereignisse erfasst ebenfalls die ursprüngliche Ausgabe, bevor der Hook läuft. Um einen Tool-Aufruf vor seiner Ausführung zu verhindern oder zu ändern, verwenden Sie stattdessen einen PreToolUse-Hook.Der Ersatzwert muss der Ausgabestruktur des Tools entsprechen. Integrierte Tools geben strukturierte Objekte statt einfacher Strings zurück. Beispielsweise gibt
Bashein Objekt mit den Feldernstdout,stderr,interruptedundisImagezurück. Bei integrierten Tools wird ein Wert, der nicht dem Ausgabeschema des Tools entspricht, ignoriert und die ursprüngliche Ausgabe verwendet. Die Ausgabe von MCP-Tools wird ohne Schemavalidierung durchgereicht. Das Entfernen von Fehlerdetails, die Claude benötigt, kann dazu führen, dass Claude auf einer falschen Annahme weiterarbeitet.
Ein Ergebnis für den Klassifikator des Auto-Modus annotieren
Geben Sie classifierContext zurück, um einen kurzen Hinweis zum Ergebnis des Tool-Aufrufs an den Klassifikator des Auto-Modus statt an Claude zu senden. Der Klassifikator erhält die Tool-Ergebnisse selbst nie, daher ist dieses Feld der unterstützte Weg, ihm etwas darüber mitzuteilen, was ein Aufruf zurückgegeben hat, bevor er spätere Aktionen prüft. Das Feld erfordert Claude Code v2.1.236 oder höher.
Das folgende Beispiel teilt dem Klassifikator mit, woher die Ausgabe einer Abfrage stammt:
{
"hookSpecificOutput": {
"hookEventName": "PostToolUse",
"classifierContext": "This query ran against the staging database, not production."
}
}
Wie viel Gewicht der Klassifikator dem Hinweis beimisst, hängt davon ab, wo Sie den Hook konfiguriert haben:
- In Claude Code konfigurierte Hooks: Bei Hooks aus Einstellungsdateien, Plugins, Skills und Agenten-Frontmatter behandelt der Klassifikator den Hinweis als nicht verifizierten, von der Anwendung bereitgestellten Kontext. Der Hinweis begründet nie eine Benutzerabsicht, und wenn er behauptet, Sie hätten etwas genehmigt oder angefordert, prüft der Klassifikator diese Behauptung anhand Ihrer eigenen Nachrichten in der Konversation
- In-Process-Callbacks des Agent SDK: Wenn eine Anwendung, die Claude Code einbettet, den Hook als TypeScript-SDK-Callback registriert und den Hinweis während der laufenden Sitzung zurückgibt, kann der Klassifikator eine im Hinweis übermittelte Benutzeraussage als Benutzerabsicht werten. Eine solche Aussage kann eine Zustimmungsanforderung erfüllen, die der Klassifikator auch von einer Nachricht von Ihnen akzeptieren würde, hebt aber nie eine Blockierung auf, die auch Ihre eigene Nachricht nicht aufheben könnte. Nach dem Fortsetzen einer Sitzung behandelt Claude Code wiederhergestellte Hinweise als nicht verifizierten Kontext. Wenn Hooks aus beiden Gruppen denselben Aufruf annotieren, behandelt der Klassifikator den kombinierten Hinweis als nicht verifiziert
Claude Code wendet bei der Zustellung des Hinweises diese Grenzen an:
- Länge: Claude Code begrenzt die Hinweise für einen Tool-Aufruf auf 2.000 Zeichen und kürzt den Rest. Die Grenze gilt gemeinsam für alle Hooks, die auf diesen Aufruf antworten
- Nur synchrone Antworten: Claude Code ignoriert das Feld in der Antwort eines Hooks, der im Hintergrund läuft, da diese Antwort eintrifft, nachdem Claude Code das Tool-Ergebnis aufgezeichnet hat
- Aufrufe, die der Klassifikator nicht aufzeichnet: Das Transkript des Klassifikators lässt nur lesende Abfragen wie Dateilesevorgänge und Suchen aus. Claude Code verwirft einen Hinweis, der an einen dieser Aufrufe angehängt ist
- Zusammenspiel mit Umschreibungen: Wenn der Hinweis eine Ausgabe beschreibt, die Sie mit
updatedToolOutputersetzen, geben Sie beide Felder in derselben Hook-Antwort zurück. Claude Code verwirft den Hinweis, wenn diese Umschreibung abgelehnt wird oder die Umschreibung eines anderen Hooks sie ersetzt. Claude Code stellt einen Hinweis, den Sie ohne Umschreibung zurückgeben, auch dann zu, wenn ein anderer Hook die Ausgabe umschreibt
[!WARNING]
Der Klassifikator liest Inhalte, die Sie in
classifierContexteinfügen, als Informationen der Anwendung, die die Sitzung hostet. Kopieren Sie daher keine nicht vertrauenswürdige Tool-Ausgabe oder Texte Dritter hinein. Beschränken Sie den Hinweis auf eine kurze Aussage über diesen einen Aufruf, etwa eine Tatsache über seine Herkunft oder eine Benutzeraussage dazu; verwenden Sie das Feld nicht, um sachfremde Nachrichten oder einen Ereignisstrom zu übermitteln.
PostToolUseFailure
Läuft, wenn ein Tool, dessen Ausführung begonnen hat, fehlschlägt: Das Tool hat einen Fehler ausgelöst, oder ein MCP-Tool hat ein Fehlerergebnis zurückgegeben. Verwenden Sie dies, um Fehler zu protokollieren, Warnungen zu senden oder Claude korrigierendes Feedback zu geben.
Matcht auf den Tool-Namen, mit denselben Werten wie PreToolUse.
[!NOTE]
Dieses Ereignis wird nicht für Tool-Aufrufe ausgelöst, die vor der Ausführung abgelehnt werden: ein unbekannter Tool-Name, eine Eingabe, die die Schemavalidierung oder toolspezifische Validierung nicht besteht, oder eine Berechtigungsablehnung. Validierungsablehnungen werden als
tool_use_error-Ergebnisse zurückgegeben und erfolgen, bevor Hooks laufen, sodass sie wederPreToolUsenochPostToolUseFailureauslösen. Berechtigungsablehnungen lösenPreToolUseaus, aber nicht dieses Ereignis; siehe PermissionDenied.
PostToolUseFailure-Eingabe
PostToolUseFailure-Hooks erhalten dieselben Felder tool_name und tool_input wie PostToolUse sowie Fehlerinformationen als Felder auf oberster Ebene. Bei einem MCP-Tool erhalten sie außerdem das Objekt mcp_server. Ein fehlgeschlagener npm test-Befehl könnte beispielsweise Folgendes liefern:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolUseFailure",
"tool_name": "Bash",
"tool_input": {
"command": "npm test",
"description": "Run test suite"
},
"tool_use_id": "toolu_01ABC123...",
"error": "Exit code 1\nError: Cannot find module 'express'",
"is_interrupt": false,
"duration_ms": 4187
}
| Feld | Beschreibung |
|---|---|
error |
String, der beschreibt, was schiefgelaufen ist. Das Format hängt vom fehlgeschlagenen Tool ab |
is_interrupt |
Optionaler Boolean. True, wenn der Fehler Claude Code als Abbruch erreicht hat statt als vom Tool gemeldeter Fehler. Das Abbrechen eines laufenden Tools löst diesen Hook nicht aus; stattdessen enthält das Tool-Ergebnis die Unterbrechungsmeldung |
duration_ms |
Optional. Ausführungszeit des Tools in Millisekunden. Ohne die Zeit in Berechtigungsabfragen und PreToolUse-Hooks |
Der error-String ist in der Regel derselbe Text, den Claude als Ergebnis des fehlgeschlagenen Tools erhält. Sein Format variiert je nach Tool und Fehler. Richten Sie Ihren Hook an tool_name, is_interrupt und der ersten Zeile Exit code N aus; behandeln Sie den Rest des Strings als Anzeigetext, nicht als stabiles Format.
- Bei Bash und PowerShell erzeugt ein Befehl, der gelaufen und beendet ist, eine erste Zeile
Exit code N, gefolgt von der Ausgabe des Befehls als ein Block, in dem stdout und stderr verschränkt sind - Eine Payload kann auch eine reine Fehlermeldung ohne Exit-Code-Zeile enthalten, wenn Claude Code den Shell-Prozess selbst nicht starten konnte
- Claude Code kürzt lange Strings in der Mitte um eine Markierung
... [N characters truncated] ...und kann eigene Zeilen einfügen, etwaCommand timed out after 2m 0s
PostToolUseFailure-Entscheidungssteuerung
PostToolUseFailure-Hooks können Claude nach einem Tool-Fehler Kontext liefern. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
| Feld | Beschreibung |
|---|---|
additionalContext |
String, der Claudes Kontext zusammen mit dem Fehler hinzugefügt wird. Siehe Kontext für Claude hinzufügen |
{
"hookSpecificOutput": {
"hookEventName": "PostToolUseFailure",
"additionalContext": "Additional information about the failure for Claude"
}
}
PostToolBatch
Läuft einmal, nachdem jeder Tool-Aufruf in einem Batch abgeschlossen ist, bevor Claude Code die nächste Anfrage an das Modell sendet. PostToolUse wird einmal pro Tool ausgelöst, also parallel, wenn Claude parallele Tool-Aufrufe ausführt. PostToolBatch wird genau einmal mit dem vollständigen Batch ausgelöst und ist daher der richtige Ort, um Kontext einzufügen, der von der Menge der ausgeführten Tools statt von einem einzelnen Tool abhängt. Für dieses Ereignis gibt es keinen Matcher.
PostToolBatch-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten PostToolBatch-Hooks tool_calls, ein Array, das jeden Tool-Aufruf im Batch beschreibt:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "PostToolBatch",
"tool_calls": [
{
"tool_name": "Read",
"tool_input": {"file_path": "/.../ledger/accounts.py"},
"tool_use_id": "toolu_01...",
"tool_response": "1\tfrom __future__ import annotations\n2\t..."
},
{
"tool_name": "Read",
"tool_input": {"file_path": "/.../ledger/transactions.py"},
"tool_use_id": "toolu_02...",
"tool_response": "1\tfrom __future__ import annotations\n2\t..."
}
]
}
tool_response enthält denselben Inhalt, den das Modell im entsprechenden tool_result-Block erhält. Der Wert ist ein serialisierter String oder ein Content-Block-Array, genau so, wie das Tool ihn ausgegeben hat. Bei Read bedeutet das Text mit vorangestellten Zeilennummern statt des rohen Dateiinhalts. Antworten können groß sein, parsen Sie daher nur die Felder, die Sie benötigen.
[!NOTE]
Die Struktur von
tool_responseunterscheidet sich von der beiPostToolUse.PostToolUseübergibt das strukturierteOutput-Objekt des Tools, etwa{filePath: "...", type: "create"}fürWrite;PostToolBatchübergibt den serialisiertentool_result-Inhalt, den das Modell sieht.
PostToolBatch-Entscheidungssteuerung
PostToolBatch-Hooks können Kontext für Claude einfügen. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
| Feld | Beschreibung |
|---|---|
additionalContext |
Kontext-String, der einmal vor dem nächsten Modellaufruf eingefügt wird. Unter Kontext für Claude hinzufügen finden Sie Details zur Zustellung, zum geeigneten Inhalt und dazu, wie fortgesetzte Sitzungen mit früheren Werten umgehen |
{
"hookSpecificOutput": {
"hookEventName": "PostToolBatch",
"additionalContext": "These files are part of the ledger module. Run pytest before marking the task complete."
}
}
Die Rückgabe von decision: "block" oder continue: false stoppt die Agentenschleife vor dem nächsten Modellaufruf. Die Blockierungsmeldung stammt aus dem JSON-Feld reason oder stopReason oder bei Exit-Code 2 aus stderr. Sie sehen sie als Warnung im Transkript, und sie bleibt in der Konversation, sodass Claude sie sieht, wenn die Konversation fortgesetzt wird.
PermissionDenied
Läuft, wenn der Auto-Modus einen Tool-Aufruf ablehnt, auch wenn er ohne Urteil des Klassifikators ablehnt, weil eine vom Auto-Modus unabhängige Sicherheitsprüfung die eigene Anfrage des Klassifikators abgelehnt hat oder dessen Antwort nicht geparst werden konnte. Dieser Hook wird nur im Auto-Modus ausgelöst: Er läuft nicht, wenn Sie einen Berechtigungsdialog manuell ablehnen, wenn ein PreToolUse-Hook einen Aufruf blockiert oder wenn eine deny-Regel zutrifft. Verwenden Sie ihn, um Ablehnungen zu protokollieren, die Konfiguration anzupassen oder dem Modell mitzuteilen, dass es den Tool-Aufruf erneut versuchen darf.
Matcht auf den Tool-Namen, mit denselben Werten wie PreToolUse.
PermissionDenied-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten PermissionDenied-Hooks tool_name, tool_input, tool_use_id und reason. Bei einem MCP-Tool erhalten sie außerdem das Objekt mcp_server.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "auto",
"hook_event_name": "PermissionDenied",
"tool_name": "Bash",
"tool_input": {
"command": "rm -rf /tmp/build",
"description": "Clean build directory"
},
"tool_use_id": "toolu_01ABC123...",
"reason": "[Irreversible Local Destruction]"
}
| Feld | Beschreibung |
|---|---|
reason |
Der Ablehnungsgrund. Bei einem Urteil des Klassifikators nennt er in den meisten Sitzungen die zutreffende Regel in eckigen Klammern, etwa [Data Exfiltration]; die anderen Formen finden Sie unter Ablehnungen prüfen. Bei einer Ablehnung ohne Urteil beginnt er mit Auto mode could not evaluate this action and is blocking it for safety. Bei einer Ablehnung, weil das Klassifikatormodell nicht verfügbar war, ist er der feste Text Classifier unavailable |
PermissionDenied-Entscheidungssteuerung
PermissionDenied-Hooks können dem Modell mitteilen, dass es den abgelehnten Tool-Aufruf erneut versuchen darf. Geben Sie ein JSON-Objekt zurück, in dem hookSpecificOutput.retry auf true gesetzt ist:
{
"hookSpecificOutput": {
"hookEventName": "PermissionDenied",
"retry": true
}
}
Wenn retry den Wert true hat, fügt Claude Code der Konversation eine Nachricht hinzu, die dem Modell mitteilt, dass es den Tool-Aufruf erneut versuchen darf. Claude Code macht die Ablehnung selbst nicht rückgängig. Wenn Ihr Hook kein JSON zurückgibt oder retry: false zurückgibt, bleibt die Ablehnung bestehen, und das Modell erhält die ursprüngliche Ablehnungsmeldung.
Claude Code ignoriert retry: true, wenn der Klassifikator kein Urteil über die Aktion gefällt hat: Seine Antwort konnte nicht geparst werden, oder eine vom Auto-Modus unabhängige Sicherheitsprüfung hat die eigene Anfrage des Klassifikators abgelehnt. Bei diesen Ablehnungen teilt Claude Code dem Modell bereits in der Ablehnungsmeldung mit, ob es später erneut versuchen oder weitermachen soll.
Notification
Läuft, wenn Claude Code Benachrichtigungen sendet. Matcht auf den Benachrichtigungstyp. Lassen Sie den Matcher weg, um Hooks für alle Benachrichtigungstypen auszuführen.
Sie erhalten diese Hook-Ereignisse auch bei deaktivierten Desktop-Benachrichtigungen: Die Einstellung preferredNotifChannel, einschließlich notifications_disabled, ändert nur, wie Sie benachrichtigt werden, nicht, ob Ihr Hook läuft.
| Matcher | Wann er ausgelöst wird |
|---|---|
permission_prompt |
Claude benötigt Ihre Genehmigung für eine Tool-Nutzung oder die Netzwerkanfrage eines in einer Sandbox ausgeführten Befehls, und die Abfrage wartet seit etwa sechs Sekunden |
idle_prompt |
Claude hat vor etwa 60 Sekunden seine Antwort beendet, und Sie haben seitdem nichts eingegeben |
auth_success |
Die Authentifizierung ist abgeschlossen |
auth_storage_failure |
Ihre Anmeldung ist abgeschlossen, aber Claude Code kann die neuen Anmeldedaten nicht in seinem Anmeldedatenspeicher speichern. Wird anstelle von auth_success ausgelöst |
elicitation_dialog |
Ein MCP-Server öffnet ein Elicitation-Formular, und Sie haben seit etwa sechs Sekunden nichts eingegeben |
elicitation_url_dialog |
Ein MCP-Server bittet Sie, eine Browser-URL zu öffnen, und Sie haben seit etwa sechs Sekunden nichts eingegeben |
elicitation_complete |
Ein MCP-Server meldet, dass eine Elicitation im URL-Modus abgeschlossen ist |
elicitation_response |
Eine MCP-Elicitation-Antwort wird an den Server zurückgesendet |
agent_needs_input |
Eine Hintergrundsitzung beginnt, auf Ihre Eingabe zu warten, während die Agentenansicht in einem Terminal geöffnet ist. Wird auch ausgelöst, wenn eine Terminalsitzung Ihnen die Frage zur Terminal-Einrichtung eines Teammitglieds in einem Agent-Team oder den Hinweis des Auto-Modus zu Kosten für Klassifikatoranfragen anzeigt und Sie seit etwa sechs Sekunden nichts eingegeben haben |
agent_completed |
Eine Hintergrundsitzung wird beendet oder schlägt fehl. Wird nur ausgelöst, während die Agentenansicht in einem Terminal geöffnet ist |
quota_auto_resume_fired |
Claude Code setzt Ihre Aufgabe fort, nachdem ein claude.ai-Nutzungslimit sie pausiert hatte: beim Zurücksetzen oder früher, wenn etwas, das Sie während der Wartezeit in Claude Code tun, etwa das Hinzufügen von Nutzungsguthaben, ein Upgrade Ihres Plans oder ein Modellwechsel, die Nutzung wieder verfügbar macht, mit der Ausnahme für die Modelleinstellung |
quota_auto_resume_stale |
Ein claude.ai-Nutzungslimit wurde zurückgesetzt, während Ihr Computer länger als etwa 30 Minuten im Ruhezustand war. Claude Code wartet darauf, dass Sie Enter drücken, statt fortzufahren. Nach einem kürzeren Ruhezustand fährt es fort und löst stattdessen quota_auto_resume_fired aus |
quota_auto_resume_disabled |
Claude Code beendet das Warten auf ein claude.ai-Nutzungslimit, ohne Ihre Aufgabe fortzusetzen: autoContinueAtUsageLimit wurde ausgeschaltet oder das Zurücksetzen hat sich während einer Wartezeit, die Claude Code selbst begonnen hat, um mehr als 24 Stunden verschoben, die fortgesetzte Aufgabe ist wiederholt an das Limit gestoßen, oder die Fortsetzung wurde blockiert, bevor sie das Modell erreichte. Wird nicht ausgelöst, wenn Sie Esc oder Ctrl+C drücken oder Don't continue automatically wählen |
plugin_notification |
Ein Mod löst mit $.ui.notify eine Benachrichtigung aus. Der title des Hooks ist der Name des Mods, sofern der Mod keinen festgelegt hat |
Die Typen quota_auto_resume_fired, quota_auto_resume_stale und quota_auto_resume_disabled erfordern Claude Code v2.1.234 oder höher. Der Typ auth_storage_failure erfordert v2.1.288 oder höher.
In Terminalsitzungen erfordert permission_prompt für die Netzwerkanfrage eines in einer Sandbox ausgeführten Befehls Claude Code v2.1.246 oder höher.
agent_needs_input für die Frage zur Terminal-Einrichtung eines Teammitglieds erfordert Claude Code v2.1.248 oder höher.
[!NOTE]
Die Typen
permission_prompt,idle_prompt,elicitation_dialogundelicitation_url_dialogteilen ihr Timing mit Desktop-Benachrichtigungen, sodass Sie sie in Terminalsitzungen nur sehen, wenn Sie scheinbar nicht am Terminal sind:
- Rechnen Sie mit
permission_prompt, sobald Sie seit etwa sechs Sekunden nichts eingegeben haben. Der Timer beginnt, wenn die Berechtigungsabfrage erscheint, und jeder Tastendruck verschiebt ihn. Um einen Hook sofort auszuführen, wenn Claude um Berechtigung für die Nutzung eines Tools bittet, verwenden Sie stattdessen PermissionRequest.- Rechnen Sie mit
idle_promptetwa 60 Sekunden, nachdem Claude seine Antwort beendet hat, und nur, wenn Sie seitdem nichts eingegeben haben und kein Hintergrundagent, etwa ein Subagent im Hintergrund, noch läuft. Claude Code sendetidle_promptnicht, während es darauf wartet, dass ein claude.ai-Nutzungslimit zurückgesetzt wird. Wenn die Wartezeit von selbst endet, wird stattdessen einer der Typenquota_auto_resume_*ausgelöst.- Rechnen Sie mit
elicitation_dialogfür ein Elicitation-Formular oderelicitation_url_dialogfür eine Browser-URL-Anfrage, sobald Sie seit etwa sechs Sekunden nichts eingegeben haben. Beide teilen dieselbe Sechs-Sekunden-Schwelle wiepermission_prompt: Der Timer beginnt, wenn der Dialog erscheint, und jeder Tastendruck verschiebt ihn.Eine Berechtigungsanfrage oder Elicitation, die eintrifft, während ein anderer Dialog angezeigt wird, behält dieselbe Sechs-Sekunden-Schwelle, gemessen ab dem Eintreffen der Anfrage. Ihre Benachrichtigung kann Sie erreichen, während die Anfrage noch hinter dem geöffneten Dialog wartet.
Claude Code taktet permission_prompt anders in Sitzungen, in denen es Berechtigungsanfragen an den canUseTool-Callback des Agent SDK sendet, so wie Claude Desktop und die VS Code-Erweiterung Claude Code hosten:
- Rechnen Sie mit
permission_promptetwa sechs Sekunden, nachdem Claude um Berechtigung gebeten hat. Claude Code verschiebt es nicht, während Sie tippen. - Wenn Sie oder ein PermissionRequest-Hook früher antworten, führt Claude Code
permission_promptnicht aus. - Setzen Sie
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSauf1, umpermission_promptin diesen Sitzungen auszuschalten.
Vor v2.1.233 wurde permission_prompt in diesen Sitzungen nicht ausgelöst.
Verwenden Sie separate Matcher, um je nach Benachrichtigungstyp unterschiedliche Handler auszuführen. Diese Konfiguration löst ein berechtigungsspezifisches Warnskript aus, wenn Claude eine Berechtigungsgenehmigung benötigt, und eine andere Benachrichtigung, wenn Claude untätig war:
{
"hooks": {
"Notification": [
{
"matcher": "permission_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/permission-alert.sh"
}
]
},
{
"matcher": "idle_prompt",
"hooks": [
{
"type": "command",
"command": "/path/to/idle-notification.sh"
}
]
}
]
}
}
Notification-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten Notification-Hooks message mit dem Benachrichtigungstext, einen optionalen title und notification_type, das angibt, welcher Typ ausgelöst wurde.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Notification",
"message": "Claude needs your permission",
"title": "Permission needed",
"notification_type": "permission_prompt"
}
Notification-Hooks können Benachrichtigungen weder blockieren noch ändern. Claude Code verwirft ihre Felder systemMessage und continue, gibt aber weiterhin terminalSequence aus, worauf das Beispiel für Desktop-Benachrichtigungen beruht. Notification-Hooks sind für Nebeneffekte gedacht, etwa das Weiterleiten der Benachrichtigung an einen externen Dienst.
SubagentStart
Läuft, wenn Claude mit dem Agent-Tool einen Subagenten startet, wenn Claude einen Subagenten fortsetzt und jedes Mal, wenn ein In-Process-Teammitglied eines Agent-Teams eine neue Nachricht bearbeitet. Unterstützt Matcher, um nach dem Namen des Agententyps zu filtern. Bei integrierten Agenten ist dies der Agentenname wie general-purpose, Explore oder Plan. Bei benutzerdefinierten Subagenten ist dies das Feld name aus dem Frontmatter des Agenten, nicht der Dateiname.
Bei Subagenten, die von einem Plugin bereitgestellt werden, ist der Agententyp der Plugin-bezogene Bezeichner wie my-plugin:reviewer, nicht der reine Frontmatter-Name. Der Doppelpunkt führt einen Plugin-bezogenen Namen auf den Pfad für reguläre Ausdrücke, verankern Sie den Matcher daher für eine exakte Übereinstimmung mit ^ und $: ^my-plugin:reviewer$.
SubagentStart-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten SubagentStart-Hooks agent_id mit dem eindeutigen Bezeichner des Subagenten und agent_type mit dem Agentennamen, nach dem der Matcher filtert.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SubagentStart",
"agent_id": "agent-abc123",
"agent_type": "Explore"
}
SubagentStart-Hooks können die Erstellung von Subagenten nicht blockieren, aber Kontext in den Subagenten einfügen. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, können Sie Folgendes zurückgeben:
| Feld | Beschreibung |
|---|---|
additionalContext |
String, der dem Kontext des Subagenten zu Beginn seiner Konversation vor seinem ersten Prompt hinzugefügt wird. Siehe Kontext für Claude hinzufügen |
{
"hookSpecificOutput": {
"hookEventName": "SubagentStart",
"additionalContext": "Follow security guidelines for this task"
}
}
Wenn der Hook für denselben Subagenten erneut läuft, fügt Claude Code den zurückgegebenen Kontext nur ein, wenn der Kontext des Subagenten die Kopie aus einem früheren Lauf nicht bereits enthält. Die beim Start eingefügte Kopie bleibt bestehen, sodass der Prompt-Cache des Subagenten intakt bleibt. Nachdem die automatische Komprimierung diese Kopie verworfen hat, fügt Claude Code den Kontext des nächsten Laufs erneut ein.
SubagentStop
Läuft, wenn ein Subagent von Claude Code seine Antwort beendet hat. Matcht auf den Agententyp, mit denselben Werten wie SubagentStart.
SubagentStop-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten SubagentStop-Hooks stop_hook_active, agent_id, agent_type, agent_transcript_path und last_assistant_message. Das Feld agent_type ist der Wert, nach dem der Matcher filtert. transcript_path ist das Transkript der Hauptsitzung, während agent_transcript_path das eigene Transkript des Subagenten ist, das in einem verschachtelten Ordner subagents/ gespeichert wird. Das Feld last_assistant_message enthält den Textinhalt der letzten Antwort des Subagenten, sodass Hooks darauf zugreifen können, ohne die Transkriptdatei zu parsen.
Nicht jedes SubagentStop-Ereignis stammt von einem Subagenten, den Claude gestartet hat. Claude Code führt für einige seiner eigenen Funktionen auch interne Agenten aus, etwa für Prompt-Vorschläge und /btw-Nebenfragen, und SubagentStop wird auch ausgelöst, wenn einer dieser Agenten fertig ist. Bei diesen Ereignissen ist agent_type der Agentenname, unter dem die Sitzung selbst läuft, etwa einer, der mit --agent oder der Einstellung agent festgelegt wurde, und ein leerer String, wenn die Sitzung ohne einen solchen läuft.
Ein matcher, der Agententypen nennt, passt nicht auf einen leeren agent_type. Ein Hook, dessen Matcher weggelassen wurde, "" oder "*" ist oder ein regulärer Ausdruck ist, der auf einen leeren String passt, läuft auch für Ereignisse mit leerem agent_type.
Ab Claude Code v2.1.271 liefert ein Subagent, der mit dem Tool SubagentHandback läuft, seinen Bericht über dieses Tool, bevor er stoppt. Das Feld last_assistant_message enthält dann den abschließenden Text des Subagenten, falls vorhanden, und nicht den gelieferten Bericht. Der Bericht ist die message-Eingabe dieses Aufrufs, die ein PreToolUse- oder PostToolUse-Hook mit dem Matcher SubagentHandback als tool_input.message erhält.
SubagentStop-Hooks erhalten außerdem die Arrays background_tasks und session_crons, die unter Stop-Eingabe beschrieben sind. Beide Arrays beziehen sich auf die übergeordnete Sitzung, nicht auf den Subagenten.
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../abc123.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "SubagentStop",
"stop_hook_active": false,
"agent_id": "def456",
"agent_type": "Explore",
"agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
"last_assistant_message": "Analysis complete. Found 3 potential issues...",
"background_tasks": [],
"session_crons": []
}
SubagentStop-Hooks verwenden dasselbe Format zur Entscheidungssteuerung wie Stop-Hooks, einschließlich hookSpecificOutput.additionalContext mit hookEventName auf "SubagentStop" gesetzt, für Feedback ohne Fehlercharakter, das den Subagenten weiterlaufen lässt. Die Rückgabe von decision: "block" mit einem reason lässt den Subagenten weiterlaufen und übergibt reason als nächste Anweisung an den Subagenten. Ein Hook, der durch Exit-Code 2 blockiert, übergibt seine stderr-Meldung auf dieselbe Weise. Um nach der Rückkehr eines Subagenten Kontext in die übergeordnete Sitzung einzufügen, verwenden Sie stattdessen einen PostToolUse-Hook für das Tool Agent.
TaskCreated
Läuft, wenn eine Aufgabe über das Tool TaskCreate erstellt wird. Verwenden Sie dies, um Namenskonventionen durchzusetzen, Aufgabenbeschreibungen zu verlangen oder das Erstellen bestimmter Aufgaben zu verhindern. In einer Sitzung ohne die Task-Tools wird dieses Ereignis nicht ausgelöst.
TaskCreated-Hooks unterstützen keine Matcher und werden bei jedem Auftreten ausgelöst.
TaskCreated-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten TaskCreated-Hooks task_id, task_subject und optional task_description, teammate_name und team_name.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "TaskCreated",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "session-a1b2c3d4"
}
| Feld | Beschreibung |
|---|---|
task_id |
Bezeichner der Aufgabe, die erstellt wird |
task_subject |
Titel der Aufgabe |
task_description |
Ausführliche Beschreibung der Aufgabe. Kann fehlen |
teammate_name |
Name des Teammitglieds, das die Aufgabe erstellt. Kann fehlen |
team_name |
Veraltet. Aus der Sitzung abgeleiteter Teamname; wird in einem zukünftigen Release entfernt |
agent_id |
Bei diesem Ereignis identifiziert das allgemeine Eingabefeld den Subagenten oder das In-Process-Teammitglied, das die Aufgabe erstellt. Kann fehlen. Erfordert Claude Code v2.1.290 oder höher |
TaskCreated-Entscheidungssteuerung
Ein TaskCreated-Hook kann die Erstellung mit Exit-Code 2 oder mit einer JSON-Entscheidung blockieren. In beiden Fällen löscht Claude Code die Aufgabe und gibt Ihre Meldung als Fehler des Tools an Claude zurück. Claude Code ignoriert continue: false bei diesem Ereignis, und Claude arbeitet weiter.
- Exit-Code 2: Claude Code gibt den stderr-Text als Meldung zurück.
- JSON
{"decision": "block", "reason": "..."}: Claude Code gibtreasonals Meldung zurück.
Dieses Beispiel blockiert Aufgaben, deren Betreff nicht dem erforderlichen Format entspricht:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
exit 2
fi
exit 0
TaskCompleted
Läuft, wenn eine Aufgabe als abgeschlossen markiert wird. Dies wird in zwei Situationen ausgelöst: wenn ein beliebiger Agent eine Aufgabe über das TaskUpdate-Tool explizit als abgeschlossen markiert, oder wenn ein Teammitglied eines Agent-Teams seinen Turn mit laufenden Aufgaben beendet. Verwenden Sie dies, um Abschlusskriterien wie bestandene Tests oder Lint-Prüfungen durchzusetzen, bevor eine Aufgabe geschlossen werden kann.
TaskCompleted-Hooks unterstützen keine Matcher und werden bei jedem Auftreten ausgelöst.
TaskCompleted-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten TaskCompleted-Hooks task_id, task_subject und optional task_description, teammate_name und team_name.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TaskCompleted",
"task_id": "task-001",
"task_subject": "Implement user authentication",
"task_description": "Add login and signup endpoints",
"teammate_name": "implementer",
"team_name": "session-a1b2c3d4"
}
| Feld | Beschreibung |
|---|---|
task_id |
Bezeichner der Aufgabe, die abgeschlossen wird |
task_subject |
Titel der Aufgabe |
task_description |
Ausführliche Beschreibung der Aufgabe. Kann fehlen |
teammate_name |
Name des Teammitglieds, das die Aufgabe abschließt. Kann fehlen |
team_name |
Veraltet. Aus der Sitzung abgeleiteter Teamname; wird in einem zukünftigen Release entfernt |
agent_id |
Bei diesem Ereignis identifiziert das allgemeine Eingabefeld den Subagenten oder das In-Process-Teammitglied, das die Aufgabe abschließt. Kann fehlen. Erfordert Claude Code v2.1.290 oder höher |
TaskCompleted-Entscheidungssteuerung
TaskCompleted-Hooks unterstützen zwei Möglichkeiten, den Aufgabenabschluss zu steuern:
- Exit-Code 2: Die Aufgabe wird nicht als abgeschlossen markiert, und die stderr-Meldung wird dem Modell als Feedback zurückgegeben.
- JSON
{"continue": false, "stopReason": "..."}: Wenn ein Teammitglied, das seinen Turn beendet, das Ereignis ausgelöst hat, wird das Teammitglied vollständig gestoppt, entsprechend dem Verhalten desStop-Hooks. DerstopReasonwird dem Benutzer angezeigt. Wenn das ToolTaskUpdatedas Ereignis ausgelöst hat, ignoriert Claude Codecontinue: false; Exit-Code 2 blockiert den Abschluss weiterhin.
Dieses Beispiel führt Tests aus und blockiert den Aufgabenabschluss, wenn sie fehlschlagen:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')
# Run the test suite
if ! npm test 2>&1; then
echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
exit 2
fi
exit 0
Stop
Läuft, wenn der Hauptagent von Claude Code seine Antwort beendet hat. Läuft nicht, wenn der Stopp durch eine Unterbrechung des Benutzers erfolgte. API-Fehler lösen stattdessen StopFailure aus.
[!TIP]
Der Befehl
/goalist eine integrierte Abkürzung für einen sitzungsbezogenen, Prompt-basierten Stop-Hook. Verwenden Sie ihn, wenn Claude auf eine Bedingung hinarbeiten soll, ohne dass Sie eine Hook-Konfiguration schreiben.
Stop-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten Stop-Hooks stop_hook_active, last_assistant_message, background_tasks und session_crons. Das Feld stop_hook_active ist true, wenn Claude Code bereits aufgrund eines Stop-Hooks fortfährt. Prüfen Sie diesen Wert oder verarbeiten Sie das Transkript, um nicht auf eine Bedingung hin zu blockieren, die sich nie auflöst.
Claude Code wendet eine Obergrenze von 8 aufeinanderfolgenden Fortsetzungen an: Nachdem Stop-Hooks den Turn achtmal in Folge fortgesetzt haben, überschreibt Claude Code die nächste Blockierung und beendet den Turn. Der Zähler aufeinanderfolgender Fortsetzungen wird jedes Mal zurückgesetzt, wenn Claude ein Tool aufruft. Um die Obergrenze anzuheben, setzen Sie CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Das Feld last_assistant_message enthält den Textinhalt von Claudes letzter Antwort, sodass Hooks darauf zugreifen können, ohne die Transkriptdatei zu parsen. Für Hooks, die auf den gerade abgeschlossenen Turn reagieren, etwa Vorlese- oder Benachrichtigungs-Hooks, verwenden Sie dieses Feld, statt transcript_path zu lesen: Es ist nicht in allen Versionen garantiert, dass die Transkriptdatei zum Zeitpunkt von Stop die letzte Nachricht enthält.
Mit den Arrays background_tasks und session_crons können Hooks zwischen „Sitzung ist fertig“ und „Sitzung ist pausiert und wartet darauf, dass Hintergrundarbeit sie wieder aufweckt“ unterscheiden. Beide Arrays sind vorhanden, wenn die Aufgabenregistrierung erreichbar ist, und leer, wenn nichts läuft oder geplant ist.
Jeder Eintrag in background_tasks beschreibt eine laufende Aufgabe und verwendet diese Felder:
| Feld | Beschreibung |
|---|---|
id |
Aufgabenbezeichner |
type |
Lesbare Bezeichnung des Aufgabentyps wie shell, subagent, monitor, workflow, teammate, cloud session oder MCP task. Jede Bezeichnung gibt an, welche Funktion von Claude Code die Aufgabe erstellt hat. Bei nicht erkannten Typen wird auf den rohen Diskriminator zurückgegriffen |
status |
Aktueller Aufgabenstatus |
description |
Freitextbeschreibung, auf 1000 Zeichen begrenzt, mit einer Markierung … [+N chars] im String, wenn gekürzt |
command |
Shell-Befehlszeile, auf 1000 Zeichen begrenzt. Nur bei shell-Aufgaben vorhanden |
agent_type |
Name des Subagententyps. Nur bei subagent-Aufgaben vorhanden |
server |
Name des MCP-Servers. Nur bei monitor- und MCP task-Aufgaben vorhanden |
tool |
Name des MCP-Tools. Nur bei monitor- und MCP task-Aufgaben vorhanden |
name |
Workflow-Name. Nur bei workflow-Aufgaben vorhanden |
Jeder Eintrag in session_crons beschreibt ein sitzungsbezogenes geplantes Aufwecken, das aus CronCreate, ScheduleWakeup und /loop stammt:
| Feld | Beschreibung |
|---|---|
id |
Bezeichner der Cron-Aufgabe |
schedule |
Cron-Ausdruck, zum Beispiel 0 9 * * 1-5 |
recurring |
false für einmalige Aufweckvorgänge, deren Zeitplan einen einzigen Auslösezeitpunkt kodiert, true für Aufgaben, die bei jeder Übereinstimmung erneut ausgelöst werden |
prompt |
Prompt, der beim Auslösen des Cron-Jobs gesendet wird, auf 1000 Zeichen begrenzt, mit derselben Markierung … [+N chars] |
Dieses Beispiel zeigt eine Stop-Eingabe mit einer laufenden Shell-Aufgabe und einem wiederkehrenden Cron-Job:
{
"session_id": "abc123",
"transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "Stop",
"stop_hook_active": true,
"last_assistant_message": "I've completed the refactoring. Here's a summary...",
"background_tasks": [
{
"id": "task-001",
"type": "shell",
"status": "running",
"description": "tail logs",
"command": "tail -f /var/log/syslog"
}
],
"session_crons": [
{
"id": "cron-001",
"schedule": "0 9 * * 1-5",
"recurring": true,
"prompt": "check the build"
}
]
}
Stop-Entscheidungssteuerung
Stop- und SubagentStop-Hooks können steuern, ob Claude fortfährt. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, kann Ihr Hook-Skript diese ereignisspezifischen Felder zurückgeben:
| Feld | Beschreibung |
|---|---|
decision |
"block" verhindert, dass Claude stoppt. Weglassen, um Claude stoppen zu lassen |
reason |
Erforderlich, wenn decision den Wert "block" hat. Teilt Claude mit, warum es fortfahren soll |
hookSpecificOutput.additionalContext |
Feedback ohne Fehlercharakter für Claude. Die Konversation wird fortgesetzt, damit Claude darauf reagieren kann, aber anders als bei decision: "block" wird es im Transkript als Hook-Feedback statt als Hook-Fehler angezeigt |
Ein Hook, der durch Exit-Code 2 blockiert, wird genauso behandelt wie reason: Claude erhält die stderr-Meldung als Erklärung, warum es fortfahren soll.
{
"decision": "block",
"reason": "Must be provided when Claude is blocked from stopping"
}
Verwenden Sie additionalContext, wenn der Hook wie vorgesehen arbeitet und Claude Hinweise gibt, etwa „Führe die Testsuite aus, bevor du fertig bist“. Es hält die Konversation mit denselben Schleifenschutzmechanismen wie decision: "block" am Laufen, nämlich der Eingabe stop_hook_active und der Obergrenze von 8 aufeinanderfolgenden Fortsetzungen, aber das Transkript kennzeichnet es als Stop hook feedback, und es wird keine Hook-Fehlerbenachrichtigung angezeigt:
{
"hookSpecificOutput": {
"hookEventName": "Stop",
"additionalContext": "Please run the test suite before finishing"
}
}
StopFailure
Läuft anstelle von Stop, wenn der Turn aufgrund eines API-Fehlers endet. Claude Code ignoriert die Ausgabe und den Exit-Code des Hooks, abgesehen von terminalSequence. Verwenden Sie dies, um Fehler zu protokollieren, Warnungen zu senden oder Wiederherstellungsmaßnahmen zu ergreifen, wenn Claude eine Antwort aufgrund von Rate-Limits, Authentifizierungsproblemen oder anderen API-Fehlern nicht abschließen kann.
StopFailure-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten StopFailure-Hooks error, optional error_details und optional last_assistant_message. Das Feld error identifiziert den Fehlertyp und wird für die Filterung durch den Matcher verwendet.
| Feld | Beschreibung |
|---|---|
error |
Fehlertyp: rate_limit, overloaded, authentication_failed, oauth_org_not_allowed, account_on_hold, billing_error, invalid_request, model_not_found, server_error, max_output_tokens, cloud_credential_error oder unknown |
error_details |
Zusätzliche Details zum Fehler, sofern verfügbar |
last_assistant_message |
Der gerenderte Fehlertext, der in der Konversation angezeigt wird. Anders als bei Stop und SubagentStop, wo dieses Feld Claudes Konversationsausgabe enthält, enthält es bei StopFailure den API-Fehlerstring selbst, etwa "API Error: Rate limit reached" |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "StopFailure",
"error": "rate_limit",
"error_details": "429 Too Many Requests",
"last_assistant_message": "API Error: Rate limit reached"
}
StopFailure-Hooks haben keine Entscheidungssteuerung. Sie laufen nur zu Benachrichtigungs- und Protokollierungszwecken.
TeammateIdle
Läuft, wenn ein Teammitglied eines Agent-Teams nach Beenden seines Turns gleich in den Leerlauf wechselt. Verwenden Sie dies, um Qualitätsschranken durchzusetzen, bevor ein Teammitglied aufhört zu arbeiten, etwa bestandene Lint-Prüfungen zu verlangen oder zu prüfen, ob Ausgabedateien existieren.
TeammateIdle-Hooks unterstützen keine Matcher und werden bei jedem Auftreten ausgelöst.
TeammateIdle-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten TeammateIdle-Hooks teammate_name und team_name.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"permission_mode": "default",
"hook_event_name": "TeammateIdle",
"teammate_name": "researcher",
"team_name": "session-a1b2c3d4"
}
| Feld | Beschreibung |
|---|---|
teammate_name |
Name des Teammitglieds, das gleich in den Leerlauf wechselt |
team_name |
Veraltet. Aus der Sitzung abgeleiteter Teamname; wird in einem zukünftigen Release entfernt |
agent_id |
Bei diesem Ereignis identifiziert das allgemeine Eingabefeld das In-Process-Teammitglied, das gleich in den Leerlauf wechselt. Kann fehlen. Erfordert Claude Code v2.1.290 oder höher |
TeammateIdle-Entscheidungssteuerung
TeammateIdle-Hooks unterstützen zwei Möglichkeiten, das Verhalten von Teammitgliedern zu steuern:
- Exit-Code 2: Das Teammitglied erhält die stderr-Meldung als Feedback und arbeitet weiter, statt in den Leerlauf zu wechseln.
- JSON
{"continue": false, "stopReason": "..."}: Stoppt das Teammitglied vollständig, entsprechend dem Verhalten desStop-Hooks. DerstopReasonwird dem Benutzer angezeigt.
Dieses Beispiel prüft, ob ein Build-Artefakt existiert, bevor ein Teammitglied in den Leerlauf wechseln darf:
#!/bin/bash
if [ ! -f "./dist/output.js" ]; then
echo "Build artifact missing. Run the build before stopping." >&2
exit 2
fi
exit 0
ConfigChange
Läuft, wenn sich während einer Sitzung eine Konfigurationsdatei ändert. Verwenden Sie dies, um Änderungen an Einstellungen zu prüfen, Sicherheitsrichtlinien durchzusetzen oder nicht autorisierte Änderungen an Konfigurationsdateien zu blockieren.
Claude Code führt ConfigChange-Hooks aus, wenn sich eine Einstellungsdatei, eine verwaltete Richtliniendatei oder eine Skill-Datei ändert. Bei verwalteten Richtlinien führt es sie nur aus, wenn sich managed-settings.json oder eine Datei in managed-settings.d/ ändert. Servergesteuerte Einstellungen sowie Änderungen an verwalteten macOS-Einstellungen oder Windows-Registry-Richtlinien übernimmt es, ohne die Hooks auszuführen. Unter WSL mit wslInheritsWindowsSettings übernimmt es außerdem eine geänderte Windows-seitige verwaltete Einstellungsdatei bei seiner Richtlinienabfrage, ohne die Hooks auszuführen.
Der Matcher filtert nach der Konfigurationsquelle:
| Matcher | Wann er ausgelöst wird |
|---|---|
user_settings |
~/.claude/settings.json ändert sich |
project_settings |
.claude/settings.json ändert sich |
local_settings |
.claude/settings.local.json ändert sich |
policy_settings |
managed-settings.json oder eine Datei in managed-settings.d/ ändert sich |
skills |
Eine Skill-Datei in .claude/skills/ ändert sich |
Dieses Beispiel protokolliert alle Konfigurationsänderungen für Sicherheitsaudits:
{
"hooks": {
"ConfigChange": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/audit-config-change.sh",
"args": []
}
]
}
]
}
}
ConfigChange-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten ConfigChange-Hooks source und optional file_path. Das Feld source gibt an, welcher Konfigurationstyp sich geändert hat, und file_path liefert den Pfad zur konkret geänderten Datei.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "ConfigChange",
"source": "project_settings",
"file_path": "/Users/.../my-project/.claude/settings.json"
}
ConfigChange-Entscheidungssteuerung
ConfigChange-Hooks können verhindern, dass Konfigurationsänderungen wirksam werden. Verwenden Sie Exit-Code 2 oder eine JSON-decision, um die Änderung zu verhindern. Bei einer Blockierung werden die neuen Einstellungen nicht auf die laufende Sitzung angewendet.
| Feld | Beschreibung |
|---|---|
decision |
"block" verhindert, dass die Konfigurationsänderung angewendet wird. Weglassen, um die Änderung zuzulassen |
reason |
Wird akzeptiert, aber nie angezeigt |
{
"decision": "block",
"reason": "Configuration changes to project settings require admin approval"
}
Änderungen an policy_settings können nicht blockiert werden. Hooks werden für policy_settings-Quellen weiterhin ausgelöst, wenn sich eine verwaltete Einstellungsdatei auf dem Rechner ändert, sodass Sie sie zum Protokollieren dieser Änderungen verwenden können, aber jede Blockierungsentscheidung wird ignoriert. Dadurch wird sichergestellt, dass unternehmensweit verwaltete Einstellungen immer wirksam werden. Claude Code führt keine ConfigChange-Hooks aus, wenn servergesteuerte Einstellungen eintreffen oder aktualisiert werden.
Claude Code reagiert auf die Blockierungsentscheidung aus der JSON-Ausgabe eines ConfigChange-Hooks und verwirft systemMessage und continue. Eine blockierte Änderung zeigt weder Ihnen noch Claude eine Meldung an, unabhängig davon, ob Sie mit reason oder mit stderr bei Exit-Code 2 blockieren. Claude Code schreibt lediglich eine Zeile in das Debug-Log.
CwdChanged
Läuft, wenn ein Shell-Befehl in der Hauptkonversation das Arbeitsverzeichnis ändert, etwa wenn Claude einen cd-Befehl ausführt. Verwenden Sie dies, um auf Verzeichniswechsel zu reagieren: Umgebungsvariablen neu laden, projektspezifische Toolchains aktivieren oder Einrichtungsskripte automatisch ausführen. Lässt sich mit FileChanged kombinieren, etwa für Tools wie direnv, die Umgebungen pro Verzeichnis verwalten.
CwdChanged-Hooks haben Zugriff auf CLAUDE_ENV_FILE. Wenn Ihr Hook Variablen in diese Datei schreibt, bleiben sie für nachfolgende Bash-Befehle erhalten, bis zum nächsten CwdChanged-Event, bei dem Claude Code sie löscht.
CwdChanged unterstützt keine Matcher und wird bei jedem Auftreten ausgelöst.
CwdChanged-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten CwdChanged-Hooks old_cwd und new_cwd.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project/src",
"hook_event_name": "CwdChanged",
"old_cwd": "/Users/my-project",
"new_cwd": "/Users/my-project/src"
}
CwdChanged-Ausgabe
Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, können CwdChanged-Hooks watchPaths zurückgeben, um dynamisch festzulegen, welche Dateipfade FileChanged überwacht:
| Feld | Beschreibung |
|---|---|
watchPaths |
Array absoluter Pfade. Ersetzt die aktuelle dynamische Überwachungsliste. Pfade aus Ihrer matcher-Konfiguration werden immer überwacht. Die Rückgabe eines leeren Arrays leert die dynamische Liste, was beim Wechsel in ein neues Verzeichnis typisch ist |
CwdChanged-Hooks haben keine Entscheidungssteuerung. Sie können den Verzeichniswechsel nicht blockieren.
Claude Code liest watchPaths und systemMessage aus ihrer JSON-Ausgabe und verwirft continue. In interaktiven Sitzungen zeigt es die systemMessage als kurze Terminal-Benachrichtigung an. Die Meldung erreicht den SDK-Nachrichtenstrom nicht.
DirectoryAdded
Läuft, nachdem Sie während einer Sitzung mit dem Befehl /add-dir ein Arbeitsverzeichnis hinzugefügt haben oder nachdem ein SDK-Client eines mit der Steuerungsanfrage register_repo_root hinzugefügt hat. Verwenden Sie dies, um ein neu hinzugefügtes Repository vorzubereiten, etwa durch Installieren seiner Abhängigkeiten.
Claude Code löst dieses Ereignis nicht aus, wenn:
- Sie ein Verzeichnis mit dem Start-Flag
--add-dirübergeben; SessionStart deckt diese Verzeichnisse ab - Sie ein Verzeichnis auf dem Tab „Workspace“ von
/permissionshinzufügen - Sie ein Verzeichnis hinzufügen, das bereits ein Arbeitsverzeichnis ist oder sich innerhalb eines solchen befindet
Claude Code löst DirectoryAdded aus, nachdem der Sandbox- und Berechtigungsstatus aktualisiert wurde, sodass in einer Sandbox ausgeführte Tools das neue Verzeichnis bereits sehen, wenn Ihr Hook läuft. Hook-Befehle selbst laufen ohne Sandbox.
Claude Code wartet nicht auf den Hook: Das Hinzufügen wird sofort abgeschlossen, und der Hook läuft im Hintergrund mit dem Standard-Timeout von 600 Sekunden.
Der Matcher filtert danach, wie das Verzeichnis hinzugefügt wurde:
| Matcher | Wann er ausgelöst wird |
|---|---|
slash_command |
Sie fügen ein Verzeichnis mit /add-dir hinzu |
register_repo_root |
Ein SDK-Client fügt ein Verzeichnis mit der Steuerungsanfrage register_repo_root hinzu |
DirectoryAdded-Eingabe
Zusätzlich zu den allgemeinen Eingabefeldern erhalten DirectoryAdded-Hooks directory und source.
| Feld | Beschreibung |
|---|---|
directory |
Absoluter Pfad des hinzugefügten Verzeichnisses |
source |
Wie das Verzeichnis hinzugefügt wurde: "slash_command" für /add-dir oder "register_repo_root" für die SDK-Steuerungsanfrage |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "DirectoryAdded",
"directory": "/Users/my-other-repo",
"source": "slash_command"
}
DirectoryAdded-Hooks haben keine Entscheidungssteuerung. Sie können das Hinzufügen nicht blockieren, da es bereits abgeschlossen ist, wenn der Hook läuft. Claude Code verwirft das Feld continue aus ihrer JSON-Ausgabe und gibt den Rest je nach Quelle unterschiedlich aus:
slash_command: Claude Code übergibt diesystemMessagedes Hooks im nächsten Konversations-Turn als Kontext an Claude, statt sie Ihnen anzuzeigen. Die Anzahl fehlgeschlagener Hooks erscheint im Transkript. Die vollständige Fehlerausgabe wird in das Debug-Log geschriebenregister_repo_root: Claude Code schreibt diesystemMessage-Ausgabe und die Fehlerausgabe nur in das Debug-Log
FileChanged
Wird ausgeführt, wenn sich eine überwachte Datei auf dem Datenträger ändert. Claude Code erkennt Änderungen mit einem Dateisystem-Watcher und nicht durch Prüfen von Tool-Aufrufen. Daher führt es den Hook unabhängig davon aus, was die Datei geändert hat: ein Edit- oder Write-Tool-Aufruf, ein Skript, das Claude mit Bash ausführt, oder ein Prozess vollständig außerhalb von Claude Code. Ein häufiger Anwendungsfall ist das Neuladen von Umgebungsvariablen, wenn sich Konfigurationsdateien des Projekts ändern.
Der matcher für dieses Ereignis erfüllt zwei Aufgaben:
- Die Überwachungsliste aufbauen: Der Wert wird an
|aufgeteilt, und jedes Segment wird als literaler Dateiname im Arbeitsverzeichnis registriert, sodass".envrc|.env"genau diese beiden Dateien überwacht. Regex-Muster sind hier nicht sinnvoll: Ein Wert wie^\.envwürde eine Datei überwachen, die buchstäblich^\.envheißt. - Filtern, welche Hooks ausgeführt werden: Wenn sich eine überwachte Datei ändert, filtert derselbe Wert anhand der Standard-Matcher-Regeln und des Basisnamens der geänderten Datei, welche Hook-Gruppen ausgeführt werden.
Dieses Beispiel normalisiert die Zeilenenden in data.csv nach jeder Änderung, auch wenn ein Bash-Befehl oder ein externes Skript die Datei neu schreibt:
{
"hooks": {
"FileChanged": [
{
"matcher": "data.csv",
"hooks": [
{
"type": "command",
"command": "/path/to/normalize-line-endings.sh"
}
]
}
]
}
}
Der Hook liest den absoluten Pfad der geänderten Datei aus dem Feld file_path der JSON-Eingabe auf stdin. Seine grep-Prüfung testet auf genau das, was perl entfernt, nämlich ein CR am Zeilenende, sodass der Durchlauf nach einer Normalisierung beendet wird, ohne die Datei anzufassen. Eine lockerere Prüfung führt zu einer Endlosschleife, weil perl -i die Datei auch dann neu schreibt, wenn nichts ersetzt wird, und Claude Code den Hook nach jedem Neuschreiben erneut ausführt. Speichern Sie dieses Skript unter /path/to/normalize-line-endings.sh und machen Sie es ausführbar:
#!/bin/bash
FILE=$(jq -r .file_path)
if grep -q $'\r$' "$FILE"; then
perl -pi -e 's/\r$//' "$FILE"
fi
Um zu prüfen, ob der Hook funktioniert, bitten Sie Claude, mit einem Bash-Befehl eine CRLF-Zeile an data.csv anzuhängen. Claude Code führt den Hook aus, und die Datei hat anschließend LF-Zeilenenden.
Um Dateien zu überwachen, die Sie nicht vorab benennen können, geben Sie aus einem Hook watchPaths zurück, um die Überwachungsliste dynamisch zu aktualisieren. Claude Code startet den Watcher erst, wenn etwas eine zu überwachende Datei benennt. Befüllen Sie die Liste daher mit einer FileChanged-Gruppe, deren Matcher mindestens eine Datei benennt, oder mit einem SessionStart- oder CwdChanged-Hook, der watchPaths zurückgibt. Der Matcher filtert weiterhin, welche Hook-Gruppen ausgeführt werden, wenn sich eine überwachte Datei ändert. Lassen Sie den Matcher daher bei der Gruppe weg, die dynamische Pfade verarbeitet: Ein fehlender Matcher passt auf jede überwachte Datei und fügt der Überwachungsliste nichts hinzu. Ein "*"-Matcher passt ebenfalls auf jede Datei, aber Claude Code registriert ihn wie jeden anderen Wert in der Überwachungsliste, als literale Datei namens *.
FileChanged-Hooks haben Zugriff auf CLAUDE_ENV_FILE. In diese Datei geschriebene Variablen bleiben für nachfolgende Bash-Befehle erhalten, bis Claude Code sie beim nächsten CwdChanged-Ereignis löscht.
FileChanged-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten FileChanged-Hooks file_path und event.
| Feld | Beschreibung |
|---|---|
file_path |
Absoluter Pfad zur geänderten Datei |
event |
Was passiert ist: "change" für eine geänderte Datei, "add" für eine erstellte Datei oder "unlink" für eine gelöschte Datei |
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
"cwd": "/Users/my-project",
"hook_event_name": "FileChanged",
"file_path": "/Users/my-project/.envrc",
"event": "change"
}
FileChanged-Ausgabe
Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, können FileChanged-Hooks watchPaths zurückgeben, um dynamisch zu aktualisieren, welche Dateipfade überwacht werden:
| Feld | Beschreibung |
|---|---|
watchPaths |
Array absoluter Pfade. Ersetzt die aktuelle dynamische Überwachungsliste. Pfade aus Ihrer matcher-Konfiguration werden immer überwacht. Verwenden Sie dies, wenn Ihr Hook-Skript anhand der geänderten Datei weitere zu überwachende Dateien ermittelt |
FileChanged-Hooks haben keine Entscheidungskontrolle. Sie können die Dateiänderung nicht verhindern.
Claude Code liest watchPaths und systemMessage aus ihrer JSON-Ausgabe und verwirft continue. In interaktiven Sitzungen zeigt es die systemMessage als kurze Terminal-Benachrichtigung an. Die Nachricht erreicht den SDK-Nachrichtenstream nicht.
WorktreeCreate
Wird ausgeführt, wenn Claude Code einen Worktree erstellt, etwa in diesen Fällen:
- Sie starten eine Sitzung mit
claude --worktree - Claude erstellt während einer Sitzung einen Worktree und wechselt mit dem Tool
EnterWorktreehinein - Ein Subagent verwendet
isolation: "worktree" - Ein Workflow führt einen Agenten in einem eigenen Worktree aus
- Claude Code isoliert eine Hintergrundsitzung in einem eigenen Worktree
claude remote-controlim Modusworktreestartet eine Sitzung in einem eigenen Worktree
Ein WorktreeCreate-Hook wird nicht für einen Worktree ausgeführt, den Claude über Bash hinzufügt, und der Hook eines Plugins wird nicht für den Worktree ausgeführt, der beim Start mit --worktree und --tmux erstellt wird:
git worktree addüber Bash: Um diesen Befehl zu prüfen oder zu blockieren, fügen Sie einenPreToolUse-Hook mit dem MatcherBashhinzu.--worktreemit--tmux: Claude Code erstellt den Worktree, bevor es Plugins lädt, sodass ein WorktreeCreate-Hook aus einem Plugin nicht ausgeführt wird. Damit Ihr Hook mit beiden Flags ausgeführt wird, definieren Sie ihn stattdessen in einer Einstellungsdatei. Vor v2.1.300 wurde der WorktreeCreate-Hook eines Plugins auch mit--worktreeallein nicht ausgeführt.
Standardmäßig erstellt Claude Code die isolierte Arbeitskopie mit git worktree. Wenn Sie einen WorktreeCreate-Hook konfigurieren, ersetzt dieser das standardmäßige Git-Verhalten, sodass Sie ein anderes Versionskontrollsystem wie SVN, Perforce oder Mercurial verwenden können.
Da der Hook das Standardverhalten 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 Pfad zum erstellten Worktree-Verzeichnis zurückgeben. Claude Code verwendet diesen Pfad als Arbeitsverzeichnis für die isolierte Sitzung. Unter WorktreeCreate-Ausgabe erfahren Sie, wie jeder Hook-Typ den Pfad zurückgibt.
Claude Code reagiert auf den Erfolg des Hooks und den zurückgegebenen Pfad und verwirft systemMessage und continue.
Dieses Beispiel erstellt eine SVN-Arbeitskopie und gibt den Pfad aus, den Claude Code verwenden soll. Ersetzen Sie die Repository-URL durch Ihre eigene:
{
"hooks": {
"WorktreeCreate": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
}
]
}
]
}
}
Der Hook liest den Worktree-name aus der JSON-Eingabe auf stdin, checkt eine neue Kopie in ein neues Verzeichnis aus und gibt den Verzeichnispfad aus. Das echo in der letzten Zeile ist das, was Claude Code als Worktree-Pfad liest. Leiten Sie jede andere Ausgabe nach stderr um, damit sie den Pfad nicht stört.
WorktreeCreate-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten WorktreeCreate-Hooks das Feld name. Dies ist ein Slug-Bezeichner für den neuen Worktree, entweder vom Benutzer angegeben oder automatisch generiert, zum Beispiel bold-oak-a3f2.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeCreate",
"name": "feature-auth"
}
WorktreeCreate-Ausgabe
WorktreeCreate-Hooks verwenden nicht das standardmäßige Allow/Block-Entscheidungsmodell. Stattdessen bestimmt Erfolg oder Fehlschlag des Hooks das Ergebnis. Der Hook muss den Pfad zum erstellten Worktree-Verzeichnis zurückgeben:
- Command-Hooks (
type: "command"): Geben Sie den Pfad als letzte nicht leere Zeile auf stdout aus. Claude Code entfernt ANSI-Escape-Codes, bevor es diese Zeile liest, sodass Shell-Startbanner, die vor Ihremechoausgegeben werden, ignoriert werden. Leiten Sie jede andere Hook-Ausgabe nach stderr um. - HTTP-Hooks (
type: "http"): Geben Sie{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }im Response-Body zurück.
Wenn der Hook fehlschlägt oder keinen Pfad liefert, schlägt die Worktree-Erstellung mit einem Fehler fehl. Claude Code löst einen relativen Pfad relativ zu dem Verzeichnis auf, in dem der Hook ausgeführt wurde, und fasst dabei alle .- oder ..-Segmente zusammen. Wenn der resultierende Pfad kein Verzeichnis ist, das Claude Code betreten kann, gibt die Sitzung einen Fehler mit dem Pfad aus und beendet sich mit Code 1.
Claude Code lehnt einen absoluten Pfad ab, der .- oder ..-Segmente enthält, sowie jeden Pfad, der unterhalb des Repository-Stammverzeichnisses durch einen Symlink führt. Der Fehler nennt die abgelehnte Komponente. Ein im Repository committeter Symlink könnte den Worktree nach außerhalb umleiten. Geben Sie einen normalisierten Pfad zurück, der nicht durch einen Symlink innerhalb des Repositorys führt.
WorktreeRemove
Wird ausgeführt, wenn Claude Code einen Worktree bereinigt, den Ihr WorktreeCreate-Hook erstellt hat. Das Ereignis wird ausgelöst, wenn:
- Sie eine interaktive Worktree-Sitzung beenden und auf Nachfrage von Claude Code entscheiden, den Worktree zu entfernen
- Sie eine interaktive Worktree-Sitzung beenden, die Sie nicht benannt haben, Claude Code keine geänderten oder nicht verfolgten Dateien findet und den Worktree ohne Nachfrage entfernt
- Sie eine Hintergrundsitzung löschen, die im Worktree läuft
- Sie Claude während einer Sitzung bitten, den Worktree zu verlassen und zu entfernen, und Claude ihn mit dem Tool
ExitWorktreeentfernt - Eine Sitzung, die
claude remote-controlim Modusworktreegestartet hat, ohne Absturz endet und Claude Code in ihrem Worktree keine geänderten oder nicht verfolgten Dateien und keine neuen Commits findet - Sie
claude remote-controlstoppen, während Sitzungen, die es im Modusworktreegestartet hat, noch laufen, und Claude Code in deren Worktrees keine geänderten oder nicht verfolgten Dateien und keine neuen Commits findet
Wenn ein Subagent oder ein Workflow-Agent fertig ist, behält Claude Code den Worktree, den Ihr WorktreeCreate-Hook für ihn erstellt hat, und führt Ihren WorktreeRemove-Hook nicht aus. Entfernen Sie diese Worktrees selbst, wenn die Arbeit erledigt ist.
Claude Code fragt nicht bei Ihnen nach, bevor Ihr Hook für einen ExitWorktree-Aufruf oder eine Bereinigung durch claude remote-control ausgeführt wird. ExitWorktree weigert sich, einen per Hook erstellten Worktree zu entfernen, sofern der Aufruf nicht discard_changes: true übergibt. Damit ist Ihr Hook auf diesem Weg die letzte Prüfung.
Claude Code sucht mit Git nach geänderten oder nicht verfolgten Dateien. Daher findet es in einem Worktree, der kein Git-Checkout ist und auch nicht in einem liegt, keine solchen Dateien, selbst wenn das Verzeichnis nicht committete Arbeit enthält. Prüfen Sie in Ihrem WorktreeRemove-Hook auf solche Arbeit, bevor er etwas löscht.
Bei Git-basierten Worktrees übernimmt Claude Code die Bereinigung automatisch mit git worktree remove. Wenn Sie einen WorktreeCreate-Hook konfiguriert haben, kombinieren Sie ihn mit einem WorktreeRemove-Hook, um die Bereinigung zu steuern:
- Kein WorktreeRemove-Hook: Claude Code greift beim Entfernen beim Beenden oder über
ExitWorktreeauf Git zurück und behält den Worktree, wennclaude remote-controlbereinigt:- Sie beenden eine Worktree-Sitzung, oder Claude ruft
ExitWorktreeauf: Claude Code entfernt den von Ihrem WorktreeCreate-Hook zurückgegebenen Pfad so, wie esgit worktree remove --forcetun würde, sodass ein Worktree, den Git erkennt, entfernt wird. Ein Worktree, den Git nicht erkennt, etwa einer, den Ihr Hook mit einem Nicht-Git-Versionskontrollsystem erstellt hat, bleibt auf dem Datenträger. claude remote-controlbereinigt den Worktree einer Sitzung: Der Worktree bleibt auf dem Datenträger, und das Terminal, in demclaude remote-controlläuft, zeigtworktree removal failed, kept: <path>an.- Sie löschen eine Hintergrundsitzung: siehe Was das Löschen einer Sitzung entfernt.
- Sie beenden eine Worktree-Sitzung, oder Claude ruft
- Hook wird mit 0 beendet: Der Worktree gilt als entfernt. Claude Code liest nichts weiter vom Hook, stellen Sie also sicher, dass Ihr Hook das Verzeichnis gelöscht hat.
- Hook wird mit einem Wert ungleich 0 beendet: Das Entfernen schlägt fehl, wenn das Verzeichnis unter
worktree_pathdanach noch existiert, und der Worktree bleibt ohne Git-Fallback auf dem Datenträger. Ein Hook, der das Verzeichnis gelöscht hat, bevor er mit einem Wert ungleich 0 beendet wurde, gilt als entfernt. Wie der Fehler gemeldet wird, lesen Sie unter WorktreeRemove-Eingabe.
Claude Code löscht niemals einen Branch, der zu einem per Hook erstellten Worktree gehört, weil es nur den Pfad kennt, den Ihr WorktreeCreate-Hook zurückgegeben hat. Wenn Ihr WorktreeCreate-Hook einen Branch erstellt, löschen Sie ihn in Ihrem WorktreeRemove-Hook. Claude Code verwirft die JSON-Ausgabefelder eines WorktreeRemove-Hooks, etwa systemMessage und continue.
Beim Löschen einer Hintergrundsitzung überprüft Claude Code den gespeicherten Worktree-Pfad, bevor der Hook ausgeführt wird, und lehnt einen Pfad ab, der ein Symlink ist oder unterhalb des Repository-Stammverzeichnisses durch einen führt. Auf diesem Weg wird der Hook für einen Worktree, der noch Dateien enthält, nur ausgeführt, wenn Sie das Löschen in der Agentenansicht bestätigen. Für einen solchen Worktree behält claude rm stattdessen Sitzung und Worktree.
Claude Code übergibt den von WorktreeCreate zurückgegebenen Pfad als worktree_path in der Hook-Eingabe. Dieses Beispiel liest diesen Pfad und entfernt das Verzeichnis:
{
"hooks": {
"WorktreeRemove": [
{
"hooks": [
{
"type": "command",
"command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
}
]
}
]
}
}
WorktreeRemove-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten WorktreeRemove-Hooks das Feld worktree_path, den absoluten Pfad zum Worktree, der entfernt wird.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "WorktreeRemove",
"worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}
Der Exit-Code eines WorktreeRemove-Hooks entscheidet über das Ergebnis. Wenn ein Hook mit einem Wert ungleich 0 beendet wird und das Verzeichnis unter worktree_path danach noch existiert, schlägt das Entfernen fehl:
- Der Worktree bleibt auf dem Datenträger, und der Befehl sowie stderr des Hooks werden in das Debug-Log geschrieben.
- Wenn Sie eine Hintergrundsitzung gelöscht haben, bleibt auch die Sitzung bestehen. Die Ablehnungsmeldung in der Agent-Ansicht meldet, wie der Hook beendet wurde, etwa
exited 1, zitiert den Anfang seiner stderr-Ausgabe und gibt an, ob ein erneutes Löschen der Sitzung das Verzeichnis trotzdem entfernt. - Wenn
claude remote-controlden Worktree einer Sitzung bereinigt hat, zeigt das Terminal, in dem es läuft,worktree removal failed, kept: <path>an.
PreCompact
Wird ausgeführt, bevor Claude Code eine Komprimierung durchführt.
Der Matcher-Wert gibt an, ob die Komprimierung manuell oder automatisch ausgelöst wurde:
| Matcher | Wann er ausgelöst wird |
|---|---|
manual |
/compact |
auto |
Automatische Komprimierung, wenn die Konversation das Fenster für die automatische Komprimierung erreicht, sowie Leerlauf-Komprimierung |
Beenden Sie mit Code 2, um die Komprimierung zu blockieren. Bei einem manuellen /compact wird die stderr-Meldung dem Benutzer angezeigt. Sie können auch blockieren, indem Sie JSON mit "decision": "block" zurückgeben.
Das Blockieren der automatischen Komprimierung hat je nach Zeitpunkt unterschiedliche Auswirkungen. Wenn die Komprimierung proaktiv vor Erreichen des Kontextlimits ausgelöst wurde, überspringt Claude Code sie, und die Konversation wird unkomprimiert fortgesetzt. Wenn die Komprimierung ausgelöst wurde, um sich von einem bereits von der API zurückgegebenen Kontextlimit-Fehler zu erholen, wird der zugrunde liegende Fehler angezeigt, und die aktuelle Anfrage schlägt fehl.
Claude Code verwirft die Felder systemMessage und continue eines PreCompact-Hooks.
PreCompact-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PreCompact-Hooks trigger und custom_instructions. Bei manual enthält custom_instructions, was der Benutzer an /compact übergibt, und ist null, wenn nichts übergeben wird. Bei auto ist custom_instructions null.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PreCompact",
"trigger": "manual",
"custom_instructions": null
}
PreCompact-Ausgabe
Wenn ein PreCompact-Hook mit 0 beendet wird und nicht blockiert, verwendet Claude Code seine stdout-Ausgabe als Anweisungen für die Zusammenfassung der Komprimierung. Halten Sie diese stdout-Ausgabe pro Hook unter 10.000 Zeichen. Claude Code schneidet längere Ausgabe an dieser Grenze ab und hängt eine Markierung an.
PostCompact
Wird ausgeführt, nachdem Claude Code eine Komprimierung abgeschlossen hat. Verwenden Sie dieses Ereignis, um auf den neuen komprimierten Zustand zu reagieren, etwa um die generierte Zusammenfassung zu protokollieren oder externen Zustand zu aktualisieren. Claude Code verwirft die Felder systemMessage und continue eines PostCompact-Hooks.
Es gelten dieselben Matcher-Werte wie für PreCompact:
| Matcher | Wann er ausgelöst wird |
|---|---|
manual |
Nach /compact |
auto |
Nach der automatischen Komprimierung, wenn die Konversation das Fenster für die automatische Komprimierung erreicht, sowie nach der Leerlauf-Komprimierung |
PostCompact-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PostCompact-Hooks trigger und compact_summary. Das Feld compact_summary enthält die von der Komprimierung generierte Zusammenfassung der Konversation.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PostCompact",
"trigger": "manual",
"compact_summary": "Summary of the compacted conversation..."
}
PostCompact-Hooks haben keine Entscheidungskontrolle. Sie können das Ergebnis der Komprimierung nicht beeinflussen, aber Folgeaufgaben ausführen.
PreModelSwitch
Wird ausgeführt, bevor Claude Code einen Modellwechsel anwendet, den Sie oder ein Client angefordert haben. Verwenden Sie ihn, um einen Wechsel zu blockieren, eine Bestätigung zu verlangen oder vorab anzuzeigen, was der Wechsel kosten wird.
PreModelSwitch erfordert Claude Code v2.1.251 oder höher. Claude Code führt ihn bei diesen Anfragen aus:
/model <name>und die/model-Auswahl- Die Modellauswahl über
Option+PoderAlt+P - Die Einstellung „Model“ in
/config - Das Einschalten des Fast-Modus, wenn dies das Modell der Sitzung ändert
- Eine
set_model-Anfrage oder eine Modelländerung in einerapply_flag_settings-Anfrage von einem Agent SDK-Host oder von Remote Control
Claude Code führt PreModelSwitch-Hooks nicht für Wechsel aus, die es selbst vornimmt, etwa einen automatischen Modell-Fallback oder die Wiederherstellung des Modells beim Fortsetzen einer Sitzung. Diese Änderungen erreichen nur PostModelSwitch.
Claude Code vergleicht den Matcher mit dem kanonischen Namen des Modells, zu dem die Sitzung wechselt, und ignoriert dabei ein eventuelles [1m]-Suffix. Ein Alias wie opus, eine datierte Modell-ID und eine anbieterspezifische ID wie eine Amazon-Bedrock-Modell-ID passen alle auf den einen kanonischen Namen, zu dem sie aufgelöst werden, sodass claude-opus-5 jede Schreibweise von Opus 5 abdeckt.
Wenn Claude Code keinen kanonischen Namen für das Ziel ermitteln kann, etwa bei einer benutzerdefinierten Modell-ID, die nur Ihr LLM-Gateway kennt, führt es jeden PreModelSwitch-Hook unabhängig vom Matcher aus. Ein blockierender Hook sollte daher to_model aus seiner Eingabe prüfen, statt sich allein auf den Matcher zu verlassen.
Schreiben Sie den Matcher als exakten Namen, als durch | getrennte Liste wie claude-opus-4-6|claude-opus-5 oder als regulären Ausdruck wie .*opus.*. Dieses Beispiel verwendet einen Matcher mit exaktem Namen und prüft zusätzlich to_model aus der Hook-Eingabe. So lehnt es einen Wechsel zu Opus 4.6 ab, indem es mit Code 2 beendet wird, und lässt jedes andere Ziel durch:
[tab] macOS/Linux
Der Befehl prüft
to_modelmitjq:{ "hooks": { "PreModelSwitch": [ { "matcher": "claude-opus-4-6", "hooks": [ { "type": "command", "command": "jq -e '.to_model | test(\"opus-4-6\")' > /dev/null && { echo 'Opus 4.6 is retired for this project. Use a newer model.' >&2; exit 2; }; exit 0" } ] } ] } }
[tab] Windows (PowerShell)
Registrieren Sie einen Command-Hook, der ein Skript über PowerShell ausführt:
{ "hooks": { "PreModelSwitch": [ { "matcher": "claude-opus-4-6", "hooks": [ { "type": "command", "command": "powershell.exe", "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-opus-46.ps1" ] } ] } ] } }Speichern Sie dieses Skript unter
.claude/hooks/block-opus-46.ps1in Ihrem Projekt:$hookInput = [Console]::In.ReadToEnd() | ConvertFrom-Json if ($hookInput.to_model -match 'opus-4-6') { [Console]::Error.WriteLine('Opus 4.6 is retired for this project. Use a newer model.') exit 2 } exit 0
Um zu prüfen, ob der Hook funktioniert, führen Sie /model claude-opus-4-6 in einer Sitzung aus, die ein anderes Modell verwendet. Claude Code behält das aktuelle Modell bei und meldet, dass ein PreModelSwitch-Hook den Wechsel blockiert hat, mit Ihrer Nachricht als Grund.
PreModelSwitch-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten PreModelSwitch-Hooks die Felder in dieser Tabelle. Die letzten fünf beschreiben, was das erneute Senden der Konversation an das neue Modell kostet, sodass ein Hook diesen Betrag vor dem Wechsel anzeigen kann.
| Feld | Typ | Beschreibung |
|---|---|---|
from_model |
string | Modell-ID, von der gewechselt wird |
to_model |
string | Modell-ID, zu der gewechselt wird. Der Matcher wird mit dem kanonischen Namen dieses Modells verglichen |
requested_model |
string oder null |
Das in der Anfrage genannte Modell: ein Alias wie opus, eine vollständige Modell-ID oder null, wenn das Standardmodell angefordert wurde |
source |
string | Woher die Anfrage stammt: "command" für /model <name>, die Einstellung „Model“ in /config oder das Einschalten des Fast-Modus; "picker" für eine Modellauswahl; "sdk" für eine set_model-Anfrage oder eine Modelländerung in einer apply_flag_settings-Anfrage von einem Agent-SDK-Host oder von Remote Control |
context_tokens |
number | Token, die die nächste Anfrage erneut als Prompt sendet: Eingabe-, Cache-Lese-, Cache-Erstellungs- und Ausgabe-Token der letzten Antwort in der Hauptkonversation zusammengenommen. 0 vor der ersten Antwort |
prompt_cache_warm |
boolean | Ob der Prompt-Cache des aktuellen Modells wahrscheinlich noch warm ist, sodass er durch den Wechsel verloren geht |
cache_ttl |
string | Lebensdauer des Prompt-Caches der Hauptkonversation zum Zeitpunkt dieses Wechsels: "5m" oder "1h" |
estimated_cache_write_usd |
number | Geschätzte Kosten in US-Dollar für das Schreiben von context_tokens in den Prompt-Cache von to_model zum cache_ttl-Tarif, ohne die nächste Antwort. Der Server muss möglicherweise nicht den gesamten Kontext erneut zwischenspeichern, betrachten Sie den Wert daher als Schätzung |
pricing |
string | Wie Claude Code estimated_cache_write_usd berechnet hat: "configured" zu den eigenen Tarifen Ihrer Organisation, sofern diese konfiguriert sind, "catalog" zum Listenpreis oder "default", wenn für to_model kein Preis bekannt ist und Claude Code einen Standardtarif angenommen hat |
Dieses Beispiel zeigt die Eingabe für /model opus in einer Sitzung mit Sonnet 5:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "PreModelSwitch",
"from_model": "claude-sonnet-5",
"to_model": "claude-opus-5",
"requested_model": "opus",
"source": "command",
"context_tokens": 182340,
"prompt_cache_warm": true,
"cache_ttl": "5m",
"estimated_cache_write_usd": 1.1396,
"pricing": "catalog"
}
PreModelSwitch-Entscheidungskontrolle
PreModelSwitch-Hooks können den Wechsel abbrechen, den Benutzer um Bestätigung bitten oder ihn durchführen lassen. Exit-Code 2 oder ein decision: "block" auf oberster Ebene bricht den Wechsel ab.
Für eine feinere Steuerung geben Sie permissionDecision und permissionDecisionReason in einem hookSpecificOutput-Objekt zurück, wie bei PreToolUse. PreModelSwitch akzeptiert "allow", "deny" und "ask". Es akzeptiert weder "defer" noch updatedInput oder additionalContext. Die folgende Tabelle beschreibt beide Felder:
| Feld | Beschreibung |
|---|---|
permissionDecision |
"allow" führt den Wechsel durch und überspringt die Bestätigung, die Claude Code anzeigt, solange der Prompt-Cache warm ist. "deny" bricht den Wechsel ab. "ask" bittet den Benutzer um Bestätigung |
permissionDecisionReason |
Bei "deny" wird er dem Benutzer als Grund für die Blockierung angezeigt oder bei einer set_model-Anfrage als Fehler zurückgegeben. Bei "ask" wird er in der Bestätigungsabfrage angezeigt. Bei "allow" wird er ignoriert |
Nur /model in einer interaktiven Sitzung kann die "ask"-Abfrage anzeigen. Auf allen anderen Oberflächen, einschließlich des nicht interaktiven Modus mit dem Flag -p, /config und set_model-Anfragen, behandelt Claude Code "ask" als Ablehnung.
Dieses Beispiel bittet den Benutzer um Bestätigung und nennt die Token-Anzahl aus context_tokens:
{
"hookSpecificOutput": {
"hookEventName": "PreModelSwitch",
"permissionDecision": "ask",
"permissionDecisionReason": "Switching now re-sends about 180k tokens to the new model. Continue?"
}
}
Wenn mehrere PreModelSwitch-Hooks unterschiedliche Entscheidungen zurückgeben, gilt die Rangfolge deny > ask > allow.
Claude Code zeigt dem Benutzer jede systemMessage, die Ihr Hook zurückgibt, unabhängig von der Entscheidung an. Ein Hook, der Kosten meldet, kann also {"systemMessage": "..."} zurückgeben und mit 0 beendet werden.
PreModelSwitch-Timeouts und andere Exit-Codes
Ein PreModelSwitch-Hook, der nicht vor Ablauf seines Timeouts antwortet, blockiert den Wechsel. Der Standard-Timeout für dieses Ereignis beträgt 30 Sekunden.
Ein anderer Exit-Code als 0 oder 2 ohne gültige JSON-Ausgabe ist ein nicht blockierender Fehler, der Wechsel findet also statt, außer bei Hooks, deren Fehlschlagen ihn blockiert.
PostModelSwitch
Wird ausgeführt, nachdem sich das Modell der Sitzung geändert hat. Verwenden Sie ihn, um Claude modellspezifische Hinweise zu geben, ohne jede CLAUDE.md zu bearbeiten, etwa eine organisationsweite Anweisung, die für bestimmte Modelle gilt.
PostModelSwitch erfordert Claude Code v2.1.251 oder höher. Er kann nicht blockieren, da sich das Modell bereits geändert hat. Claude Code führt PostModelSwitch-Hooks nach jeder dieser Änderungen aus:
- Ein von Ihnen oder einem Client angeforderter Wechsel
- Ein automatischer Modell-Fallback, der das Modell der Sitzung ändert
- Eine Einstellung wie
opusplan, die den Plan-Modus betritt oder verlässt - Claude Code stellt das Modell wieder her, wenn Sie eine Sitzung fortsetzen
Claude Code führt PostModelSwitch-Hooks nicht aus, wenn ein Modell aus einer Fallback-Modellkette einen Turn bedient, da diese Ersetzung nur einen Turn dauert und das Modell der Sitzung unverändert lässt.
Der Matcher folgt denselben Regeln wie bei PreModelSwitch: Claude Code vergleicht ihn mit dem kanonischen Namen des Modells, zu dem die Sitzung gewechselt hat.
Dieses Beispiel fügt Hinweise hinzu, sobald das Modell der Sitzung zu einem beliebigen Opus-Modell wechselt:
{
"hooks": {
"PostModelSwitch": [
{
"matcher": ".*opus.*",
"hooks": [
{
"type": "command",
"command": "echo 'On Opus, delegate implementation work to subagents and keep this conversation for planning and review.'"
}
]
}
]
}
}
Um zu prüfen, ob der Hook funktioniert, wechseln Sie aus einer Sitzung mit einem anderen Modell zu einem Opus-Modell, führen Sie also zum Beispiel /model opus in einer Sonnet-Sitzung aus, und fragen Sie Claude dann, welche Hinweise es zum aktuellen Modell hat.
PostModelSwitch-Eingabe
PostModelSwitch-Hooks erhalten dieselben Felder wie PreModelSwitch, wobei hook_event_name auf "PostModelSwitch" gesetzt ist, und zwei weitere source-Werte: "auto" für einen automatischen Fallback oder eine andere Änderung, die Claude Code selbst vorgenommen hat, und "resume" für das beim Fortsetzen einer Sitzung wiederhergestellte Modell.
requested_model ist null, wenn source den Wert "auto" hat. Wenn source den Wert "resume" hat, ist es die gespeicherte Modelleinstellung, die Claude Code wiederhergestellt hat.
PostModelSwitch-Entscheidungskontrolle
Claude Code übernimmt bei Exit 0 die reine Textausgabe auf stdout Ihres Hooks oder additionalContext aus der JSON-Ausgabe und übergibt sie Claude mit der nächsten Anfrage nach dem Wechsel. Zusätzlich zu den JSON-Ausgabefeldern, die allen Hooks zur Verfügung stehen, können Sie Folgendes zurückgeben:
| Feld | Beschreibung |
|---|---|
additionalContext |
String, der Claudes Kontext mit der nächsten Anfrage hinzugefügt wird. Siehe Kontext für Claude hinzufügen |
Wenn der Hook nicht innerhalb von fünf Sekunden nach dem Senden Ihres nächsten Prompts fertig ist, sendet Claude Code diese Anfrage ohne die Ausgabe und hängt sie stattdessen an die darauffolgende Anfrage an. Wenn sich das Modell vor der nächsten Anfrage mehrmals ändert, übergibt Claude Code nur die Ausgabe für das Zielmodell des letzten Wechsels.
SessionEnd
Wird ausgeführt, wenn eine Claude-Code-Sitzung endet. Nützlich für Aufräumarbeiten, das Protokollieren von Sitzungsstatistiken oder das Speichern des Sitzungszustands. Unterstützt Matcher zum Filtern nach dem Beendigungsgrund.
Das Feld reason in der Hook-Eingabe gibt an, warum die Sitzung beendet wurde:
| Grund | Beschreibung |
|---|---|
clear |
Sitzung mit dem Befehl /clear geleert |
resume |
Sitzung über interaktives /resume gewechselt |
logout |
Benutzer hat sich abgemeldet |
prompt_input_exit |
Benutzer hat beendet, während die Prompt-Eingabe sichtbar war |
other |
Andere Beendigungsgründe |
bypass_permissions_disabled |
In v2.1.234 entfernt; Claude Code sendet ihn nicht. Entfernen Sie ihn aus Ihren SessionEnd-Matchern |
SessionEnd-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten SessionEnd-Hooks ein Feld reason, das angibt, warum die Sitzung beendet wurde. Alle Werte finden Sie in der Tabelle der Gründe oben.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "SessionEnd",
"reason": "other"
}
SessionEnd-Hooks haben keine Entscheidungskontrolle. Sie können das Beenden der Sitzung nicht blockieren, aber Aufräumarbeiten ausführen. Claude Code verwirft ihre JSON-Ausgabefelder, etwa systemMessage.
SessionEnd-Hooks haben einen Standard-Timeout von 1,5 Sekunden. Er gilt, wenn Sie beenden, /clear ausführen oder mit interaktivem /resume die Sitzung wechseln. Sie können einem Hook auf zwei Arten mehr Zeit geben:
timeoutpro Hook: Setzen Sietimeoutin der Konfiguration dieses Hooks. Das Gesamtbudget steigt automatisch auf den höchstentimeout-Wert pro Hook in Ihren Einstellungsdateien, bis zu 60 Sekunden. Wenn Sie das Budget auf diese Weise erhöhen, behält ein Hook ohne eigenentimeoutweiterhin den Standardwert. Timeouts, die für von Plugins bereitgestellte Hooks gesetzt sind, erhöhen das Budget nicht.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: Setzen Sie diese Umgebungsvariable in Millisekunden, um das Budget explizit zu überschreiben. Der gesetzte Wert wird außerdem zum Timeout für jeden Hook ohne eigenentimeout.
Dieses Beispiel setzt das Budget auf 5 Sekunden:
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude
Vor v2.1.268 erhöhte CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS nur das Gesamtbudget, und ein Hook ohne eigenen timeout wurde weiterhin nach 1,5 Sekunden abgebrochen.
Elicitation
Wird ausgeführt, wenn ein MCP-Server mitten in einer Aufgabe eine Benutzereingabe anfordert. Standardmäßig zeigt Claude Code einen interaktiven Dialog an, in dem der Benutzer antworten kann. Hooks können diese Anfrage abfangen und programmgesteuert beantworten, wodurch der Dialog vollständig übersprungen wird.
Einen vollständigen Hook mit Einstellungseintrag und Skript finden Sie unter Eine Formularanfrage per Skript beantworten.
Das Matcher-Feld wird mit dem Namen des MCP-Servers abgeglichen.
Elicitation-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten Elicitation-Hooks mcp_server_name, message sowie die optionalen Felder mode, url, elicitation_id und requested_schema.
Für eine Elicitation im Formularmodus, den häufigsten Fall:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please provide your credentials",
"mode": "form",
"requested_schema": {
"type": "object",
"properties": {
"username": { "type": "string", "title": "Username" }
}
}
}
Für eine Elicitation im URL-Modus, die für browserbasierte Authentifizierung verwendet wird:
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "Elicitation",
"mcp_server_name": "my-mcp-server",
"message": "Please authenticate",
"mode": "url",
"url": "https://auth.example.com/login"
}
Elicitation-Ausgabe
Ein Elicitation-Hook kann die Anfrage für den Benutzer beantworten, sie ablehnen oder abbrechen oder sie dem Dialog überlassen. Um zu antworten, abzulehnen oder abzubrechen, beenden Sie mit 0 und geben ein hookSpecificOutput-Objekt mit einer action aus. Der Server erhält Ihre Antwort, und es erscheint kein Dialog. Jede Zeile dieser Tabelle zeigt, was Sie für ein Ergebnis zurückgeben und was der MCP-Server erhält:
| Ziel | Rückgabe | Der Server erhält |
|---|---|---|
| Für den Benutzer antworten | "action": "accept", mit den Formularfeldwerten in content |
accept mit Ihrem content |
| Die Anfrage ablehnen | "action": "decline" |
decline |
| Die Anfrage abbrechen | "action": "cancel" |
cancel |
| Die Anfrage dem Benutzer überlassen | Keine Ausgabe, mit Exit-Code 0 | Die Antwort des Benutzers aus dem Dialog |
Diese Ausgabe beantwortet die unter Elicitation-Eingabe gezeigte Anfrage im Formularmodus. Die Schlüssel in content sind die Eigenschaftsnamen aus dem requested_schema dieser Anfrage:
{
"hookSpecificOutput": {
"hookEventName": "Elicitation",
"action": "accept",
"content": {
"username": "alice"
}
}
}
Diese Ausgabe lehnt eine Anfrage ab:
{
"hookSpecificOutput": {
"hookEventName": "Elicitation",
"action": "decline"
}
}
Im Dialog sendet die Auswahl von Decline decline und das Drücken von Esc sendet cancel. Geben Sie also den Wert zurück, den der Server sehen soll.
Bei einer Anfrage im URL-Modus überspringt ein Hook, der accept zurückgibt, den Dialog, sodass die URL nie geöffnet wird.
Claude Code verwirft reason, systemMessage und continue aus der JSON-Ausgabe eines Elicitation-Hooks, unabhängig davon, welche action Sie zurückgeben.
Weitere Möglichkeiten, eine Elicitation abzulehnen
Ihr Hook kann auch auf diese Weise ablehnen. Der Server erhält dasselbe decline wie bei "action": "decline":
- Beenden mit Code 2: Claude Code ignoriert ein vom selben Hook ausgegebenes
hookSpecificOutput - Ausgabe eines
"decision": "block"auf oberster Ebene: Die Blockierung überschreibt eineactionin derselben Ausgabe
Wenn mehrere Hooks auf dieselbe Anfrage passen, überschreibt eine Ablehnung durch einen von ihnen ein accept oder cancel eines anderen.
Dieses Skript lehnt Anfragen im URL-Modus ab und überlässt Formularanfragen dem Dialog:
#!/bin/bash
if [ "$(jq -r '.mode')" = "url" ]; then
exit 2
fi
Weder der Benutzer noch der Server sieht, warum Ihr Hook abgelehnt hat, da Claude Code weder Ihre stderr-Ausgabe noch Ihren reason anzeigt.
Claude Code ignorierte eine decision auf oberster Ebene von Elicitation- und ElicitationResult-Hooks ab v2.1.105 bis zur Korrektur in v2.1.284.
Eine Formularanfrage per Skript beantworten
Dieses Beispiel beantwortet eine wiederkehrende Frage für den Benutzer. Ein MCP-Server namens issue-tracker fragt in einem Formular nach einem Projektschlüssel, und der Hook trägt DOCS ein. Das Skript akzeptiert, wenn project_key das einzige Feld des Formulars ist. Bei jeder anderen Anfrage gibt es nichts aus, sodass der Dialog erscheint.
[tab] macOS/Linux
Registrieren Sie in Ihrer Einstellungsdatei einen Command-Hook für das Ereignis, mit dem Servernamen als Matcher:
{ "hooks": { "Elicitation": [ { "matcher": "issue-tracker", "hooks": [ { "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.sh", "args": [] } ] } ] } }Speichern Sie dieses Skript unter
.claude/hooks/answer-project-key.shin Ihrem Projekt und machen Sie es mitchmod +xausführbar:#!/bin/bash input=$(cat) fields=$(jq -c '.requested_schema.properties // {} | keys' <<<"$input") if [ "$fields" = '["project_key"]' ]; then jq -n '{hookSpecificOutput: {hookEventName: "Elicitation", action: "accept", content: {project_key: "DOCS"}}}' fi
[tab] Windows (PowerShell)
Registrieren Sie einen Command-Hook, der das Skript über PowerShell ausführt, mit dem Servernamen als Matcher:
{ "hooks": { "Elicitation": [ { "matcher": "issue-tracker", "hooks": [ { "type": "command", "command": "powershell.exe", "args": [ "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/answer-project-key.ps1" ] } ] } ] } }Speichern Sie dieses Skript unter
.claude/hooks/answer-project-key.ps1in Ihrem Projekt:$request = [Console]::In.ReadToEnd() | ConvertFrom-Json $fields = @($request.requested_schema.properties.PSObject.Properties.Name) if ($fields.Count -eq 1 -and $fields[0] -eq 'project_key') { @{ hookSpecificOutput = @{ hookEventName = "Elicitation" action = "accept" content = @{ project_key = "DOCS" } } } | ConvertTo-Json -Depth 3 }
Um zu prüfen, ob der Hook funktioniert, starten Sie Claude Code mit claude --debug und geben Claude eine Aufgabe, bei der der Server nach dem Projektschlüssel fragt. Es erscheint kein Dialog, und das Debug-Log enthält eine Zeile, die mit Elicitation resolved by hook: {"action":"accept","content":{"project_key":"DOCS"}} endet.
ElicitationResult
Wird ausgeführt, nachdem ein Benutzer auf eine MCP-Elicitation geantwortet hat. Hooks können die Antwort beobachten, ändern oder blockieren, bevor sie an den MCP-Server zurückgesendet wird.
Wenn ein Elicitation-Hook eine Anfrage beantwortet, sendet Claude Code diese Antwort an den Server, ohne ElicitationResult-Hooks auszuführen.
Das Matcher-Feld wird mit dem Namen des MCP-Servers abgeglichen.
ElicitationResult-Eingabe
Zusätzlich zu den gemeinsamen Eingabefeldern erhalten ElicitationResult-Hooks mcp_server_name, action sowie die optionalen Felder mode, elicitation_id und content.
{
"session_id": "abc123",
"transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
"cwd": "/Users/...",
"hook_event_name": "ElicitationResult",
"mcp_server_name": "my-mcp-server",
"action": "accept",
"content": { "username": "alice" },
"mode": "form"
}
ElicitationResult-Ausgabe
Ein ElicitationResult-Hook kann die Antwort des Benutzers durchlassen, ihre Werte ändern oder sie blockieren. Um die Antwort zu ändern oder zu blockieren, beenden Sie mit 0 und geben ein hookSpecificOutput-Objekt mit einer action aus. Jede Zeile dieser Tabelle zeigt, was Sie für ein Ergebnis zurückgeben und was der MCP-Server erhält:
| Ziel | Rückgabe | Der Server erhält |
|---|---|---|
| Die Antwort durchlassen | Keine Ausgabe, mit Exit-Code 0 | Die Antwort des Benutzers, unverändert |
| Die übermittelten Werte ändern | "action": "accept", mit den neuen Werten in content |
accept mit Ihrem content anstelle der Werte des Benutzers |
| Die Antwort blockieren | "action": "decline" |
decline, ohne die Werte des Benutzers |
| Die Anfrage abbrechen | "action": "cancel" |
cancel, zusammen mit den Werten, die der Benutzer übermittelt hat. Um sie zurückzuhalten, geben Sie "decline" zurück |
Diese Ausgabe ändert die unter ElicitationResult-Eingabe gezeigte Antwort, sodass der Server alice@example.com erhält, wo der Benutzer alice übermittelt hat:
{
"hookSpecificOutput": {
"hookEventName": "ElicitationResult",
"action": "accept",
"content": {
"username": "alice@example.com"
}
}
}
Ihr content ersetzt das gesamte content-Objekt des Benutzers, also fügen Sie auch die Felder ein, die Sie nicht ändern. Geben Sie action mit zurück, da Claude Code ein hookSpecificOutput ohne action ignoriert.
ElicitationResult-Hooks werden auch ausgeführt, wenn der Benutzer ablehnt oder abbricht, und Ihre action ersetzt die des Benutzers. Prüfen Sie, ob die action der Eingabe accept ist, bevor Sie accept zurückgeben, sonst macht Ihr Hook aus einer abgelehnten Anfrage eine akzeptierte. Dieses Skript nimmt dieselbe Änderung vor, wenn der Benutzer akzeptiert hat, behält die anderen Felder bei und gibt andernfalls nichts aus:
#!/bin/bash
input=$(cat)
if [ "$(jq -r '.action' <<<"$input")" = "accept" ]; then
jq '{hookSpecificOutput: {hookEventName: "ElicitationResult", action: "accept", content: (.content + {username: (.content.username + "@example.com")})}}' <<<"$input"
fi
Diese Ausgabe blockiert die Antwort:
{
"hookSpecificOutput": {
"hookEventName": "ElicitationResult",
"action": "decline"
}
}
Exit-Code 2 und ein "decision": "block" auf oberster Ebene blockieren die Antwort ebenfalls. Unter Weitere Möglichkeiten, eine Elicitation abzulehnen erfahren Sie, welche Variante wirksam wird, wenn ein Hook sie kombiniert, was der Benutzer sieht und welche Versionen decision ignoriert haben.
Claude Code verwirft reason, systemMessage und continue aus der JSON-Ausgabe eines ElicitationResult-Hooks, unabhängig davon, welche action Sie zurückgeben.
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):
PermissionDeniedPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
PermissionRequest unterstützt command, http, mcp_tool und prompt Hooks, aber keine agent Hooks. Wenn Sie einen Agent-Hook bei diesem Ereignis konfigurieren, überspringt Claude Code ihn und der Genehmigungsfluss wird unverändert fortgesetzt. Um von einem Hook aus zuzulassen oder zu verweigern, geben Sie das Entscheidungsobjekt von einem Command- oder HTTP-Hook zurück.
Ereignisse, die command, http und mcp_tool Hooks unterstützen, aber nicht prompt oder agent:
ConfigChangeCwdChangedDirectoryAddedElicitationElicitationResultFileChangedInstructionsLoadedMessageDisplayNotificationPostCompactPostModelSwitchPreCompactPreModelSwitchSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart und Setup unterstützen command und mcp_tool Hooks, und MCP-Tool-Hook-Felder beschreibt, wann ihre mcp_tool Hooks ausgeführt werden. 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 das Modell, das Claude Code für Hintergrundfunktionalität verwendet
- Das LLM antwortet mit strukturiertem JSON, das eine Entscheidung enthält
- Claude Code verarbeitet die Entscheidung automatisch
Prompt-Hook-Konfiguration
Setzen Sie type 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.
In einem Prompt- oder Agent-Hook können Sie den prompt als Regel formulieren, was blockiert oder zugelassen werden soll, etwa „Blockiere jeden Bash-Befehl, der .env-Dateien liest“, oder als Bedingung, die erfüllt sein muss, etwa „Alle Unit-Tests bestehen“.
Dieser Stop-Hook fragt das LLM, ob Claude stoppen sollte, bevor Claude beendet wird:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
}
]
}
]
}
}
| Feld | Erforderlich | Beschreibung |
|---|---|---|
type |
ja | Muss "prompt" sein |
prompt |
ja | Der Prompt-Text zum Senden an das LLM. Verwenden Sie $ARGUMENTS als Platzhalter für die Hook-Eingabe JSON. Wenn $ARGUMENTS nicht vorhanden ist, wird die Eingabe JSON an den Prompt angehängt |
model |
nein | Modell zur Verwendung für die Evaluierung. Standardwert ist das Modell, das Claude Code für Hintergrundfunktionalität verwendet |
timeout |
nein | Timeout in Sekunden. Standard: 30 |
continueOnBlock |
nein | Bei den Ereignissen, auf die es zutrifft, speist true einen ok: false Grund an Claude zurück und setzt den Turn fort, anstatt ihn zu beenden. Standard: false. Siehe Response-Schema für ereignisspezifisches Verhalten |
Response-Schema
Das LLM muss mit JSON antworten, das Folgendes enthält:
{
"ok": true | false,
"reason": "Explanation for the decision",
"impossible": true | false
}
| Feld | Beschreibung |
|---|---|
ok |
true erlaubt die Aktion. Bei false siehe das ereignisspezifische Verhalten unten |
reason |
Erforderlich, wenn ok false ist |
impossible |
Optional. Das Modell gibt es mit ok: false zurück, wenn es beurteilt, dass die Bedingung niemals erfüllt werden kann. Bei Stop und SubagentStop lässt Claude Code dann den Turn enden, anstatt den Grund zurückzugeben. Agent-Hooks und andere Ereignisse ignorieren es |
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 fortgesetzt, es sei denn, die Antwort setzt auchimpossible: true, in welchem Fall Claude Code den Stop zulässt und der Turn endetPreToolUse: der Tool-Aufruf wird verweigert; standardmäßig endet der Turn und der Verweigerungsgrund wird im Chat als Warnzeile angezeigt. Setzen SiecontinueOnBlock: true, um den Grund stattdessen an Claude als Tool-Fehler zurückzugeben, damit es anpassen und fortfahren kann, äquivalent zu 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 voncontinuePostToolUseFailureundTaskCreated: der Grund wird an Claude als Tool-Fehler zurückgegeben und der Turn wird fortgesetzt, unabhängig voncontinueOnBlockTaskCompleted: wenn es ausgelöst wird, weil eine Aufgabe während eines Turns als abgeschlossen markiert wird, wird der Grund an Claude als Tool-Fehler zurückgegeben und der Turn wird fortgesetzt, unabhängig voncontinueOnBlock. Wenn es ausgelöst wird, weil ein Teammate stoppt, verhält es sich wieTeammateIdleund stoppt den Teammate standardmäßigTeammateIdle: 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
Wenn Sie eine feinere Kontrolle bei einem Ereignis benötigen, verwenden Sie einen Command-Hook mit den ereignisspezifischen Feldern, die in Entscheidungskontrolle beschrieben sind.
Mehrere Bedingungen vor dem Stoppen überprüfen
Dieser Stop-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 das Modell "ok": false zurückgibt, weil die Bedingung noch nicht erfüllt ist, setzt Claude die Arbeit mit dem bereitgestellten Grund als nächste Anweisung fort:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
"timeout": 30
}
]
}
]
}
}
Agent-basierte Hooks
[!WARNING]
Agent-Hooks sind experimentell. Das Verhalten und die Konfiguration können sich in zukünftigen Versionen ändern. Für Produktions-Workflows bevorzugen Sie Command 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, mit Ausnahme von PermissionRequest.
Wie Agent-Hooks funktionieren
Wenn ein Agent-Hook ausgelöst wird:
- Claude Code spawnt einen Subagenten mit Ihrem Prompt und der Hook-Eingabe JSON
- 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 erlaubt die Aktion, wenn
oktrueist. Wennokfalseist, verarbeitet Claude Code die Blockierung auf die gleiche Weise wie ein Prompt-Hook mitcontinueOnBlock: truebei diesem Ereignis, wie unter Response-Schema aufgelistet
Agent-Hooks sind nützlich, wenn die Überprüfung das Überprüfen tatsächlicher Dateien oder Test-Ausgabe erfordert, nicht nur die Evaluierung der Hook-Eingabedaten allein.
Agent-Hook-Konfiguration
Setzen Sie type auf "agent" und geben Sie eine prompt-Zeichenkette an, wobei Sie $ARGUMENTS als Platzhalter für die Hook-Eingabe JSON verwenden. Die Konfigurationsfelder sind die gleichen wie Prompt-Hooks, mit der Ausnahme, dass Agent-Hooks ein längeres Standard-Timeout von 60 Sekunden haben und kein continueOnBlock-Feld haben.
Das Response-Schema ist { "ok": true } zum Zulassen oder { "ok": false, "reason": "..." } zum Blockieren. Bei ok: false verarbeitet Claude Code einen Agent-Hook auf die gleiche Weise wie einen Prompt-Hook mit continueOnBlock: true bei demselben Ereignis; Agent-Hooks haben kein continueOnBlock-Feld und unterstützen nicht das Prompt-Hook-Feld impossible.
Dieser Stop-Hook überprüft, dass alle Unit-Tests bestanden sind, bevor Claude fertig ist:
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
"timeout": 120
}
]
}
]
}
}
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 ausgeführt wird. Wenn das Skript fertig ist, wird seine Ausgabe beim nächsten Gesprächsturn geliefert:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "/path/to/run-tests.sh",
"async": true
}
]
}
]
}
}
Sobald ein asynchroner Hook im Hintergrund ausgeführt wird, erzwingt Claude Code kein timeout darauf. Claude Code erzwingt immer noch timeout auf einem Hook, den Sie mit asyncRewake ausführen.
Claude Code liefert die Ergebnisse eines asynchronen Hooks nur während der Sitzung:
- Im nicht-interaktiven Modus mit dem Flag
-pbeendet Claude Code jeden noch laufenden asynchronen Hook beim Herunterfahren und finalisiert ihn mit dem Ergebniscancelled - Wenn die Arbeit Ihres Hooks eine
claude -p-Sitzung überdauern muss, starten Sie einen vollständig abgelösten Prozess davon
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, liefert Claude Code die Felder additionalContext und systemMessage aus der JSON-Antwort des Hooks beim nächsten Gesprächsturn an Claude, mit Ausnahme von wiederholter SessionStart-Ausgabe. Im Gegensatz zur systemMessage eines synchronen Hooks wird Ihnen keines dieser Felder angezeigt.
Geben Sie die JSON-Antwort allein auf stdout oder in einer eigenen Zeile aus:
- Allein auf stdout: Wenn die Antwort der einzige Text auf stdout ist, kann sie sich über mehrere Zeilen erstrecken, etwa bei formatierter
jq-Ausgabe. Mehrzeilige Antworten erfordern Claude Code v2.1.295 oder höher. - In einer eigenen Zeile: Ein asynchroner Hook kann anderen Text auf stdout ausgeben, wenn die Antwort allein in eine Zeile passt, zum Beispiel mit
jq -c.
Claude Code validiert diese JSON-Antwort 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.
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:
#!/bin/bash
# run-tests-async.sh
# Hook-Eingabe von stdin lesen
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
# Tests nur für Quelldateien ausführen
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
exit 0
fi
# Tests ausführen und Ergebnisse über additionalContext an Claude melden
RESULT=$(npm test 2>&1)
EXIT_CODE=$?
if [ $EXIT_CODE -eq 0 ]; then
MSG="Tests passed after editing $FILE_PATH"
else
MSG="Tests failed after editing $FILE_PATH: $RESULT"
fi
jq -nc --arg msg "$MSG" '{hookSpecificOutput: {hookEventName: "PostToolUse", additionalContext: $msg}}'
Fügen Sie dann diese Konfiguration zu .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:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/run-tests-async.sh",
"args": [],
"async": true
}
]
}
]
}
}
Einschränkungen
Asynchrone Hooks haben zusätzliche Einschränkungen im Vergleich zu synchronen Hooks:
- 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.
- Ein asynchroner
SessionStart-Hook wird jedes Mal ausgeführt, wenn das Ereignis ausgelöst wird. Claude Code überspringt die Ausgabe des Hooks, wenn der Text vonadditionalContextundsystemMessagemit dem übereinstimmt, was Claude zuletzt von diesem Hook erhalten hat, es sei denn, ein synchronerSessionStart-Hook hat seitdem anderen Text hinzugefügt. Claude Code liefert den Text, wenn er sich ändert, und erneut bei der nächsten Ausführung des Hooks nach einer Komprimierung oder/clear. Vor v2.1.295 fügte Claude Code denselben Text bei jeder Wiederaufnahme erneut zur Konversation hinzu.
Sicherheitsüberlegungen
Haftungsausschluss
[!WARNING]
Command-Hooks führen Shell-Befehle mit Ihren vollständigen Benutzerberechtigungen aus. Sie können alle Dateien ändern, löschen oder zugreifen, auf die Ihr Benutzerkonto zugreifen kann. Überprüfen und testen Sie alle Hook-Befehle, bevor Sie sie zu Ihrer Konfiguration hinzufügen.
Workspace-Vertrauen
Claude Code überprüft das Workspace-Vertrauen, bevor es einen Hook aus einer Einstellungsdatei ausführt. Was als vertrauenswürdig gilt, hängt vom Sitzungstyp ab:
- Interaktive Sitzung: Claude Code hält Hooks aus jeder Einstellungsdatei zurück, einschließlich Ihrer eigenen
~/.claude/settings.json, bis Sie den Workspace-Vertrauensdialog für den Ordner oder für ein übergeordnetes Verzeichnis, dessen Vertrauen sich darauf erstreckt, akzeptieren -poder SDK-Sitzung: Claude Code zeigt den Dialog nie an und behandelt den Ordner als vertrauenswürdig, sodass Hooks, die in der.claude/settings.jsoneines Repositorys committed sind, in einem Ordner ausgeführt werden, dem Sie nie vertraut haben
Bevor Sie claude -p über ein Repository ausführen, das Sie nicht geschrieben haben, überprüfen Sie seine .claude/-Einstellungsdateien, starten Sie mit --bare, oder deaktivieren Sie Hooks für diesen Durchlauf mit --settings '{"disableAllHooks": true}'. Frontmatter-Hooks in einem Projekt-Subagent folgen einer strengeren Regel als Einstellungsdatei-Hooks. Was vor dem Vertrauen eines Ordners ausgeführt wird listet jede Art von Repository-Inhalt nach Sitzungstyp auf.
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. 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.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "Write-Host 'File written'"
}
]
}
]
}
}
Skripte in PowerShell-Hooks über ihren Pfad referenzieren
Um auf das Projektverzeichnis aus einem PowerShell-Shell-Form-Befehl zu verweisen, schreiben Sie ${CLAUDE_PROJECT_DIR} oder $env:CLAUDE_PROJECT_DIR. Claude Code schreibt 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 einem 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.
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:
{
"type": "command",
"shell": "powershell",
"command": "& \"$env:CLAUDE_PROJECT_DIR\\.claude\\hooks\\check.ps1\""
}
Debug-Hooks
Hook-Ausführungsdetails werden in die Debug-Log-Datei geschrieben. Starten Sie Claude Code mit claude --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.
Beispielsweise erzeugt ein PostToolUse-Hook auf Write, dessen Befehl hook-ran ausgibt, Einträge wie:
2026-07-19T02:03:24.382Z [DEBUG] Hook output does not start with {, treating as plain text
2026-07-19T02:03:24.382Z [DEBUG] "Hook PostToolUse:Write (PostToolUse) success:\nhook-ran"
Um einen langsamen Hook zu finden, durchsuchen Sie das Log nach Hooks:-Zeilen, die mit einer Dauer enden. In Claude Code v2.1.296 oder höher hinterlässt jeder Befehls-Hook bei einem Tool-Ereignis, bei UserPromptSubmit, SessionStart, Stop und mehreren anderen Ereignissen eine solche Zeile, wenn er beendet ist, unabhängig davon, was er ausgegeben hat. Die Zeile enthält den Ereignisnamen, per Doppelpunkt verbunden mit dem Tool-Namen oder einem anderen Wert, auf den der Hook gematcht hat, dann den Befehl des Hooks in eckigen Klammern, das Plugin, aus dem er stammt (falls vorhanden), wie der Lauf endete und wie lange er dauerte, etwa Hooks: PostToolUse:Write [.claude/hooks/log-write.sh] finished with status 0 (31ms). Ein Lauf kann auch mit timed out after <N>ms, cancelled, moved to the background oder failed to start enden. Bei einigen Ereignissen wie Notification, SessionEnd und PreCompact hinterlässt ein Befehls-Hook stattdessen eine completed with status-Zeile ohne Dauer.
Für granularere Hook-Matching-Details setzen Sie 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.