Cycle de vie des hooks
Claude Code exécute les hooks à des points spécifiques pendant une session. Lorsqu’un événement se déclenche et qu’un matcher correspond, Claude Code transmet le contexte JSON de l’événement à votre gestionnaire de hook. Pour les hooks de commande, l’entrée arrive sur stdin. Pour les hooks HTTP, elle arrive dans le corps de la requête POST. Votre gestionnaire peut alors inspecter l’entrée, prendre une action et éventuellement retourner une décision. Les événements se déclenchent selon trois cadences :- une fois par session :
SessionStartetSessionEnd - une fois par tour :
UserPromptSubmit,StopetStopFailure - à chaque appel d’outil à l’intérieur de la boucle agentique :
PreToolUseetPostToolUse, sauf les appelsEndConversation, qui ignorent les deux
Comment un hook se résout
Pour voir comment l’événement, le matcher et le gestionnaire s’assemblent, considérez ce hookPreToolUse qui bloque les commandes shell destructrices.
- macOS/Linux
- Windows (PowerShell)
Le Le script lit l’entrée JSON depuis stdin, extrait la commande et retourne une Ce script, comme les autres exemples Bash sur cette page qui analysent l’entrée JSON, utilise
matcher se limite aux appels d’outil Bash et la condition if se limite davantage aux sous-commandes Bash correspondant à rm *, donc block-rm.sh ne s’exécute que lorsque les deux filtres correspondent :permissionDecision de "deny" si elle contient rm -rf. Enregistrez-le dans .claude/hooks/block-rm.sh dans votre projet et rendez-le exécutable avec chmod +x .claude/hooks/block-rm.sh pour que Claude Code puisse l’exécuter :jq, donc installez jq et assurez-vous qu’il se trouve sur votre PATH avant de les essayer.Bash "rm -rf /tmp/build" par rapport à la configuration macOS/Linux. Voici ce qui se passe :
1
L'événement se déclenche
L’événement
PreToolUse se déclenche. Claude Code envoie l’entrée de l’outil en JSON sur stdin au hook :2
Le matcher vérifie
Le matcher
"Bash" correspond au nom de l’outil, donc ce groupe de hook s’active. Si vous omettez le matcher ou utilisez "*", le groupe s’active à chaque occurrence de l’événement.3
La condition if vérifie
La condition
if "Bash(rm *)" correspond car rm -rf /tmp/build est une sous-commande correspondant à rm *, donc ce gestionnaire s’exécute. Si la commande avait été npm test, la vérification if échouerait et block-rm.sh ne s’exécuterait jamais, évitant la surcharge de génération de processus. Le champ if est optionnel ; sans lui, chaque gestionnaire du groupe correspondant s’exécute.4
Le gestionnaire de hook s'exécute
Le script inspecte la commande complète et trouve Si la commande avait été une variante plus sûre de
rm -rf, donc il imprime une décision sur stdout :rm comme rm file.txt, le script aurait atteint exit 0 à la place. Un code de sortie 0 sans sortie signifie que le hook n’a pas de décision à signaler, donc l’appel d’outil continue à travers le flux de permission normal. Le hook peut refuser l’appel, mais rester silencieux ne l’approuve pas.5
Claude Code agit sur le résultat
Claude Code lit la décision JSON, bloque l’appel d’outil et montre la raison à Claude.
Configuration
Les hooks sont définis dans les fichiers de paramètres JSON. La configuration a trois niveaux d’imbrication :- Choisissez un événement de hook auquel répondre, comme
PreToolUseouStop - Ajoutez un groupe de matcher pour filtrer quand il se déclenche, comme « uniquement pour l’outil Bash »
- Définissez un ou plusieurs gestionnaires de hook à exécuter lorsqu’il y a correspondance
Cette page utilise des termes spécifiques pour chaque niveau : événement de hook pour le point du cycle de vie, groupe de matcher pour le filtre et gestionnaire de hook pour la commande shell, le point de terminaison HTTP, l’outil MCP, le prompt ou l’agent qui s’exécute. « Hook » seul fait référence à la fonctionnalité générale.
Emplacements des hooks
L’endroit où vous définissez un hook détermine sa portée :
Les sessions cloud sur Claude Code sur le web ne lisent pas votre
~/.claude/settings.json local. Dans un environnement auto-hébergé, Claude Code exécute également les hooks que l’opérateur a ensemencés à partir du ~/.claude/ de l’hôte du runner, et il exécute les hooks dans le fichier de paramètres gérés de l’image du runner lorsque ce fichier figure parmi les sources gérées que Claude Code applique, ce qui par défaut signifie uniquement lorsque ni les paramètres gérés par le serveur ni une politique Claude Code livrée par MDM ne fournissent le niveau géré. Consultez ce qui se transfère de votre configuration pour savoir quels fichiers de paramètres et plugins, et donc quels hooks, atteignent une session cloud.
Pour plus de détails sur la résolution des fichiers de paramètres, consultez paramètres.
Les hooks des fichiers de paramètres, des paramètres de politique gérée et des plugins s’exécutent également à l’intérieur des subagents. Lorsqu’un subagent appelle un outil, les événements d’outil tels que PreToolUse et PostToolUse déclenchent les mêmes hooks configurés que dans la conversation principale, et l’entrée porte les champs d’entrée communs agent_id et agent_type qui identifient le subagent.
Les administrateurs peuvent utiliser allowManagedHooksOnly dans les paramètres gérés pour restreindre les hooks qui s’exécutent :
- Vos hooks utilisateur, projet, local et plugin sont bloqués. Les hooks des plugins forcément activés dans les paramètres gérés
enabledPluginssont exempts - Claude Code restreint également votre
statusLine,fileSuggestionetsubagentStatusLineaux paramètres gérés - Claude Code désactive également les plugins avec une source
command, y compris les plugins forcément activés dans les paramètres gérésenabledPlugins, sauf sidisableCommandPluginSourcesest explicitement défini àfalse. Les sourcescommandnécessitent Claude Code v2.1.229 ou ultérieur - Claude Code bloque également les commandes
headersHelperdu marketplace sauf sidisableCommandPluginSourcesest explicitement défini àfalse, sauf pour un marketplace que les paramètres gérés eux-mêmes déclarent
allowManagedHooksOnly.
Les entrées de hook fusionnent entre les niveaux de paramètres plutôt que de se remplacer mutuellement : les paramètres utilisateur, projet et local ajoutent leurs propres hooks sans supprimer les hooks gérés, et le paramètre disableAllHooks ne peut pas désactiver les hooks gérés en dehors des paramètres gérés.
Les listes blanches de hooks HTTP s’appliquent aux hooks de chaque source, y compris les paramètres de politique gérée :
allowedHttpHookUrls: lorsqu’il est défini à n’importe quel niveau de paramètres, Claude Code exécute un gestionnaire de hook HTTP uniquement si son URL correspond à la liste blanche fusionnéehttpHookAllowedEnvVars: lorsqu’il est défini, Claude Code n’interpose que les variables d’environnement de cette liste dans les en-têtes de hook
Modèles de matcher
Le champmatcher filtre quand les hooks se déclenchent. La façon dont un matcher est évalué dépend des caractères qu’il contient :
Un matcher sur le chemin de l’expression régulière est testé avec
RegExp.prototype.test de JavaScript, qui réussit sur une correspondance n’importe où dans la valeur. Edit.* correspond à la fois à Edit et à NotebookEdit ; enveloppez le modèle dans ^ et $, comme dans ^Edit$, lorsque vous avez besoin d’une correspondance de chaîne entière.
FileChanged et StopFailure utilisent un ensemble de correspondance exacte plus étroit contenant uniquement des lettres, des chiffres, _ et |. Un trait d’union, un espace ou une virgule dans un matcher pour ces deux événements le maintient sur le chemin de l’expression régulière, et seul | sépare les alternatives. Tous les autres événements avec support de matcher dans le tableau qui suit acceptent | ou ,.
L’événement FileChanged ne suit pas ces règles lors de la construction de sa liste de surveillance. Consultez FileChanged.
Chaque type d’événement correspond sur un champ différent :
Correspondre à
StopFailure sur cloud_credential_error nécessite Claude Code v2.1.267 ou ultérieur, la première version qui signale les échecs de chargement des identifiants sous cette valeur plutôt que server_error ou unknown.
Pour la plupart des événements, Claude Code évalue le matcher par rapport à un champ de l’entrée JSON qu’il envoie à votre hook sur stdin. Pour les événements d’outil, ce champ est tool_name. Pour PreModelSwitch et PostModelSwitch, Claude Code évalue le matcher par rapport au nom canonique qu’il dérive de to_model, comme décrit sous PreModelSwitch. Chaque section événement de hook liste l’ensemble complet des valeurs de matcher et le schéma d’entrée pour cet événement.
Cet exemple exécute un script de linting uniquement lorsque Claude écrit ou édite un fichier :
matcher à un événement sans support de matcher, il est silencieusement ignoré.
Pour les événements d’outil, vous pouvez filtrer plus étroitement en définissant le champ if sur les gestionnaires de hook individuels. if utilise la syntaxe des règles de permission pour correspondre au nom de l’outil et aux arguments ensemble, donc "Bash(git *)" s’exécute lorsqu’une sous-commande quelconque de l’entrée Bash correspond à git * et "Edit(*.ts)" s’exécute uniquement pour les fichiers TypeScript.
Correspondre aux outils MCP
Les outils du serveur MCP apparaissent comme des outils réguliers dans les événements d’outil (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), vous pouvez donc les faire correspondre de la même manière que tout autre nom d’outil.
Les outils MCP suivent le modèle de nommage mcp__<server>__<tool>, par exemple :
mcp__memory__create_entities: outil de création d’entités du serveur Memorymcp__filesystem__read_file: outil de lecture de fichier du serveur Filesystemmcp__github__search_repositories: outil de recherche du serveur GitHub
.* au préfixe du serveur. Le .* est requis : un matcher comme mcp__memory ou mcp__brave-search contient uniquement des caractères de correspondance exacte, donc il est comparé comme une chaîne exacte et ne correspond à aucun outil.
mcp__memory__.*correspond à tous les outils du serveurmemorymcp__brave-search__.*correspond à tous les outils d’un serveur dont le nom contient un trait d’unionmcp__.*__write.*correspond à tout outil dont le nom commence parwritede n’importe quel serveur
mcp__plugin_<plugin-name>_<server-name>__<tool>. Un matcher écrit contre la clé de serveur nue ne se déclenche jamais pour ces outils. Pour un plugin nommé my-plugin qui regroupe un serveur sous la clé db, un outil query apparaît comme mcp__plugin_my-plugin_db__query, donc le matcher pour chaque outil de ce serveur est mcp__plugin_my-plugin_db__.*. Utilisez le même nom d’outil limité dans le champ if d’un gestionnaire. Consultez Serveurs MCP fournis par un plugin pour savoir comment le nom limité est construit.
Cet exemple enregistre toutes les opérations du serveur memory et valide les opérations d’écriture de n’importe quel serveur MCP :
Champs du gestionnaire de hook
Chaque objet du tableauhooks interne est un gestionnaire de hook : la commande shell, le point de terminaison HTTP, l’outil MCP, le prompt LLM ou l’agent qui s’exécute lorsque le matcher correspond. Il y a cinq types :
- Hooks de commande (
type: "command") : exécutent une commande shell. Votre script reçoit l’entrée JSON de l’événement sur stdin et communique les résultats via les codes de sortie et stdout. - Hooks HTTP (
type: "http") : envoient l’entrée JSON de l’événement en tant que requête HTTP POST à une URL. Le point de terminaison communique les résultats via le corps de la réponse en utilisant le même format de sortie JSON que les hooks de commande. - Hooks de l’outil MCP (
type: "mcp_tool") : appellent un outil sur un serveur MCP configuré. La sortie textuelle de l’outil est traitée comme stdout d’un hook de commande. - Hooks de prompt (
type: "prompt") : envoient un prompt à un modèle Claude pour une évaluation en un seul tour. Le modèle retourne sa décision en JSON. Consultez Hooks basés sur des prompts. - Hooks d’agent (
type: "agent") : lancent un subagent qui peut utiliser des outils comme Read, Grep et Glob pour vérifier les conditions avant de retourner une décision. Les hooks d’agent sont expérimentaux et peuvent changer. Consultez Hooks basés sur des agents.
$CLAUDE_CODE_REMOTE est "true" dans les environnements web distants et n’est pas définie dans le CLI local. Claude Code v2.1.199 et ultérieur définit $CLAUDE_CODE_BRIDGE_SESSION_ID à l’ID de session Contrôle à distance tandis que la session locale a une connexion Contrôle à distance active.
Champs communs
Ces champs s’appliquent à tous les types de hooks :
Le champ
if contient exactement une règle de permission. Il n’y a pas de syntaxe &&, || ou de liste pour combiner les règles ; pour appliquer plusieurs conditions, définissez un gestionnaire de hook séparé pour chacune.
Dans une condition if pour un outil de fichier, un modèle de répertoire à un seul segment comme "Edit(src/**)" correspond uniquement au répertoire src dans le répertoire de travail et aux fichiers sous celui-ci. Pour correspondre à un répertoire nommé src à n’importe quelle profondeur, écrivez "Edit(**/src/**)". Avant v2.1.214, "Edit(src/**)" correspondait à un répertoire nommé src à n’importe quelle profondeur sous le répertoire de travail.
Pour les modèles Bash, le fait que votre commande de hook s’exécute dépend de la forme du modèle et de la commande Bash que Claude invoque. Les affectations VAR=value en début sont supprimées avant la correspondance.
Lorsque Claude Code ne peut pas déterminer quelles commandes l’entrée Bash exécute, il exécute votre hook indépendamment du modèle. Parce que le filtre
if est au mieux un effort, utilisez le système de permission plutôt qu’un hook pour appliquer une autorisation ou un refus strict.
Champs des hooks de commande
En plus des champs communs, les hooks de commande acceptent ces champs :
Un hook de commande s’exécute en forme exec lorsque
args est défini, et en forme shell lorsque args est omis. Définissez args chaque fois que le hook référence un placeholder de chemin, puisque chaque élément est passé comme un argument sans guillemets. Omettez args lorsque vous avez besoin de fonctionnalités shell comme les pipes ou &&, ou lorsqu’aucune de ces préoccupations ne s’applique.
Forme exec s’exécute lorsque args est présent. Claude Code résout command comme un exécutable sur PATH et le lance directement avec args comme vecteur d’arguments. Il n’y a pas de shell, donc chaque élément args est un argument exactement tel qu’écrit, et les placeholders de chemin comme ${CLAUDE_PLUGIN_ROOT} sont substitués dans command et dans chaque élément args comme des chaînes brutes. Les caractères spéciaux tels que les apostrophes, $ et les backticks passent verbatim car il n’y a pas de shell pour les interpréter. Aucune tokenisation shell ne se produit sur aucune plateforme.
Forme shell s’exécute lorsque args est absent. La chaîne command est passée à un shell : sh -c sur macOS et Linux, Git Bash sur Windows, ou PowerShell lorsque Git Bash n’est pas installé. Définissez le champ shell pour choisir explicitement. Le shell tokenise la chaîne, développe les variables et interprète les pipes, &&, les redirections et les globs.
Sur Windows, la forme exec nécessite que
command se résolve en un véritable exécutable tel qu’un .exe. Les shims .cmd et .bat que npm, npx, eslint et d’autres outils installent dans node_modules/.bin ne sont pas des exécutables et ne peuvent pas être lancés sans un shell. Pour les exécuter en forme exec, invoquez le script sous-jacent avec node directement, par exemple "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. Le modèle node plus chemin de script fonctionne sur chaque plateforme car node.exe est un vrai binaire. Pour exécuter un shim .cmd ou .bat par nom, utilisez la forme shell.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT et CLAUDE_PLUGIN_DATA sur le processus lancé, donc un script peut lire process.env.CLAUDE_PLUGIN_ROOT indépendamment de la façon dont il a été lancé.
Les hooks de plugin substituent également les valeurs ${user_config.*}, en forme exec uniquement : la valeur est substituée dans command et dans chaque élément args comme une chaîne brute, donc aucun shell ne la réanalyse.
Un hook de plugin en forme shell dont la command référence ${user_config.*} échoue avec une erreur au lieu de s’exécuter. Pour utiliser une valeur d’option à partir d’un hook en forme shell, lisez la variable d’environnement $CLAUDE_PLUGIN_OPTION_<KEY>, comme $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL pour une option webhook_url, ou définissez args pour basculer le hook en forme exec. Avant v2.1.207, les commandes de hook de plugin en forme shell substituaient également ${user_config.*}.
En forme exec,
command est uniquement le nom ou le chemin de l’exécutable. Si command est un nom nu sans séparateur de chemin et contient des espaces aux côtés de args, Claude Code enregistre un avertissement car le lancement échouera : il n’y a pas d’exécutable nommé node script.js. Déplacez les tokens supplémentaires dans args. Les chemins absolus avec des espaces, tels que C:\Program Files\nodejs\node.exe, sont un seul exécutable valide et ne déclenchent pas l’avertissement.Champs des hooks HTTP
En plus des champs communs, les hooks HTTP acceptent ces champs :
Claude Code envoie l’entrée JSON du hook en tant que corps de la requête POST avec
Content-Type: application/json. Le corps de la réponse utilise le même format de sortie JSON que les hooks de commande.
La gestion des erreurs diffère des hooks de commande ; consultez Gestion des réponses HTTP.
Cet exemple envoie les événements PreToolUse à un service de validation local, en s’authentifiant avec un token de la variable d’environnement MY_TOKEN :
Champs des hooks de l’outil MCP
En plus des champs communs, les hooks de l’outil MCP acceptent ces champs :
Cet exemple appelle l’outil
security_scan sur le serveur MCP my_server après chaque Write ou Edit, en passant le chemin du fichier édité :
isError: true, le hook produit une erreur non-bloquante et l’exécution continue.
Sur les événements où un hook peut bloquer ou changer le résultat, tels que PreToolUse ou Stop, Claude Code attend qu’un serveur se connecte avant d’appeler l’outil, pendant au maximum MCP_TIMEOUT et dans le timeout du hook lui-même. Sur les événements observationnels, tels que Notification ou SessionEnd, il n’attend pas.
Un serveur affichant le statut cached se connecte lorsque le hook appelle son outil. Si le serveur n’est pas connecté à ce moment, le hook produit une erreur non-bloquante et l’exécution continue. Le hook ne démarre jamais un flux OAuth, donc authentifiez le serveur à partir de /mcp d’abord.
SessionStart au lancement, y compris avec --continue ou --resume, et chaque événement Setup se déclenchent avant que les serveurs MCP de la session ne soient disponibles pour les hooks. Claude Code ignore leurs hooks mcp_tool sans appeler l’outil, et le journal de débogage enregistre mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), ou le même message nommant Setup. Lorsque SessionStart se déclenche à nouveau plus tard dans la session, après /clear ou une compaction, ses hooks mcp_tool s’exécutent. Pour tout ce dont la session a besoin au lancement, utilisez un hook type: "command" sur SessionStart à la place.
Champs des hooks de prompt et d’agent
En plus des champs communs, les hooks de prompt et d’agent acceptent ces champs :Référencer les scripts par chemin
Utilisez ces placeholders pour référencer les scripts de hook par rapport à la racine du projet ou du plugin, indépendamment du répertoire de travail lorsque le hook s’exécute :${CLAUDE_PROJECT_DIR}: la racine du projet où la session a démarré. Claude Code définit également cette variable dans l’environnement des serveurs MCP stdio et des serveurs LSP de plugin.${CLAUDE_PLUGIN_ROOT}: le répertoire d’installation du plugin, pour les scripts fournis avec un plugin. Consultez variables d’environnement du plugin pour savoir comment le chemin se comporte lors des mises à jour.${CLAUDE_PLUGIN_DATA}: le répertoire de données persistantes du plugin, pour les dépendances et l’état qui doivent survivre aux mises à jour du plugin.
Les worktrees sont différents. Si Claude entre dans un worktree pendant la session, Claude Code garde
${CLAUDE_PROJECT_DIR} où il était et passe le chemin du worktree à vos hooks d’une manière différente :${CLAUDE_PROJECT_DIR}reste en place : il pointe toujours vers la racine du projet où la session a démarré, donc une commande comme${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.shexécute toujours le script dans le checkout principal.cwdsuit Claude : le champcwddans l’entrée JSON du hook est la racine du worktree après que Claude entre dans un worktree, et le nouveau répertoire après que Claude exécutecd. Lisez-le lorsqu’un hook a besoin de savoir dans quel répertoire Claude travaille.
- Scripts de projet
- Scripts de plugin
Cet exemple utilise
${CLAUDE_PROJECT_DIR} pour exécuter un vérificateur de style à partir du répertoire .claude/hooks/ du projet après tout appel d’outil Write ou Edit :Hooks dans les skills et agents
En plus des fichiers de paramètres et des plugins, les hooks peuvent être définis directement dans les skills et les subagents en utilisant le frontmatter, dans le même format de configuration que les hooks basés sur les paramètres. La durée pendant laquelle Claude Code les garde enregistrés dépend du composant :- Hooks de subagent : Claude Code les exécute uniquement pendant que ce subagent s’exécute et les supprime lorsqu’il se termine. Claude Code convertit un hook
Stopici enSubagentStop, l’événement qu’il déclenche lorsqu’un subagent se termine. - Hooks de skill : Claude Code les enregistre lorsque vous ou Claude invoquez le skill et continue à les exécuter pour le reste de la session, sur les tours après le tour du skill lui-même. Pour que Claude Code supprime un hook après sa première exécution réussie à la place, définissez
once: truesur celui-ci.
PreToolUse qui exécute un script de validation de sécurité avant chaque commande Bash :
-p dans un dossier que vous n’avez pas approuvé.
Les hooks de frontmatter dans un subagent de projet s’exécutent uniquement après que vous acceptiez le dialogue de confiance de l’espace de travail pour le dossier d’où provient le fichier de l’agent. Une session -p ne compte pas comme l’accepter. Ce qui s’exécute avant que vous approuviez un dossier compare cela avec la règle du fichier de paramètres, et la page des subagents liste quels scopes sont exempts. Avant v2.1.218, ces hooks pouvaient s’exécuter à partir de dossiers que vous n’aviez pas approuvés.
Le menu /hooks
Tapez /hooks dans Claude Code pour ouvrir un navigateur en lecture seule pour vos hooks configurés. La liste étiquette chaque hook avec sa provenance, comme les paramètres utilisateur, les paramètres du projet, les paramètres locaux, un plugin ou la session actuelle.
Sélectionnez un hook pour voir le texte complet de ce qu’il exécute et l’endroit où il est défini, comme le chemin de son fichier de paramètres ou le nom de son plugin.
Pour parcourir tous les événements de hook, y compris ceux pour lesquels aucun hook n’est configuré, sélectionnez All events à la fin de la liste.
Désactiver ou supprimer les hooks
Pour supprimer un hook défini dans un fichier de paramètres, supprimez son entrée de ce fichier. Pour désactiver temporairement tous les hooks sans les supprimer, définissez"disableAllHooks": true dans votre fichier de paramètres. Claude Code lit la valeur restante après que la précédence des paramètres s’applique, donc un "disableAllHooks": false dans le .claude/settings.json d’un projet remplace un true dans vos paramètres utilisateur. Pour désactiver les hooks pour une exécution quelle que soit la configuration du projet, passez --settings '{"disableAllHooks": true}', qui prend la précédence sur les paramètres du projet et locaux. Il n’y a aucun moyen de désactiver un hook individuel tout en le gardant dans la configuration.
Le paramètre disableAllHooks respecte la hiérarchie des paramètres gérés. Si un administrateur a configuré des hooks via les paramètres de politique gérée, disableAllHooks défini dans les paramètres utilisateur, projet ou local ne peut pas désactiver ces hooks gérés. Seul disableAllHooks défini au niveau des paramètres gérés peut désactiver les hooks gérés. Pour la portée complète de chaque niveau, consultez disableAllHooks.
Les éditions directes des hooks dans les fichiers de paramètres sont normalement détectées automatiquement par le moniteur de fichiers.
Entrée et sortie des hooks
Les hooks de commande reçoivent les données JSON via stdin et communiquent les résultats via les codes de sortie, stdout et stderr. Les hooks HTTP reçoivent le même JSON que le corps de la requête POST et communiquent les résultats via le corps de la réponse HTTP. Cette section couvre les champs et le comportement communs à tous les événements. Chaque section d’événement sous Événements de hook inclut son schéma d’entrée spécifique et les options de contrôle de décision. Sur macOS et Linux, les hooks de commande s’exécutent dans leur propre session sans terminal de contrôle. Le processus de hook et tous les processus enfants ne peuvent pas ouvrir/dev/tty ou envoyer des séquences d’échappement directement à l’interface Claude Code. Windows n’a pas de /dev/tty.
Pour afficher un message à l’utilisateur sur n’importe quelle plateforme, retournez systemMessage dans la sortie JSON. Certains événements le rejettent ou le livrent ailleurs, et chaque section d’événement le précise. Pour déclencher une notification de bureau, définir un titre de fenêtre ou sonner la cloche, retournez terminalSequence à la place.
Champs d’entrée communs
Les événements de hook reçoivent ces champs en JSON, en plus des champs spécifiques à l’événement documentés dans chaque section événement de hook. Pour les hooks de commande, ce JSON arrive via stdin. Pour les hooks HTTP, il arrive dans le corps de la requête POST.
Lors de l’exécution avec
--agent ou à l’intérieur d’un subagent, deux champs supplémentaires sont inclus :
Seuls les hooks
SessionStart peuvent recevoir un champ model, et Claude Code ne l’inclut pas toujours. Les hooks PreModelSwitch et PostModelSwitch reçoivent from_model et to_model à la place, utilisez donc un hook PostModelSwitch pour suivre le modèle au fur et à mesure qu’il change pendant une session.
Il n’y a pas de variable d’environnement $CLAUDE_MODEL. Le hook peut lire $ANTHROPIC_MODEL si vous la définissez dans votre shell, mais cette valeur ne change pas lorsque vous changez de modèle avec /model pendant une session.
Un processus de hook hérite de l’environnement parent, à l’exception des variables d’exportateur OTEL_* que Claude Code supprime de chaque sous-processus qu’il génère et, lorsque CLAUDE_CODE_SUBPROCESS_ENV_SCRUB est défini sur 1, les variables qu’il supprime.
Par exemple, un hook PreToolUse pour une commande Bash reçoit ceci sur stdin :
tool_name, tool_input et tool_use_id sont spécifiques à l’événement. Chaque section événement de hook documente les champs supplémentaires pour cet événement.
Sortie du code de sortie
Le code de sortie de votre commande de hook indique à Claude Code si l’action doit procéder, être bloquée ou être ignorée. Le code de sortie n’agit pas seul. Claude Code lit les champs de sortie JSON depuis stdout sur chaque code de sortie, pas seulement 0, et pour les événements qui utilisent le modèle de décision standard, un objet analysé qui passe la validation du schéma prend effet aux côtés du code. Le blocage d’exit 2 est le seul résultat que JSON ne peut pas remplacer. Deux tableaux possèdent les exceptions par événement : Comportement du code de sortie 2 par événement dit ce que les codes de sortie font pour chaque événement, et Contrôle de décision dit quels champs de décision chaque événement honore. Les champs universels tels quesystemMessage fonctionnent sur la plupart des événements et sont listés dans le tableau Sortie JSON.
Exit code 0
Exit 0 signifie succès, et c’est le code de sortie prévu lorsque vous imprimez JSON pour un contrôle structuré. Pour la plupart des événements, Claude Code écrit stdout dans le journal de débogage et ne l’affiche pas dans la transcription. Les exceptions sontUserPromptSubmit, UserPromptExpansion, SessionStart et PostModelSwitch, où Claude Code ajoute stdout en texte brut comme contexte que Claude peut voir et sur lequel agir.
Que Claude Code lise votre stdout comme sortie JSON ou comme texte brut dépend de la façon dont il commence et se termine, en ignorant les espaces blancs environnants :
- Commence par
{et se termine par}: Claude Code l’analyse comme JSON. Lorsque la sortie est deux lignes ou plus qui s’analysent chacune comme JSON seules, et aucune ligne n’est un objet sortie JSON qui définit un champ, Claude Code traite la sortie entière comme du texte brut. Lorsque l’une de ces lignes définit un champ, la sortie entière est un échec d’analyse, décrit ci-dessous. - Commence par
{mais ne se termine pas par}: Claude Code le traite comme du texte brut. - Commence par n’importe quoi d’autre : Claude Code le traite comme du texte brut, un tableau JSON ou une chaîne JSON entre guillemets incluse.
<hook name> hook error avec le message de validation. La même chose se produit sur tout code de sortie autre que 2, tandis que exit 2 bloque toujours.
Pour les événements qui utilisent le modèle de décision standard, lorsque Claude Code essaie d’analyser votre stdout comme JSON et ne peut pas, il rapporte une erreur non-bloquante sur chaque code de sortie autre que 2. La transcription affiche un avis <hook name> hook error avec le message d’analyse. Sur les événements qui ajoutent stdout en texte brut comme contexte, Claude Code n’ajoute pas le texte. Avant v2.1.248, Claude Code traitait ce stdout comme du texte brut.
Stderr d’un hook qui quitte 0 va uniquement au journal de débogage, jamais à la transcription, et Claude ne le voit jamais. Pour le lire vous-même, activez la journalisation de débogage. Pour afficher un avertissement à Claude à partir d’un hook PostToolUse ou PostToolUseFailure, quittez 2 à la place afin que Claude voie stderr même si l’outil a déjà s’exécuté.
Exit code 2
Exit 2 signifie une erreur bloquante. Sur les événements qui peuvent bloquer, exit 2 bloque que vous imprimiez JSON ou non : même unepermissionDecision JSON de "allow" ne peut pas la remplacer. Claude Code lit toujours tout sortie JSON valide sur stdout. Sur Elicitation et ElicitationResult, le hookSpecificOutput d’un hook exit-2 est ignoré.
Le message de blocage est la raison de la décision de blocage de votre JSON lorsqu’elle en fait une, et votre texte stderr sinon. Ce que le blocage fait varie selon l’événement : PreToolUse bloque l’appel d’outil, UserPromptSubmit rejette le prompt, et ainsi de suite. Comportement du code de sortie 2 par événement énumère l’effet pour chaque événement, et chaque section d’événement dit où le message va.
Un hook qui quitte 2 tout en imprimant JSON qui échoue la validation du schéma sortie JSON bloque toujours : Claude Code utilise stderr comme raison de blocage et enregistre l’échec de validation dans le journal de débogage. Avant v2.1.214, Claude Code traitait cette combinaison comme une erreur non-bloquante et l’action procédait.
Ce script bloque les commandes rm en quittant 2 et laisse chaque autre commande au flux de permission normal :
Autres codes de sortie
Tout autre code de sortie ne bloque pas seul pour la plupart des événements de hook. Ce qui se passe dépend de votre stdout :- Avec un objet analysé qui passe la validation du schéma, pour les événements qui utilisent le modèle de décision standard, Claude Code ignore le code de sortie et le JSON seul décide du résultat :
- Chaque champ que l’événement supporte est honoré, y compris
permissionDecision,additionalContext,updatedInputetsystemMessage, et le hook n’est pas signalé comme une erreur. - Contrôle de décision énumère les champs de décision par événement ; les champs universels comme
systemMessagesuivent le tableau Sortie JSON.
- Chaque champ que l’événement supporte est honoré, y compris
- Avec un objet analysé qui échoue la validation du schéma, pour les événements qui utilisent le modèle de décision standard, c’est la même erreur non-bloquante que sur exit 0 : l’action procède, et l’avis
<hook name> hook errorporte le message de validation. - Avec stdout que Claude Code essaie d’analyser comme JSON et ne peut pas, Claude Code rapporte la même erreur non-bloquante que sur exit 0 pour les événements qui utilisent le modèle de décision standard. L’action procède, et l’avis porte le message d’analyse.
- Avec stdout que Claude Code traite comme du texte brut, ou avec stdout vide, c’est une erreur non-bloquante pour la plupart des événements de hook : l’action procède, et la transcription affiche un avis
<hook name> hook errorsuivi de la première ligne de stderr, préfixée parFailed with non-blocking status code:. Pour capturer le stderr complet, activez la journalisation de débogage.
WorktreeCreate échoue la création sur tout code de sortie non-zéro peu importe ce que votre JSON dit, et les événements qui rejettent complètement la sortie du hook, comme StopFailure, ignorent votre JSON sur chaque code de sortie, à part les champs d’effet secondaire comme terminalSequence, qui se déclenchent toujours.
Un hook qui ne peut pas démarrer atterrit dans le même bucket non-bloquant. Lorsque le chemin du script n’existe pas ou n’est pas exécutable, le shell quitte avec un code comme 127 et vous voyez le même avis avec le message de l’interpréteur, par exemple Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory. Pour la plupart des événements de hook, l’action procède. Lorsque vous configurez un hook de politique, regardez cet avis à sa première exécution : un chemin mal orthographié dans settings.json laisse la porte silencieusement désactivée.
Délais d’expiration
À l’exception d’un hook de commande que vous exécutez avecasync: true, Claude Code annule un hook command, http ou mcp_tool qui atteint son timeout, en rejetant la sortie du hook, donc sur la plupart des événements un hook expiré ne rend aucune décision.
Sur PreModelSwitch, un hook annulé à son délai d’expiration bloque le changement de modèle. Sur PreToolUse, les deux familles de hooks diffèrent :
- Un hook
command,httpoumcp_toolexpiré ne bloque pas l’appel d’outil. L’appel continue via le flux de permission normal, donc ne comptez pas sur un hook bloqué pour agir comme une porte. - Un hook de rappel Agent SDK qui dépasse son délai d’expiration bloque l’appel d’outil.
Comportement du code de sortie 2 par événement
Exit code 2 est la façon dont un hook signale « arrêtez, ne faites pas cela ». L’effet dépend de l’événement, car certains événements représentent des actions qui peuvent être bloquées (comme un appel d’outil qui ne s’est pas encore produit) et d’autres représentent des choses qui se sont déjà produites ou ne peuvent pas être empêchées.
Pour
SessionStart, SubagentStart et PostModelSwitch, Claude Code rend le stderr du code de sortie 2 dans la transcription comme un avis <hook name> hook error, de la même manière qu’il rend une erreur non-bloquante. Claude ne le voit pas, et la session ou le subagent procède. Pour SubagentStart, l’avis apparaît dans la propre transcription du subagent, pas dans la conversation parent.
Gestion des réponses HTTP
Les hooks HTTP utilisent les codes de statut HTTP et les corps de réponse au lieu des codes de sortie et stdout. Les résultats ci-dessous s’appliquent à la plupart des événements ; un événement avec son propre contrat d’échec dans le tableau par événement, tel queWorktreeCreate, applique ce contrat à un hook HTTP échoué aussi :
- 2xx avec un corps vide : succès, équivalent à exit code 0 sans sortie
- 2xx avec un corps d’objet JSON : analysé en utilisant le même schéma sortie JSON que les hooks de commande. Un corps qui échoue la validation du schéma est une erreur non-bloquante
- 2xx avec n’importe quel autre corps, comme du texte brut : erreur non-bloquante, gérée de la même manière qu’un statut non-2xx. Claude Code n’ajoute pas le texte au contexte de Claude
- Statut non-2xx : erreur non-bloquante, l’exécution continue
- Défaillance de connexion : erreur non-bloquante, l’exécution continue
- Délai d’expiration : le hook est annulé, comme décrit sous Délais d’expiration
Sortie JSON
Les codes de sortie vous permettent uniquement de bloquer ou de rester silencieux, mais la sortie JSON vous donne un contrôle plus granulaire. Au lieu de quitter avec le code 2 pour bloquer, quittez 0 et imprimez un objet JSON sur stdout. Claude Code lit les champs spécifiques de ce JSON pour contrôler le comportement, y compris contrôle de décision pour bloquer, autoriser ou escalader à l’utilisateur.Choisissez une approche par hook : soit utiliser les codes de sortie seuls pour signaler, soit quitter 0 et imprimer JSON pour un contrôle structuré. Si vous les mélangez, exit 2 garde son effet de blocage, et Claude Code lit toujours les champs JSON, avec l’exception d’élicitation unique notée sous Exit code 2.
additionalContext, systemMessage et initialUserMessage, et son stdout brut, sont plafonnées à 10 000 caractères :
- Portée : Claude Code mesure chaque chaîne seule, même lorsque plusieurs hooks s’exécutent pour le même événement. Pour la sortie JSON, chaque champ est mesuré séparément ; stdout brut est mesuré dans son ensemble.
- Au-delà de la limite : Claude Code enregistre la sortie dans un fichier du répertoire de session et la remplace par le chemin du fichier et un aperçu de jusqu’à 2 000 premiers caractères. Un grand résultat Bash valide est géré de la même manière, décrit sous Output limits. Contrairement à ce plafond Bash, ce cap n’a pas de paramètre ou de variable d’environnement pour l’augmenter.
- Lecture du fichier : Claude Code ne demande pas à Claude de lire le fichier, donc gardez tout ce que Claude doit toujours voir dans le cap.
- Champs universels comme
continuesont listés dans le tableau ci-dessous. Chaque événement les accepte, mais certains événements les rejettent ou livrentsystemMessageailleurs que dans la transcription. Chaque section d’événement le précise.terminalSequencefonctionne sur ces événements aussi, avec les exceptions listées sous Émettre des notifications de terminal. decisionetreasonau niveau supérieur sont utilisés par certains événements pour bloquer ou fournir des commentaires.hookSpecificOutputest un objet imbriqué pour les événements qui ont besoin d’un contrôle plus riche. Il nécessite un champhookEventNamedéfini au nom de l’événement.
Pour arrêter Claude entièrement :
PreToolUse et PostToolUse, l’arrêt s’applique même lorsque l’appel d’outil échoue ou se termine tandis que Claude diffuse toujours une réponse.
Émettre des notifications de terminal
Les hooks s’exécutent sans terminal de contrôle, donc écrire des séquences d’échappement directement sur/dev/tty échoue. À la place, retournez la séquence d’échappement dans le champ terminalSequence et Claude Code l’émet pour vous via son propre chemin d’écriture de terminal. C’est sans course, fonctionne à l’intérieur de tmux et GNU screen, et fonctionne sur Windows où il n’y a pas de /dev/tty.
Le champ accepte une chaîne d’une ou plusieurs séquences d’échappement en liste blanche :
- OSC
0,1,2: titres de fenêtre et d’icône - OSC
9: notifications iTerm2, ConEmu, Windows Terminal et WezTerm, y compris la progression de la barre des tâches9;4 - OSC
99: notifications Kitty - OSC
777: notifications urxvt, Ghostty et Warp - BEL nu
systemMessage et continue, tels que Notification et StopFailure. Il a deux limites :
- Claude Code écrit la séquence uniquement dans une session interactive, et uniquement tandis que son interface est à l’écran. En mode non-interactif avec le drapeau
-pet dans l’Agent SDK, il ignore le champ. - Un hook de commande
WorktreeCreatene peut pas retourner JSON, car Claude Code lit son stdout comme le chemin du worktree. Un hook HTTPWorktreeCreateretourne JSON et peut inclure le champ.
Notification. La séquence d’échappement est construite avec des échappements octaux printf afin que les octets de contrôle n’apparaissent jamais sur la ligne de commande shell, et jq -n --arg construit la sortie JSON afin que les guillemets, les barres obliques inverses et les sauts de ligne dans le message de notification soient correctement échappés :
{ "terminalSequence": "..." } est la même à partir de n’importe quel shell ou langage.
Ajouter du contexte pour Claude
Le champadditionalContext transmet une chaîne de votre hook dans la fenêtre de contexte de Claude. Claude Code enveloppe la chaîne dans un rappel système et l’insère dans la conversation au point où le hook s’est déclenché. Claude lit le rappel lors de la prochaine demande du modèle, mais il n’apparaît pas comme un message de chat dans l’interface.
Retournez additionalContext à l’intérieur de hookSpecificOutput aux côtés du nom de l’événement :
- SessionStart et SubagentStart : au début de la conversation, avant le premier prompt
- UserPromptSubmit et UserPromptExpansion : aux côtés du prompt soumis
- PreToolUse, PostToolUse, PostToolUseFailure et PostToolBatch : à côté du résultat de l’outil
- Stop et SubagentStop : à la fin du tour. La conversation continue afin que Claude puisse agir sur les commentaires. Consultez Contrôle de décision Stop
- PostModelSwitch : avec la prochaine demande après le changement. Consultez Contrôle de décision PostModelSwitch pour le timing
additionalContext pour le même événement, Claude reçoit toutes les valeurs.
Si une valeur dépasse 10 000 caractères, Claude Code écrit le texte dans un fichier du répertoire de session et transmet à Claude le chemin du fichier avec un aperçu de jusqu’à 2 000 premiers caractères à la place. Claude peut lire le fichier, mais Claude Code ne le demande pas.
Utilisez additionalContext pour les informations que Claude devrait connaître sur l’état actuel de votre environnement ou l’opération qui vient de s’exécuter :
- État de l’environnement : la branche actuelle, la cible de déploiement ou les drapeaux de fonctionnalité actifs
- Règles de projet conditionnelles : quelle commande de test s’applique au fichier qui vient d’être modifié, quels répertoires sont en lecture seule dans ce worktree
- Données externes : problèmes ouverts qui vous sont assignés, résultats CI récents, contenu récupéré à partir d’un service interne
bun test » se lisent comme des informations de projet. Le texte encadré comme des commandes système hors bande peut déclencher les défenses contre l’injection de prompt de Claude, ce qui amène Claude à vous présenter le texte au lieu de le traiter comme du contexte.
Claude Code enregistre le texte injecté dans la transcription de session. Pour les événements mid-session comme PostToolUse ou UserPromptSubmit, lorsque vous reprenez avec --continue ou --resume, Claude Code rejoue le texte enregistré plutôt que de réexécuter le hook pour les tours passés, de sorte que les valeurs comme les horodatages ou les SHA de commit deviennent obsolètes. Les hooks SessionStart s’exécutent à nouveau à la reprise avec source défini sur "resume", ou "fork" si vous avez ajouté --fork-session, afin qu’ils puissent actualiser leur contexte.
Contrôle de décision
Tous les événements ne supportent pas le blocage ou le contrôle du comportement via JSON. Les événements qui le font utilisent chacun un ensemble différent de champs pour exprimer cette décision. Utilisez ce tableau comme référence rapide avant d’écrire un hook :
Quelques événements peuvent également réécrire le contenu plutôt que seulement l’autoriser ou le bloquer :
PreToolUse:updatedInputdirectement soushookSpecificOutputremplace les arguments d’un outil avant son exécution. Consultez Contrôle de décision PreToolUsePermissionRequest:updatedInputà l’intérieur de l’objetdecision. Consultez Contrôle de décision PermissionRequestPostToolUse:updatedToolOutputremplace le résultat de l’outil. Consultez Contrôle de décision PostToolUseUserPromptSubmit: ne peut pas remplacer le prompt ; injecte uniquementadditionalContextà côté de celui-ci
PreToolUse pour les entrées d’outil sortantes et PostToolUse pour les résultats d’outil entrants.
Voici des exemples de chaque modèle en action :
- Décision au niveau supérieur
- PreToolUse
- PermissionRequest
La seule valeur pour
decision est "block". Pour autoriser l’action à procéder, omettez decision de votre JSON, ou quittez 0 sans aucun JSON :Événements de hook
Chaque événement correspond à un point du cycle de vie de Claude Code où les hooks peuvent s’exécuter. Les sections ci-dessous sont ordonnées pour correspondre au cycle de vie : de la configuration de la session à la boucle agentive jusqu’à la fin de la session. Chaque section décrit quand l’événement se déclenche, quels matchers il supporte, l’entrée JSON qu’il reçoit, et comment contrôler le comportement via la sortie.SessionStart
S’exécute quand Claude Code démarre une nouvelle session ou reprend une session existante. Utile pour charger le contexte de développement comme les problèmes existants ou les modifications récentes de votre base de code, ou pour configurer des variables d’environnement. Pour un contexte statique qui ne nécessite pas de script, utilisez plutôt CLAUDE.md. SessionStart s’exécute à chaque session, donc gardez ces hooks rapides. Seuls les hookstype: "command" et type: "mcp_tool" sont supportés. Voir Champs de hook MCP tool pour savoir quand les hooks mcp_tool s’exécutent.
La valeur du matcher correspond à la façon dont la session a été initiée :
Avant v2.1.214, les sessions créées rapportaient la source
"resume".
Quand vous démarrez une session interactive, reprenez une conversation au lancement avec --continue ou --resume, ou exécutez /clear, les hooks SessionStart s’exécutent en arrière-plan. Vous pouvez taper immédiatement, et une conversation que vous avez reprise apparaît sans attendre les hooks. La première réponse de Claude attend toujours que les hooks se terminent, donc leur contexte atteint Claude.
Quand vous changez de conversation avec /resume dans une session, le changement attend que les hooks se terminent. Si vous exécutez /clear ou changez vers une autre conversation pendant que les hooks en arrière-plan s’exécutent toujours, rien de ce qu’ils retournent ne s’applique à la session.
La même attente s’applique au lancement, y compris une session reprise : une invite que vous envoyez pendant que les hooks SessionStart s’exécutent toujours n’atteint Claude que lorsqu’ils se terminent.
Pendant l’une ou l’autre attente, appuyez sur Esc pour reprendre l’invite dans l’entrée sans l’envoyer. Les hooks continuent de s’exécuter.
Entrée SessionStart
En plus des champs d’entrée communs, les hooks SessionStart reçoiventsource et optionnellement model, agent_type, et session_title :
Une session que vous n’avez pas nommée peut toujours avoir un titre généré. Ce titre n’est pas un titre personnalisé et n’apparaît pas dans
session_title.
Quand source est "resume" ou "fork" et que la transcription contient au moins une réponse de Claude, les hooks SessionStart reçoivent également les quatre champs ci-dessous. Votre hook peut les utiliser pour signaler le coût de reprendre une conversation obsolète avant la première requête, par exemple dans un systemMessage. Ces champs nécessitent Claude Code v2.1.251 ou ultérieur.
Cet exemple montre l’entrée pour une session reprise 90 minutes après sa dernière réponse :
Contrôle de décision SessionStart
Claude Code ajoute la sortie standard qu’il traite comme du texte brut au contexte de Claude. En plus des champs de sortie JSON disponibles pour tous les hooks, vous pouvez retourner ces champs spécifiques à l’événement :sessionTitle.
Utilisez reloadSkills quand un hook SessionStart installe ou met à jour des skills. La découverte de skills s’exécute normalement avant que les hooks SessionStart se terminent, donc les fichiers que le hook écrit dans ~/.claude/skills/ ou .claude/skills/ n’apparaîtraient autrement que dans la session suivante. Cet exemple synchronise un référentiel de skills partagé et demande la réanalyse :
fatal: sur stderr. Stderr d’un hook SessionStart qui quitte 0 est informatif uniquement, donc la demande reloadSkills s’applique toujours.
Persister les variables d’environnement
Les hooks SessionStart ont accès à la variable d’environnementCLAUDE_ENV_FILE, qui fournit un chemin de fichier où vous pouvez persister les variables d’environnement pour les commandes Bash suivantes.
Pour définir des variables d’environnement individuelles, écrivez des instructions export dans CLAUDE_ENV_FILE. Utilisez l’ajout (>>) pour préserver les variables définies par d’autres hooks :
CLAUDE_ENV_FILE est disponible pour les hooks SessionStart, Setup, CwdChanged, et FileChanged. Les autres types de hooks n’ont pas accès à cette variable.Setup
S’exécute uniquement quand vous lancez Claude Code avec--init-only, ou avec --init ou --maintenance en mode non-interactif avec le drapeau -p. Il ne s’exécute pas au démarrage normal. Utilisez-le pour l’installation de dépendances ponctuelles ou le nettoyage programmé que vous déclenchez explicitement à partir de CI ou de scripts, séparé du démarrage normal de la session. Pour l’initialisation par session, utilisez plutôt SessionStart.
La valeur du matcher correspond au drapeau CLI qui a déclenché le hook :
Quand vous exécutez
claude --init-only, Claude Code exécute les hooks Setup et les hooks SessionStart avec le matcher startup, puis quitte sans démarrer une conversation.
Quand vous démarrez ou continuez une conversation avec -p, vous devez également fournir une invite, comme argument ou piped sur stdin. Vous pouvez ignorer l’invite quand un hook SessionStart fournit initialUserMessage ou quand vous reprenez une session avec un appel d’outil différé.
En cas de succès, --init-only n’imprime rien sur le terminal. Pour confirmer que les hooks se sont exécutés, commencez par claude --debug-file <path> --init-only, en remplaçant <path> par un emplacement de fichier journal, et vérifiez le journal pour les entrées de hook Setup et SessionStart.
Parce que Setup ne s’exécute pas à chaque lancement, un plugin qui a besoin d’une dépendance installée ne peut pas compter sur Setup seul. Le modèle pratique est de vérifier la dépendance à la première utilisation et d’installer en cas d’absence, par exemple un hook ou skill qui teste ${CLAUDE_PLUGIN_DATA}/node_modules et exécute npm install s’il est absent. Voir le répertoire de données persistantes pour savoir où stocker les dépendances installées. Si vous distribuez votre plugin via une marketplace, vous n’aurez peut-être pas besoin de ce modèle : Claude Code installe automatiquement les dépendances de package Node.js éligibles quand il met en cache le plugin.
Entrée Setup
En plus des champs d’entrée communs, les hooks Setup reçoivent un champtrigger défini à "init" ou "maintenance" :
Contrôle de décision Setup
Les hooks Setup ne peuvent pas bloquer ; l’exécution continue sur n’importe quel code de sortie. Sur chaque code de sortie, Claude Code rejette les champs de sortie JSON d’un hook Setup, commesystemMessage, continue, et hookSpecificOutput.additionalContext. Avec -p, la sortie standard, stderr, et le code de sortie d’un hook Setup n’apparaissent dans la sortie de la session que comme des événements hook_response quand vous lancez avec --output-format stream-json --verbose.
Les hooks Setup ont accès à CLAUDE_ENV_FILE. Les variables écrites dans ce fichier persistent dans les commandes Bash suivantes pour la session, tout comme dans les hooks SessionStart. Seuls les hooks type: "command" s’exécutent sur Setup. Un hook type: "mcp_tool" sur Setup est toujours ignoré, comme décrit sous Champs de hook MCP tool.
InstructionsLoaded
S’exécute quand un fichierCLAUDE.md ou .claude/rules/*.md est chargé dans le contexte. Cet événement se déclenche au démarrage de la session pour les fichiers chargés avec impatience et à nouveau plus tard quand les fichiers sont chargés avec paresse, par exemple quand Claude accède à un sous-répertoire qui contient un CLAUDE.md imbriqué ou quand les règles conditionnelles avec le frontmatter paths: correspondent. Le hook ne supporte pas le blocage ou le contrôle de décision. Il s’exécute de manière asynchrone à des fins d’observabilité.
Cet événement ne se déclenche pas quand Claude lit AGENTS.md directement via le paramètre Project instructions. Il se déclenche quand un CLAUDE.md importe votre AGENTS.md, avec load_reason défini à include comme pour tout autre fichier importé, et quand CLAUDE.md est un lien symbolique vers lui, comme un chargement normal de CLAUDE.md.
Le matcher s’exécute contre load_reason. Par exemple, utilisez "matcher": "session_start" pour se déclencher uniquement pour les fichiers chargés au démarrage de la session, ou "matcher": "path_glob_match|nested_traversal" pour se déclencher uniquement pour les chargements avec paresse.
Entrée InstructionsLoaded
En plus des champs d’entrée communs, les hooks InstructionsLoaded reçoivent ces champs :Contrôle de décision InstructionsLoaded
Les hooks InstructionsLoaded n’ont pas de contrôle de décision. Ils ne peuvent pas bloquer ou modifier le chargement des instructions. Claude Code rejette leurs champs de sortie JSON, commesystemMessage et continue. Utilisez cet événement pour l’audit logging, le suivi de conformité, ou l’observabilité.
UserPromptSubmit
S’exécute quand l’utilisateur soumet une invite, avant que Claude la traite. Cela vous permet d’ajouter du contexte supplémentaire basé sur l’invite/conversation, de valider les invites, ou de bloquer certains types d’invites. Les hooksUserPromptSubmit ont un délai d’expiration par défaut de 30 secondes pour les types command, http, et mcp_tool, plus court que le défaut de 600 secondes pour ces types sur la plupart des autres événements. Parce que ce hook s’exécute avant chaque invite et bloque le traitement du modèle jusqu’à ce qu’il se termine, un hook bloqué paralyse la session. Si votre hook a besoin de plus de temps, définissez le champ timeout dans l’entrée du hook.
À part un hook de commande que vous exécutez avec async: true, un hook de commande, HTTP, ou MCP tool UserPromptSubmit qui atteint son délai d’expiration est annulé et sa sortie, y compris tout additionalContext, est rejetée. L’invite atteint toujours Claude sans ce contexte. La transcription affiche un avis nommant le hook, le délai d’expiration qui s’est déclenché, et que la sortie a été rejetée.
Un hook de rappel Agent SDK sur UserPromptSubmit qui atteint son délai d’expiration bloque l’invite avec un message nommant le hook et le délai d’expiration, parce qu’un rappel là peut agir comme une porte de politique qui ne doit pas échouer ouvertement. La session continue. Avant v2.1.208, un délai d’expiration de rappel sur cet événement terminait le tour avec une erreur d’exécution.
Entrée UserPromptSubmit
En plus des champs d’entrée communs, les hooks UserPromptSubmit reçoivent le champprompt contenant le texte que l’utilisateur a soumis. Le contenu collé qui s’est effondré en un espace réservé [Pasted text #N] arrive développé en place. Dans les sessions où Claude Code marque le texte collé pour Claude, ce contenu développé se situe entre une ligne <pasted_content id="…"> et une ligne </pasted_content id="…">, donc tenez compte de ces lignes si votre hook analyse l’invite.
Les hooks UserPromptSubmit reçoivent également session_title quand la session a un titre personnalisé, avec la même signification que le champ SessionStart session_title.
Contrôle de décision UserPromptSubmit
Les hooksUserPromptSubmit peuvent contrôler si une invite utilisateur est traitée et ajouter du contexte. Tous les champs de sortie JSON sont disponibles.
Il y a deux façons d’ajouter du contexte à la conversation en cas de code de sortie 0 :
- Sortie standard en texte brut : Claude Code ajoute la sortie standard qu’il traite comme du texte brut au contexte de Claude
- JSON avec
additionalContext: utilisez le format JSON ci-dessous pour plus de contrôle. Le champadditionalContextest ajouté comme contexte
additionalContext sont chacune injectées comme un rappel système qui commence par le nom du hook ; Claude lit les deux. Pour confirmer la livraison, vérifiez le journal de débogage.
Pour bloquer une invite, retournez un objet JSON avec decision défini à "block" :
Un hook qui bloque en quittant 2 s’achemine de la même façon que
reason : le message de blocage montre le texte stderr à l’utilisateur, et il n’est pas ajouté au contexte.
Ce qu’une invite bloquée laisse derrière
Une invite bloquée n’atteint jamais Claude, mais son texte n’est pas supprimé partout. Par défaut, le message de blocage montré à l’utilisateur se termine parOriginal prompt: suivi du texte soumis, et Claude Code écrit ce message dans le fichier de transcription de la session sur le disque. Pour laisser le texte hors du message, imprimez JSON avec "suppressOriginalPrompt": true à l’intérieur de hookSpecificOutput. Cela fonctionne que le hook bloque avec decision: "block" ou en quittant 2. Un hook de sortie 2 qui n’imprime pas JSON obtient toujours le texte d’invite dans son message de blocage.
suppressOriginalPrompt change uniquement le message de blocage. Le texte soumis peut toujours apparaître dans les fichiers locaux comme la transcription de session et votre historique d’invite, donc un hook de blocage n’est pas un moyen de garder un secret hors du disque. Pour limiter ou supprimer ces fichiers, voir Stockage en texte brut et Effacer les données locales.
UserPromptExpansion
S’exécute quand une commande tapée par l’utilisateur se développe en une invite avant d’atteindre Claude. Utilisez ceci pour bloquer des commandes spécifiques de l’invocation directe, injecter du contexte pour une skill particulière, ou enregistrer quelles commandes les utilisateurs invoquent. Par exemple, un hook correspondant àdeploy peut bloquer /deploy sauf si un fichier d’approbation est présent, ou un hook correspondant à une skill de révision peut ajouter la liste de contrôle de révision de l’équipe comme additionalContext.
Cet événement couvre le chemin que PreToolUse ne couvre pas : un hook PreToolUse correspondant à l’outil Skill se déclenche uniquement quand Claude appelle l’outil, mais taper /skillname directement contourne PreToolUse. UserPromptExpansion se déclenche sur ce chemin direct.
Correspond à command_name. Laissez le matcher vide pour se déclencher sur chaque commande de type invite.
Entrée UserPromptExpansion
En plus des champs d’entrée communs, les hooks UserPromptExpansion reçoiventexpansion_type, command_name, command_args, command_source, et la chaîne prompt originale. Le champ expansion_type est slash_command pour les skills et commandes personnalisées, ou mcp_prompt pour les prompts du serveur MCP.
Contrôle de décision UserPromptExpansion
Les hooksUserPromptExpansion peuvent bloquer l’expansion ou ajouter du contexte. Tous les champs de sortie JSON sont disponibles.
Un hook qui bloque en quittant 2 s’achemine de la même façon que
reason : le message de blocage montre le texte stderr à l’utilisateur.
MessageDisplay
S’exécute pendant qu’un message d’assistant s’affiche à l’écran. Claude Code affiche le message par incréments : chaque fois qu’un lot de lignes nouvellement complétées est prêt à être rendu, le hook s’exécute une fois avec ces lignes et Claude Code rend le texte de remplacement du hook à leur place. Un long message produit plusieurs appels ; un court message peut ne produire qu’un seul. Utilisez MessageDisplay pour :- supprimer le markdown pour un affichage minimal
- transformer le texte qu’une application Agent SDK montre à ses utilisateurs
- masquer les clés API ou les noms d’hôtes internes des réponses de Claude
timeout dans l’entrée du hook.
MessageDisplay est affichage uniquement : le texte de remplacement change uniquement ce qui est rendu à l’écran. La transcription et ce que Claude voit conservent le texte original, donc Claude ne voit jamais le remplacement, et le mode verbeux affiche l’original. Le hook reçoit uniquement le texte du message d’assistant, donc les résultats d’outils et le texte que vous tapez s’affichent inchangés.
MessageDisplay ne supporte pas les matchers et se déclenche pour chaque message d’assistant qui affiche du texte ; les messages sans texte, comme les réponses contenant uniquement des appels d’outils, ne le déclenchent pas.
Dans les exécutions non-interactives, y compris les requêtes Agent SDK et claude -p, MessageDisplay s’exécute une fois par message d’assistant au lieu d’une fois par lot de lignes. L’appel unique arrive après que le message se termine et porte le texte du message complet : index est 0, final est true, et delta contient le message entier. Un hook qui collecte le texte delta pour chaque message reçoit le même texte total dans les deux modes.
Entrée MessageDisplay
En plus des champs d’entrée communs, les hooks MessageDisplay reçoivent des identifiants pour le tour et le message, la position de cet appel dans le message, et le nouveau texte dansdelta. Les limites de lot dépendent de la façon dont le texte s’affiche, donc utilisez index et final pour suivre la progression à travers un message plutôt que de vous attendre à ce que les lignes soient groupées d’une manière particulière.
Sortie MessageDisplay
En plus des champs de sortie JSON disponibles pour tous les hooks, les hooks MessageDisplay peuvent retournerdisplayContent pour remplacer le delta à l’écran :
Les hooks MessageDisplay n’ont pas de contrôle de décision. Ils ne peuvent pas bloquer le message ou changer ce qui est stocké dans la transcription ou envoyé à Claude. Claude Code agit sur
displayContent de leur sortie JSON et rejette systemMessage et continue.
Cet exemple supprime le formatage markdown des réponses de Claude pour un affichage en texte brut. Le script lit chaque lot depuis stdin, supprime les marqueurs gras et les backticks de code en ligne de delta, et retourne le résultat comme displayContent.
- macOS/Linux
- Windows (PowerShell)
Enregistrez un hook de commande pour l’événement dans votre fichier de paramètres :Enregistrez ce script dans
.claude/hooks/plain-display.sh dans votre projet et rendez-le exécutable avec chmod +x :jq est manquant, Claude Code affiche le texte original et note l’échec uniquement dans la sortie de débogage, pas dans la session.
PreToolUse
S’exécute après que Claude crée les paramètres d’outil et avant de traiter l’appel d’outil. Correspond à n’importe quel nom d’outil saufEndConversation : les outils intégrés comme Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion, et ExitPlanMode, et n’importe quels noms d’outils MCP.
Pour exécuter un hook quand un fichier spécifique change sur le disque, peu importe ce qui l’a écrit, utilisez FileChanged au lieu de faire correspondre les outils d’édition de fichiers par nom. Contrairement à PreToolUse, Claude Code exécute les hooks FileChanged après le changement, et ils n’ont pas de contrôle de décision, donc ils ne peuvent pas bloquer l’écriture.
Utilisez Contrôle de décision PreToolUse pour permettre, refuser, demander, ou différer l’appel d’outil.
Un hook de rappel Agent SDK sur PreToolUse qui dépasse son délai d’expiration bloque l’appel d’outil, et Claude reçoit un résultat d’erreur nommant le délai d’expiration. Un refus explicite retourné par un autre hook a toujours la priorité.
Entrée PreToolUse
En plus des champs d’entrée communs, les hooks PreToolUse reçoiventtool_name, tool_input, et tool_use_id.
Pour un outil MCP, l’entrée porte également mcp_server, un objet avec le name du serveur et une source qui dit d’où vient la définition du serveur. Les valeurs source incluent plugin, sdk, et les portées de configuration comme user et project. McpServerProvenance dans la référence Agent SDK les énumère toutes et dit comment traiter une que vous ne reconnaissez pas. Basez les décisions de confiance sur source plutôt que sur name ou le préfixe du nom d’outil mcp__<server>__. Le champ mcp_server nécessite Claude Code v2.1.274 ou ultérieur.
Pour les outils de fichier Write, Edit, et Read, tool_input.file_path est toujours absolu :
- Claude Code développe
~et les chemins relatifs avant que les hooks s’exécutent, donc un hook qui correspond à des chemins ne peut pas être contourné via~ou une orthographe relative du même chemin - Sur Windows, le chemin arrive avec des séparateurs de barre oblique inverse, même quand votre hook s’exécute sous Git Bash où
$PWDressemble à/c/project - Une comparaison écrite avec des barres obliques avant, comme une vérification
/src/, ne correspond jamais à un chemin de barre oblique inverse, et l’appel d’outil procède comme si le hook n’avait rien à bloquer - Normalisez les séparateurs avant de comparer :
FILE_PATH="${FILE_PATH//\\//}"en Bash, oufile_path.replace("\\", "/")en Python, puis correspondez à un segment de chemin comme/src/plutôt que d’ancrer avec^, puisque le chemin est absolu
Write sur Windows livre :
tool_input dépendent de l’outil :
Exécute les commandes shell.
Quand une commande Bash change des fichiers dans un référentiel Git, Claude Code peut enregistrer ce qui a changé. Il enregistre les changements dans chaque mode de permission quand le paramètre
bashEditDiffEnabled active l’enregistrement ; l’entrée de ce paramètre dit quels fichiers peuvent le définir. Sinon, il les enregistre uniquement en mode auto et mode bypassPermissions, et uniquement quand Claude Code dirige Claude à éditer des fichiers via Bash. Définissez bashEditDiffEnabled à false pour désactiver l’enregistrement. Les commandes en arrière-plan et les commandes en lecture seule ne portent pas de diff.
Votre hook PostToolUse reçoit alors les fichiers modifiés dans tool_response.bashEditDiff. La liste couvre ce qui a changé sous le référentiel pendant que la commande s’exécutait. Les fichiers que Git ignore et les fichiers dans les sous-modules ne sont pas listés. Nécessite Claude Code v2.1.269 ou ultérieur.
La liste est au mieux un effort et en bêta publique. Claude Code peut manquer un changement, inclure un fichier qu’un autre processus a changé au même moment, ou s’arrêter à ses limites de taille. La forme du champ peut changer. Utilisez la liste pour trouver ce à examiner, pas pour appliquer une politique.
changedFiles et files listent ce que la commande a changé ; les champs restants disent à quel point cette liste est complète et fiable.
Exécute les commandes PowerShell. Voir l’outil PowerShell pour la disponibilité par plateforme.
Les champs correspondent à l’outil Bash, avec la chaîne de commande dans
command :
Correspondez à
Bash|PowerShell dans les hooks qui inspectent les commandes shell, pour qu’ils couvrent les deux outils :
- Sur Windows, partout où l’outil PowerShell est activé, Claude traite PowerShell comme le shell principal et achemine les commandes shell à travers lui.
- Sur Windows sans Git Bash, l’outil est activé automatiquement et Claude Code n’enregistre pas l’outil Bash du tout.
- Un hook qui correspond uniquement à
Bashne se déclenche jamais là.
Remplace une chaîne dans un fichier existant.
Lit le contenu des fichiers.
Trouve les fichiers correspondant à un modèle glob.
Recherche le contenu des fichiers avec des expressions régulières.
Récupère et traite le contenu web.
Recherche le web.
Crée un sous-agent.
Quand un appel Agent au premier plan se termine, votre hook PostToolUse reçoit le résultat du sous-agent et la télémétrie d’exécution dans
tool_response. Lisez ces champs pour inspecter l’exécution ; pour les cumuls de tokens et de coûts entre les sous-agents, utilisez les compteurs de tokens et de coûts filtrés à query_source "subagent", puisque totalTokens et usage couvrent uniquement la requête finale :
Sur Claude Code v2.1.271 ou ultérieur, un sous-agent qui s’exécute avec l’outil
SubagentHandback, que Claude Code fournit en mode auto, livre son rapport via cet outil plutôt que de le retourner comme texte. Le champ content de son résultat completed porte alors une brève note à ce sujet plutôt que le rapport lui-même. Pour lire le rapport, correspondez à un hook PreToolUse ou PostToolUse sur SubagentHandback et lisez tool_input.message.
Pour les sous-agents en arrière-plan, l’outil retourne quand la tâche passe en arrière-plan, donc tool_response ne porte pas de champs d’utilisation : un lancement en arrière-plan retourne immédiatement, et une tâche au premier plan que Claude Code met en arrière-plan en cours d’exécution retourne à cette transition. Il a status: "async_launched", agentId, description, prompt, outputFile, et resolvedModel.
Sur une réponse completed, resolvedModel nomme le modèle sur lequel le sous-agent a démarré, qui peut différer de la valeur model dans tool_input, comme quand availableModels ou un autre remplacement s’applique. Sur une réponse async_launched, resolvedModel nomme le modèle en utilisation quand l’agent est passé en arrière-plan, donc un échange qui s’est produit avant la mise en arrière-plan est reflété là. modelsUsed et le comportement resolvedModel au moment de la mise en arrière-plan nécessitent Claude Code v2.1.212 ou ultérieur.
Pose à l’utilisateur une à quatre questions à choix multiples.
Présente un plan et demande à l’utilisateur de l’approuver avant que Claude quitte le mode plan. Claude écrit le plan dans un fichier sur le disque avant d’appeler l’outil, donc le
tool_input littéral du modèle est généralement vide. Claude Code injecte le contenu du plan et le chemin du fichier avant de passer l’entrée aux hooks.
Dans
PostToolUse, tool_response est un objet avec les champs plan et filePath contenant le plan approuvé, plus les drapeaux d’état internes. Lisez tool_response.plan pour le contenu du plan plutôt que de relire le fichier depuis le disque.
Contrôle de décision PreToolUse
Les hooksPreToolUse peuvent contrôler si un appel d’outil procède. Contrairement aux autres hooks qui utilisent un champ decision de haut niveau, PreToolUse retourne sa décision à l’intérieur d’un objet hookSpecificOutput. Cela lui donne un contrôle plus riche : quatre résultats (permettre, refuser, demander, ou différer) plus la capacité de modifier l’entrée d’outil avant l’exécution.
Quand plusieurs hooks PreToolUse retournent des décisions différentes, la priorité est
deny > defer > ask > allow.
Un hook qui bloque en quittant 2 s’achemine de la même façon que "deny" : Claude voit le message stderr comme la raison du refus.
Quand un hook retourne "ask", l’invite de permission affichée à l’utilisateur inclut une étiquette identifiant d’où vient le hook : [settings] pour un hook de n’importe quel fichier de paramètres ou du frontmatter d’agent, [plugin:<name>] pour le hook d’un plugin, ou [skill] pour un hook du frontmatter de skill. Cela aide les utilisateurs à comprendre quelle source de configuration demande la confirmation.
Un "ask" d’un hook force également une invite de permission en mode auto : le classificateur peut toujours refuser l’appel d’outil, mais il ne peut pas approuver l’appel silencieusement. Avant v2.1.211, le classificateur pouvait approuver une commande Bash s’exécutant en dehors du sandbox sans montrer l’invite que le hook a demandée ; le classificateur appliquait toujours ses propres règles de sécurité à cette commande, et un refus de hook "deny" était toujours honoré.
-p, Claude Code offre AskUserQuestion et ExitPlanMode uniquement quand l’exécution a un hôte de permission pour recevoir l’invite, comme un rappel canUseTool d’Agent SDK. Ces outils nécessitent l’interaction de l’utilisateur. Retourner permissionDecision: "allow" avec updatedInput satisfait cette exigence : le hook lit l’entrée de l’outil depuis stdin, collecte la réponse via votre propre interface utilisateur, et la retourne dans updatedInput pour que l’outil s’exécute sans demander. Retourner "allow" seul n’est pas suffisant pour ces outils. Pour AskUserQuestion, renvoyez le tableau questions original et ajoutez un objet answers mappant le texte de chaque question à la réponse choisie.
À partir de v2.1.199, un outil MCP dont le serveur le marque avec _meta["anthropic/requiresUserInteraction"] est plus strict : un hook ne peut pas ignorer son invite d’approbation avec "allow", avec ou sans updatedInput, parce que Claude Code ne peut pas confirmer que le hook a collecté l’interaction que l’outil nécessite.
PreToolUse utilisait auparavant les champs
decision et reason de haut niveau, mais ceux-ci sont dépréciés pour cet événement. Utilisez plutôt hookSpecificOutput.permissionDecision et hookSpecificOutput.permissionDecisionReason. Les valeurs dépréciées "approve" et "block" correspondent à "allow" et "deny" respectivement. D’autres événements comme PostToolUse et Stop continuent d’utiliser decision et reason de haut niveau comme leur format actuel.Différer un appel d’outil pour plus tard
"defer" est pour les intégrations qui exécutent claude -p comme un sous-processus et lisent sa sortie JSON, comme une application Agent SDK ou une interface utilisateur personnalisée construite au-dessus de Claude Code. Cela permet à ce processus appelant de mettre en pause Claude à un appel d’outil, de collecter l’entrée via sa propre interface, et de reprendre où il s’était arrêté. Claude Code honore cette valeur uniquement en mode non-interactif avec le drapeau -p. Dans les sessions interactives, il enregistre un avertissement et ignore le résultat du hook.
L’outil AskUserQuestion est le cas typique : Claude veut poser quelque chose à l’utilisateur, mais il n’y a pas de terminal pour répondre. Une exécution -p offre AskUserQuestion uniquement quand elle a un hôte de permission, comme un outil MCP que vous passez avec --permission-prompt-tool, donc commencez l’exécution avec un. Le aller-retour fonctionne comme ceci :
- Claude appelle
AskUserQuestion. Le hookPreToolUsese déclenche. - Le hook retourne
permissionDecision: "defer". L’outil ne s’exécute pas. Le processus quitte avecstop_reason: "tool_deferred"et l’appel d’outil en attente préservé dans la transcription. - Le processus appelant lit
deferred_tool_usedu résultat SDK, affiche la question dans sa propre interface utilisateur, et attend une réponse. - Le processus appelant exécute
claude -p --resume <session-id>avec le même hôte de permission. Le même appel d’outil déclenchePreToolUseà nouveau. - Le hook retourne
permissionDecision: "allow"avec la réponse dansupdatedInput. L’outil s’exécute et Claude continue.
deferred_tool_use porte l’id, le name, et l’input de l’outil. L’input est les paramètres que Claude a générés pour l’appel d’outil, capturés avant l’exécution :
cleanupPeriodDays, qui supprime les fichiers de session après 30 jours par défaut, en suivant les règles de balayage de rétention. Si la réponse n’est pas prête quand vous reprenez, le hook peut retourner "defer" à nouveau et le processus quitte de la même façon. Le processus appelant contrôle quand casser la boucle en retournant finalement "allow" ou "deny" du hook.
"defer" fonctionne uniquement quand Claude effectue un seul appel d’outil dans le tour. Si Claude effectue plusieurs appels d’outils à la fois, "defer" est ignoré avec un avertissement et l’outil procède par le flux de permission normal. La contrainte existe parce que la reprise ne peut relancer qu’un seul outil : il n’y a aucun moyen de différer un appel d’un lot sans laisser les autres non résolus.
Si l’outil différé n’est plus disponible quand vous reprenez, le processus quitte avec stop_reason: "tool_deferred_unavailable" et is_error: true avant que le hook se déclenche. Cela se produit quand un serveur MCP qui a fourni l’outil n’est pas connecté pour la session reprise. La charge utile deferred_tool_use est toujours incluse pour que vous puissiez identifier quel outil a disparu.
Pour reprendre une session différée en mode plan, passez
--permission-prompt-tool avec --resume pour que Claude Code puisse présenter le plan pour approbation. Si vous passez certains autres drapeaux de lancement, l’exécution reprise ne retourne pas au mode plan ; voir Reprendre en mode plan avec -p. Nécessite Claude Code v2.1.246 ou ultérieur.Quand vous reprenez avec -p, Claude Code ne restaure aucun autre mode de permission stocké. Il démarre l’exécution dans le mode de permission qu’une nouvelle exécution claude -p démarrerait, donc passez --permission-mode ou --dangerously-skip-permissions à nouveau si la session différée en utilisait un. Quand vous reprenez avec claude --resume <session-id> sans -p, Claude Code restaure le mode de permission stocké, avec les exceptions listées dans mode de permission à la reprise.PermissionRequest
S’exécute quand Claude Code est sur le point de vous demander la permission d’utiliser un outil. Dans les sessions qui ne peuvent pas montrer une invite, comme les sous-agents en arrière-plan en mode non-interactif, Claude Code exécute toujours ces hooks, et si aucun hook ne retourne une décision, il refuse l’appel d’outil. Utilisez Contrôle de décision PermissionRequest pour permettre ou refuser au nom de l’utilisateur. Utilisez cet événement quand vous avez besoin d’un signal au moment où Claude demande la permission d’utiliser un outil. Claude Code exécute un hook Notification avec le typepermission_prompt uniquement après que l’invite ait attendu environ six secondes.
Claude Code n’exécute pas les hooks PermissionRequest pour la requête réseau d’une commande en sandbox. Pour obtenir un signal pour cette invite, utilisez le type de notification permission_prompt.
Correspond au nom de l’outil, mêmes valeurs que PreToolUse.
Entrée PermissionRequest
Les hooks PermissionRequest reçoivent les champstool_name et tool_input comme les hooks PreToolUse, mais sans tool_use_id. Pour un outil MCP, ils reçoivent également l’objet mcp_server. Un tableau optionnel permission_suggestions contient les mises à jour de permission que Claude Code suggère pour cette requête, comme ajouter une règle d’autorisation ou changer le mode de permission.
Le tableau permission_suggestions n’est pas une liste exacte des options que vous voyez, parce que chaque dialogue de permission construit ses propres options. Certains dialogues, comme celui pour les éditions de fichiers, ne lisent pas du tout le tableau et dérivent leurs options de la requête elle-même. Un dialogue qui le lit peut toujours retenir une option dont la suggestion reste dans le tableau, par exemple quand allowManagedPermissionRulesOnly cache les options de sauvegarde de règles. Il peut également offrir des options qui n’ont pas d’entrée de suggestion, comme Oui, et passer en mode auto, qui change le mode de permission directement plutôt que via une mise à jour de permission.
Les hooks PreToolUse s’exécutent avant chaque appel d’outil, qu’il ait besoin de permission ou non. Les hooks PermissionRequest s’exécutent uniquement quand Claude Code est sur le point de vous demander la permission, ou quand il refuserait autrement un appel qui ne peut pas demander. Aucun événement ne se déclenche pour EndConversation.
Contrôle de décision PermissionRequest
Les hooksPermissionRequest peuvent permettre ou refuser les demandes de permission. En plus des champs de sortie JSON disponibles pour tous les hooks, votre script de hook peut retourner un objet decision avec ces champs spécifiques à l’événement :
Un hook qui quitte 2 sans un objet
decision laisse le flux de permission inchangé, et son stderr est rejeté. Seul l’objet decision peut accorder ou refuser la requête.
Entrées de mise à jour de permission
Le champ de sortieupdatedPermissions et le champ d’entrée permission_suggestions utilisent tous deux le même tableau d’objets d’entrée. Chaque entrée a un type qui détermine ses autres champs, et une destination qui contrôle où le changement est écrit.
setMode avec bypassPermissions ne prend effet que si vous avez lancé la session avec le mode bypass déjà disponible : --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, ou permissions.defaultMode: "bypassPermissions" dans les paramètres utilisateur, --settings, ou gérés. Sinon, la mise à jour est un non-op. La mise à jour est également un non-op quand permissions.disableBypassPermissionsMode désactive le mode, ou quand la session démarre en mode restreint.bypassPermissions n’est jamais persisté comme defaultMode indépendamment de destination.destination sur chaque entrée détermine si le changement reste en mémoire ou persiste dans un fichier de paramètres.
Un hook peut renvoyer l’une des
permission_suggestions qu’il a reçues comme sa propre sortie updatedPermissions.
PostToolUse
S’exécute immédiatement après qu’un outil se termine avec succès. Correspond au nom de l’outil, mêmes valeurs que PreToolUse. Correspondez plus largement quand le nom de l’outil n’est pas le bon filtre :- Pour exécuter un hook après que n’importe quel outil se termine avec succès, omettez le
matcherou définissez-le à"*". Votre hook peut alors découvrir ce qui a changé lui-même, par exemple en exécutantgit status --porcelain, qui liste également les fichiers non suivis quegit diffmanque. Pour les appels d’outils qui échouent, ajoutez le même hook sous PostToolUseFailure. - Pour exécuter un hook quand un fichier spécifique change sur le disque, peu importe ce qui l’a écrit, utilisez FileChanged. Claude Code n’exécute pas un hook
PostToolUsecorrespondant àEdit|Writequand une commandeBashou un processus en dehors de Claude Code réécrit le même fichier.
Entrée PostToolUse
Les hooksPostToolUse se déclenchent après qu’un outil s’est déjà exécuté avec succès. L’entrée inclut à la fois tool_input, les arguments envoyés à l’outil, et tool_response, le résultat qu’il a retourné. Le schéma exact pour les deux dépend de l’outil. Les chemins tool_input des outils de fichier arrivent dans le même format que pour PreToolUse : toujours absolu, avec les séparateurs natifs de la plateforme, donc les barres obliques inverses sur Windows. Pour un outil MCP, l’entrée porte également l’objet mcp_server.
Contrôle de décision PostToolUse
Les hooksPostToolUse peuvent fournir des commentaires à Claude après l’exécution de l’outil. En plus des champs de sortie JSON disponibles pour tous les hooks, votre script de hook peut retourner ces champs spécifiques à l’événement :
L’exemple ci-dessous remplace la sortie d’un appel
Bash. La valeur de remplacement correspond à la forme de sortie de l’outil Bash :
Annoter un résultat pour le classificateur du mode auto
RetournezclassifierContext pour envoyer une brève note sur le résultat de l’appel d’outil au classificateur du mode auto plutôt qu’à Claude. Le classificateur ne reçoit jamais les résultats d’outils eux-mêmes, donc ce champ est la façon supportée de lui dire quelque chose sur ce qu’un appel a retourné avant qu’il examine les actions ultérieures. Le champ nécessite Claude Code v2.1.236 ou ultérieur.
L’exemple ci-dessous dit au classificateur d’où provient la sortie d’une requête :
- Hooks configurés dans Claude Code : pour les hooks des fichiers de paramètres, plugins, skills, et frontmatter d’agent, le classificateur traite la note comme du contexte non vérifié fourni par l’application. La note n’établit jamais l’intention de l’utilisateur, et si elle prétend que vous avez approuvé ou demandé quelque chose, le classificateur vérifie cette affirmation contre vos propres messages dans la conversation
- Rappels Agent SDK en processus : quand une application intégrant Claude Code enregistre le hook comme un rappel SDK TypeScript et retourne la note pendant la session en direct, le classificateur peut peser une déclaration d’utilisateur relayée dans la note comme intention de l’utilisateur. Une telle déclaration peut satisfaire une exigence de consentement que le classificateur accepterait d’un message que vous envoyez, mais elle ne lève jamais un blocage que votre propre message ne pourrait pas lever non plus. Après qu’une session reprenne, Claude Code traite les notes restaurées comme du contexte non vérifié. Quand les hooks des deux groupes annotent le même appel, le classificateur traite la note combinée comme non vérifiée
- Longueur : Claude Code plafonne les notes pour un appel d’outil à 2 000 caractères et tronque le reste. Le plafond est partagé entre chaque hook qui répond à cet appel
- Réponses synchrones uniquement : Claude Code ignore le champ dans la réponse d’un hook qui s’exécute en arrière-plan, parce que cette réponse arrive après que Claude Code enregistre le résultat d’outil
- Appels que le classificateur n’enregistre pas : la transcription du classificateur omet les recherches en lecture seule comme les lectures de fichiers et les recherches. Claude Code rejette une note attachée à l’un de ces appels
- Interaction avec les réécritures : quand la note décrit la sortie que vous remplacez avec
updatedToolOutput, retournez les deux champs dans la même réponse de hook. Claude Code rejette la note si cette réécriture est rejetée ou qu’une réécriture d’un autre hook la remplace. Claude Code livre une note que vous retournez sans réécriture même quand un autre hook réécrit la sortie
PostToolUseFailure
S’exécute quand un outil qui a commencé à s’exécuter échoue : l’outil a levé une erreur, ou un outil MCP a retourné un résultat d’erreur. Utilisez ceci pour enregistrer les échecs, envoyer des alertes, ou fournir des commentaires correctifs à Claude. Correspond au nom de l’outil, mêmes valeurs que PreToolUse.Cet événement ne se déclenche pas pour les appels d’outils rejetés avant l’exécution : un nom d’outil inconnu, une entrée qui échoue la validation de schéma ou spécifique à l’outil, ou un refus de permission. Les rejets de validation sont retournés comme résultats
tool_use_error et se produisent avant que les hooks s’exécutent, donc ils ne déclenchent ni PreToolUse ni PostToolUseFailure. Les refus de permission déclenchent PreToolUse mais pas cet événement ; voir PermissionDenied.Entrée PostToolUseFailure
Les hooks PostToolUseFailure reçoivent les mêmes champstool_name et tool_input que PostToolUse, ainsi que les informations d’erreur comme champs de haut niveau. Pour un outil MCP, ils reçoivent également l’objet mcp_server. Par exemple, une commande npm test échouée pourrait livrer :
La chaîne
error est généralement le même texte que Claude reçoit comme résultat de l’outil échoué. Son format varie selon l’outil et l’échec. Clé votre hook sur tool_name, is_interrupt, et la première ligne Exit code N ; traitez le reste de la chaîne comme du texte d’affichage, pas un format stable.
- Pour Bash et PowerShell, une commande qui s’est exécutée et a quitté produit une première ligne
Exit code N, puis toute sortie que la commande a produite comme un bloc avec stdout et stderr entrelacés - Une charge utile peut également porter un message d’échec nu sans ligne de code de sortie, quand Claude Code n’a pas pu démarrer le processus shell lui-même
- Claude Code tronque au milieu les longues chaînes autour d’un marqueur
... [N characters truncated] ..., et peut insérer ses propres lignes, commeCommand timed out after 2m 0s
Contrôle de décision PostToolUseFailure
Les hooksPostToolUseFailure peuvent fournir du contexte à Claude après un échec d’outil. En plus des champs de sortie JSON disponibles pour tous les hooks, votre script de hook peut retourner ces champs spécifiques à l’événement :
PostToolBatch
S’exécute une fois après que chaque appel d’outil dans un lot se soit résolu, avant que Claude Code envoie la requête suivante au modèle.PostToolUse se déclenche une fois par outil, ce qui signifie qu’il se déclenche simultanément quand Claude effectue des appels d’outils parallèles. PostToolBatch se déclenche exactement une fois avec le lot complet, donc c’est le bon endroit pour injecter du contexte qui dépend de l’ensemble des outils qui se sont exécutés plutôt que de n’importe quel outil unique. Il n’y a pas de matcher pour cet événement.
Entrée PostToolBatch
En plus des champs d’entrée communs, les hooks PostToolBatch reçoiventtool_calls, un tableau décrivant chaque appel d’outil dans le lot :
tool_response contient le même contenu que le modèle reçoit dans le bloc tool_result correspondant. La valeur est une chaîne sérialisée ou un tableau de bloc de contenu, exactement comme l’outil l’a émis. Pour Read, cela signifie du texte préfixé par le numéro de ligne plutôt que le contenu brut du fichier. Les réponses peuvent être grandes, donc analysez uniquement les champs dont vous avez besoin.
La forme
tool_response diffère de celle de PostToolUse. PostToolUse passe l’objet Output structuré de l’outil, comme {filePath: "...", type: "create"} pour Write ; PostToolBatch passe le contenu tool_result sérialisé que le modèle voit.Contrôle de décision PostToolBatch
Les hooksPostToolBatch peuvent injecter du contexte pour Claude. En plus des champs de sortie JSON disponibles pour tous les hooks, votre script de hook peut retourner ces champs spécifiques à l’événement :
decision: "block" ou continue: false arrête la boucle agentive avant l’appel du modèle suivant. Le message de blocage provient du reason JSON ou stopReason, ou de stderr en quittant 2. Vous le voyez comme un avertissement dans la transcription, et il reste dans la conversation, donc Claude le voit quand la conversation continue.
PermissionDenied
S’exécute quand le mode auto refuse un appel d’outil, y compris quand il refuse sans verdict du classificateur parce qu’une vérification de sécurité séparée du mode auto a refusé la propre requête du classificateur ou sa réponse n’a pas analysé. Ce hook ne se déclenche que en mode auto : il ne s’exécute pas quand vous refusez manuellement un dialogue de permission, quand un hookPreToolUse bloque un appel, ou quand une règle deny correspond. Utilisez-le pour enregistrer les refus, ajuster la configuration, ou dire au modèle qu’il peut réessayer l’appel d’outil.
Correspond au nom de l’outil, mêmes valeurs que PreToolUse.
Entrée PermissionDenied
En plus des champs d’entrée communs, les hooks PermissionDenied reçoiventtool_name, tool_input, tool_use_id, et reason. Pour un outil MCP, ils reçoivent également l’objet mcp_server.
Contrôle de décision PermissionDenied
Les hooks PermissionDenied peuvent dire au modèle qu’il peut réessayer l’appel d’outil refusé. Retournez un objet JSON avechookSpecificOutput.retry défini à true :
retry est true, Claude Code ajoute un message à la conversation disant au modèle qu’il peut réessayer l’appel d’outil. Claude Code ne renverse pas le refus lui-même. Si votre hook ne retourne pas JSON, ou retourne retry: false, le refus tient et le modèle reçoit le message de rejet original.
Claude Code ignore retry: true quand le classificateur a produit aucun verdict sur l’action : sa réponse n’a pas analysé, ou une vérification de sécurité séparée du mode auto a refusé la requête du classificateur. Pour ces refus, Claude Code dit déjà au modèle dans le message de rejet s’il faut réessayer plus tard ou continuer.
Notification
S’exécute quand Claude Code envoie des notifications. Correspond au type de notification. Omettez le matcher pour exécuter les hooks pour tous les types de notification. Vous recevez ces événements de hook même avec les notifications de bureau désactivées : le paramètrepreferredNotifChannel, y compris notifications_disabled, change uniquement comment vous êtes alerté, pas si votre hook s’exécute.
Les types
agent_needs_input et agent_completed nécessitent Claude Code v2.1.198 ou ultérieur.
Les types quota_auto_resume_fired, quota_auto_resume_stale, et quota_auto_resume_disabled nécessitent Claude Code v2.1.234 ou ultérieur.
En sessions de terminal, permission_prompt pour la requête réseau d’une commande en sandbox nécessite Claude Code v2.1.246 ou ultérieur.
agent_needs_input pour la question de configuration de terminal d’un coéquipier nécessite Claude Code v2.1.248 ou ultérieur.
Les types
permission_prompt, idle_prompt, elicitation_dialog, et elicitation_url_dialog partagent leur timing avec les notifications de bureau, donc en sessions de terminal vous ne les voyez que quand vous semblez être loin du terminal :- Attendez
permission_promptune fois que vous n’avez pas tapé pendant environ six secondes. Le minuteur démarre quand l’invite de permission apparaît, et chaque frappe le reporte. Pour exécuter un hook immédiatement quand Claude demande la permission d’utiliser un outil, utilisez plutôt PermissionRequest. - Attendez
idle_promptenviron 60 secondes après que Claude finisse de répondre, et uniquement si vous n’avez pas tapé depuis. Claude Code n’envoie pasidle_promptpendant qu’il attend qu’une limite d’utilisation claude.ai se réinitialise. Quand l’attente se termine d’elle-même, l’un des typesquota_auto_resume_*se déclenche à la place. - Attendez
elicitation_dialogpour un formulaire d’élicitation, ouelicitation_url_dialogpour une requête d’URL de navigateur, une fois que vous n’avez pas tapé pendant environ six secondes. Les deux partagent la même porte de six secondes quepermission_prompt: le minuteur démarre quand le dialogue apparaît, et chaque frappe le reporte.
permission_prompt différemment dans les sessions où il envoie les requêtes de permission au rappel canUseTool d’Agent SDK, ce qui est comment Claude Desktop et l’extension VS Code hébergent Claude Code :
- Attendez
permission_promptenviron six secondes après que Claude demande la permission. Claude Code ne le reporte pas pendant que vous tapez. - Si vous ou un hook PermissionRequest répondez plus tôt, Claude Code n’exécute pas
permission_prompt. - Définissez
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSà1pour désactiverpermission_promptdans ces sessions.
permission_prompt ne se déclenchait pas dans ces sessions.
Utilisez des matchers séparés pour exécuter différents gestionnaires selon le type de notification. Cette configuration déclenche un script d’alerte spécifique à la permission quand Claude a besoin d’une approbation de permission et une notification différente quand Claude a été inactif :
Entrée Notification
En plus des champs d’entrée communs, les hooks Notification reçoiventmessage avec le texte de notification, un title optionnel, et notification_type indiquant quel type s’est déclenché.
systemMessage et continue mais émet toujours terminalSequence, sur lequel l’exemple de notification de bureau s’appuie. Les hooks Notification sont destinés aux effets secondaires comme transférer la notification à un service externe.
SubagentStart
S’exécute quand Claude crée un sous-agent avec l’outil Agent, quand Claude reprend un sous-agent, et chaque fois qu’un coéquipier d’équipe d’agents en processus gère un nouveau message. Supporte les matchers pour filtrer par nom de type d’agent. Pour les agents intégrés, c’est le nom de l’agent commegeneral-purpose, Explore, ou Plan. Pour les sous-agents personnalisés, c’est le champ name du frontmatter de l’agent, pas le nom du fichier.
Pour les sous-agents expédiés par un plugin, le type d’agent est l’identifiant délimité par plugin comme my-plugin:reviewer, pas le nom du frontmatter nu. Le deux-points place un nom délimité par plugin sur le chemin d’expression régulière, donc ancrez le matcher avec ^ et $ pour une correspondance exacte : ^my-plugin:reviewer$.
Entrée SubagentStart
En plus des champs d’entrée communs, les hooks SubagentStart reçoiventagent_id avec l’identifiant unique du sous-agent et agent_type avec le nom de l’agent sur lequel le matcher filtre.
SubagentStop
S’exécute quand un sous-agent Claude Code a fini de répondre. Correspond au type d’agent, mêmes valeurs que SubagentStart.Entrée SubagentStop
En plus des champs d’entrée communs, les hooks SubagentStop reçoiventstop_hook_active, agent_id, agent_type, agent_transcript_path, et last_assistant_message. Le champ agent_type est la valeur utilisée pour le filtrage du matcher. Le transcript_path est la transcription de la session principale, tandis que agent_transcript_path est la propre transcription du sous-agent stockée dans un dossier subagents/ imbriqué. Le champ last_assistant_message contient le contenu textuel de la réponse finale du sous-agent, donc les hooks peuvent y accéder sans analyser le fichier de transcription.
Pas chaque événement SubagentStop provient d’un sous-agent que Claude a créé. Claude Code exécute également des agents internes pour certaines de ses propres fonctionnalités, comme les suggestions d’invite et les questions latérales /btw, et SubagentStop se déclenche quand l’un d’eux se termine aussi. Pour ces événements, agent_type est le nom de l’agent que la session elle-même exécute, comme celui défini avec --agent ou le paramètre agent, et une chaîne vide quand la session s’exécute sans un.
Un matcher qui nomme les types d’agent ne correspond pas à un agent_type vide. Un hook dont le matcher est omis, "", ou "*", ou est une expression régulière qui correspond à une chaîne vide, s’exécute pour les événements avec un agent_type vide aussi.
Sur Claude Code v2.1.271 ou ultérieur, un sous-agent qui s’exécute avec l’outil SubagentHandback livre son rapport via cet outil avant qu’il ne s’arrête. Le champ last_assistant_message contient alors le texte de fermeture du sous-agent, le cas échéant, qui n’est pas le rapport livré. Le rapport est l’entrée message de cet appel, qu’un hook PreToolUse ou PostToolUse correspondant à SubagentHandback reçoit comme tool_input.message.
Les hooks SubagentStop reçoivent également les tableaux background_tasks et session_crons décrits sous Entrée Stop. Les deux tableaux sont délimités à la session parent, pas au sous-agent.
hookSpecificOutput.additionalContext avec hookEventName défini à "SubagentStop", pour les commentaires sans erreur qui gardent le sous-agent en cours d’exécution. Retourner decision: "block" avec une reason garde le sous-agent en cours d’exécution et livre reason au sous-agent comme sa prochaine instruction. Un hook qui bloque en quittant 2 livre son message stderr de la même façon. Pour injecter du contexte dans la session parent après qu’un sous-agent retourne, utilisez plutôt un hook PostToolUse sur l’outil Agent.
TaskCreated
S’exécute quand une tâche est en cours de création via l’outilTaskCreate. Utilisez ceci pour appliquer les conventions de nommage, exiger les descriptions de tâche, ou empêcher certaines tâches d’être créées. Dans une session sans les outils Task, cet événement ne se déclenche pas.
Les hooks TaskCreated ne supportent pas les matchers et se déclenchent à chaque occurrence.
Entrée TaskCreated
En plus des champs d’entrée communs, les hooks TaskCreated reçoiventtask_id, task_subject, et optionnellement task_description, teammate_name, et team_name.
Contrôle de décision TaskCreated
Un hook TaskCreated peut bloquer la création de deux façons. De toute façon, Claude Code supprime la tâche et retourne votre message à Claude comme l’erreur de l’outil. Claude Code ignorecontinue: false de cet événement et Claude continue de travailler.
- Code de sortie 2 : Claude Code retourne le texte stderr comme le message.
- JSON
{"decision": "block", "reason": "..."}: Claude Code retournereasoncomme le message.
TaskCompleted
S’exécute quand une tâche est en cours de marquage comme complétée. Cela se déclenche dans deux situations : quand n’importe quel agent marque explicitement une tâche comme complétée via l’outil TaskUpdate, ou quand un coéquipier d’équipe d’agents termine son tour avec des tâches en cours. Utilisez ceci pour appliquer les critères de complétion comme passer les tests ou les vérifications de lint avant qu’une tâche puisse se fermer. Les hooks TaskCompleted ne supportent pas les matchers et se déclenchent à chaque occurrence.Entrée TaskCompleted
En plus des champs d’entrée communs, les hooks TaskCompleted reçoiventtask_id, task_subject, et optionnellement task_description, teammate_name, et team_name.
Contrôle de décision TaskCompleted
Les hooks TaskCompleted supportent deux façons de contrôler la complétion de tâche :- Code de sortie 2 : la tâche n’est pas marquée comme complétée et le message stderr est renvoyé au modèle comme commentaire.
- JSON
{"continue": false, "stopReason": "..."}: quand un coéquipier terminant son tour a déclenché l’événement, arrête le coéquipier entièrement, correspondant au comportement du hookStop. LestopReasonest montré à l’utilisateur. Quand l’outilTaskUpdatea déclenché l’événement, Claude Code ignorecontinue: false; le code de sortie 2 bloque toujours la complétion.
Stop
S’exécute quand l’agent Claude Code principal a fini de répondre. Ne s’exécute pas si l’arrêt s’est produit en raison d’une interruption utilisateur. Les erreurs API déclenchent plutôt StopFailure.Entrée Stop
En plus des champs d’entrée communs, les hooks Stop reçoiventstop_hook_active, last_assistant_message, background_tasks, et session_crons. Le champ stop_hook_active est true quand Claude Code continue déjà en raison d’un hook stop. Vérifiez cette valeur ou traitez la transcription pour éviter de bloquer sur une condition qui ne se résoudra jamais. Claude Code applique un plafond de 8 continuations consécutives : après que les hooks stop aient continué le tour huit fois de suite, Claude Code remplace le bloc suivant et termine le tour. Pour augmenter le plafond, définissez CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
Le champ last_assistant_message contient le contenu textuel de la réponse finale de Claude, donc les hooks peuvent y accéder sans analyser le fichier de transcription. Pour les hooks qui agissent sur le tour qui vient de se terminer, comme les hooks de lecture à haute voix ou de notification, utilisez ce champ plutôt que de lire transcript_path : le fichier de transcription n’est pas garanti d’inclure le message final au moment du Stop sur toutes les versions.
Les tableaux background_tasks et session_crons permettent aux hooks de distinguer « la session est terminée » de « la session est en pause en attente que le travail en arrière-plan la réveille ». Les deux tableaux sont présents quand le registre de tâches est accessible et sont vides quand rien n’est en vol ou programmé.
Chaque entrée dans background_tasks décrit une tâche en vol et utilise ces champs :
Chaque entrée dans
session_crons décrit un réveil programmé délimité à la session, provenant de CronCreate, ScheduleWakeup, et /loop :
Cet exemple montre une entrée Stop avec une tâche shell en vol et un cron récurrent :
Contrôle de décision Stop
Les hooksStop et SubagentStop peuvent contrôler si Claude continue. En plus des champs de sortie JSON disponibles pour tous les hooks, votre script de hook peut retourner ces champs spécifiques à l’événement :
Un hook qui bloque en quittant 2 s’achemine de la même façon que
reason : Claude reçoit le message stderr comme l’explication de pourquoi il devrait continuer.
additionalContext quand le hook fonctionne comme prévu et donne des conseils à Claude, comme « exécutez la suite de tests avant de terminer ». Cela garde la conversation en cours à travers les mêmes protections de boucle que decision: "block", à savoir l’entrée stop_hook_active et le plafond de 8 continuations consécutives, mais la transcription l’étiquette Stop hook feedback et aucune notification d’erreur de hook n’est montrée :
StopFailure
S’exécute à la place de Stop quand le tour se termine en raison d’une erreur API. Claude Code ignore la sortie du hook et le code de sortie, à partterminalSequence. Utilisez ceci pour enregistrer les échecs, envoyer des alertes, ou prendre des actions de récupération quand Claude ne peut pas terminer une réponse en raison des limites de débit, des problèmes d’authentification, ou d’autres erreurs API.
Entrée StopFailure
En plus des champs d’entrée communs, les hooks StopFailure reçoiventerror, optionnel error_details, et optionnel last_assistant_message. Le champ error identifie le type d’erreur et est utilisé pour le filtrage du matcher.
TeammateIdle
S’exécute quand un coéquipier d’équipe d’agents est sur le point de devenir inactif après avoir terminé son tour. Utilisez ceci pour appliquer les portes de qualité avant qu’un coéquipier arrête de travailler, comme exiger que les vérifications de lint passent ou vérifier que les fichiers de sortie existent. Les hooks TeammateIdle ne supportent pas les matchers et se déclenchent à chaque occurrence.Entrée TeammateIdle
En plus des champs d’entrée communs, les hooks TeammateIdle reçoiventteammate_name et team_name.
Contrôle de décision TeammateIdle
Les hooks TeammateIdle supportent deux façons de contrôler le comportement du coéquipier :- Code de sortie 2 : le coéquipier reçoit le message stderr comme commentaire et continue de travailler au lieu de devenir inactif.
- JSON
{"continue": false, "stopReason": "..."}: arrête le coéquipier entièrement, correspondant au comportement du hookStop. LestopReasonest montré à l’utilisateur.
ConfigChange
S’exécute quand un fichier de configuration change pendant une session. Utilisez ceci pour auditer les changements de paramètres, appliquer les politiques de sécurité, ou bloquer les modifications non autorisées aux fichiers de configuration. Claude Code exécute les hooks ConfigChange quand un fichier de paramètres, un fichier de politique gérée, ou un fichier de skill change. Pour la politique gérée, il les exécute uniquement quandmanaged-settings.json ou un fichier dans managed-settings.d/ change. Il applique les paramètres gérés par le serveur et les changements aux préférences gérées macOS ou à la politique du registre Windows sans les exécuter. Sur WSL avec wslInheritsWindowsSettings, il applique également un fichier de paramètres gérés Windows modifié du côté Windows sur son sondage de politique sans les exécuter.
Le matcher filtre sur la source de configuration :
Cet exemple enregistre tous les changements de configuration pour l’audit de sécurité :
Entrée ConfigChange
En plus des champs d’entrée communs, les hooks ConfigChange reçoiventsource et optionnellement file_path. Le champ source indique quel type de configuration a changé, et file_path fournit le chemin du fichier spécifique qui a été modifié.
Contrôle de décision ConfigChange
Les hooks ConfigChange peuvent bloquer les changements de configuration de prendre effet. Utilisez le code de sortie 2 ou un JSONdecision pour empêcher le changement. Quand bloqué, les nouveaux paramètres ne sont pas appliqués à la session en cours d’exécution.
policy_settings ne peuvent pas être bloqués. Les hooks se déclenchent toujours pour les sources policy_settings quand un fichier de paramètres gérés sur la machine change, pour que vous puissiez enregistrer ces édits, mais toute décision de blocage est ignorée. Cela garantit que les paramètres gérés par l’entreprise prennent toujours effet. Claude Code n’exécute pas les hooks ConfigChange quand les paramètres gérés par le serveur arrivent ou se rafraîchissent.
Claude Code agit sur la décision de blocage de la sortie JSON d’un hook ConfigChange et rejette systemMessage et continue. Un changement bloqué ne surface aucun message à vous ou à Claude, que vous bloquez avec reason ou avec stderr en quittant 2. Claude Code écrit uniquement une ligne au journal de débogage.
CwdChanged
S’exécute quand une commande shell dans la conversation principale change le répertoire de travail, par exemple quand Claude exécute une commandecd. Utilisez ceci pour réagir aux changements de répertoire : recharger les variables d’environnement, activer les chaînes d’outils spécifiques au projet, ou exécuter les scripts de configuration automatiquement. S’apparie avec FileChanged pour les outils comme direnv qui gèrent l’environnement par répertoire.
Les hooks CwdChanged ont accès à CLAUDE_ENV_FILE. Les variables écrites dans ce fichier persistent dans les commandes Bash suivantes jusqu’au prochain événement CwdChanged, quand Claude Code les efface.
CwdChanged ne supporte pas les matchers et se déclenche à chaque occurrence.
Entrée CwdChanged
En plus des champs d’entrée communs, les hooks CwdChanged reçoiventold_cwd et new_cwd.
Sortie CwdChanged
En plus des champs de sortie JSON disponibles pour tous les hooks, les hooks CwdChanged peuvent retournerwatchPaths pour définir dynamiquement quels chemins de fichier FileChanged surveille :
Les hooks CwdChanged n’ont pas de contrôle de décision. Ils ne peuvent pas bloquer le changement de répertoire.
Claude Code lit
watchPaths et systemMessage de leur sortie JSON et rejette continue. Dans les sessions interactives, il montre le systemMessage comme une brève notification de terminal. Le message n’atteint pas le flux de message SDK.
DirectoryAdded
S’exécute après que vous ajoutiez un répertoire de travail en cours de session avec la commande/add-dir, ou après qu’un client SDK en ajoute un avec la requête de contrôle register_repo_root. Utilisez ceci pour préparer un référentiel nouvellement ajouté, par exemple en installant ses dépendances.
Claude Code ne déclenche pas cet événement quand :
- Vous passez un répertoire avec le drapeau de démarrage
--add-dir; SessionStart couvre ces répertoires - Vous ajoutez un répertoire sur l’onglet Workspace
/permissions - Vous ajoutez un répertoire qui est déjà un répertoire de travail ou à l’intérieur d’un
Entrée DirectoryAdded
En plus des champs d’entrée communs, les hooks DirectoryAdded reçoiventdirectory et source.
continue de leur sortie JSON et affiche le reste différemment par source :
slash_command: Claude Code livre lesystemMessagedu hook à Claude comme contexte sur le tour de conversation suivant, plutôt que de vous le montrer. Un nombre de hooks échoués apparaît dans la transcription. La sortie d’échec complète va au journal de débogageregister_repo_root: Claude Code écrit la sortiesystemMessageet la sortie d’échec au journal de débogage uniquement
FileChanged
S’exécute quand un fichier surveillé change sur le disque. Claude Code détecte les changements avec un observateur de système de fichiers, pas en inspectant les appels d’outils, donc il exécute le hook peu importe ce qui a changé le fichier : un appel d’outilEdit ou Write, un script que Claude exécute avec Bash, ou un processus en dehors de Claude Code entièrement. Un usage courant est de recharger les variables d’environnement quand les fichiers de configuration du projet changent.
Le matcher pour cet événement sert deux rôles :
- Construire la liste de surveillance : la valeur est divisée sur
|et chaque segment est enregistré comme un nom de fichier littéral dans le répertoire de travail, donc".envrc|.env"surveille exactement ces deux fichiers. Les modèles regex ne sont pas utiles ici : une valeur comme^\.envsurveillerait un fichier littéralement nommé^\.env. - Filtrer quels hooks s’exécutent : quand un fichier surveillé change, la même valeur filtre quels groupes de hook s’exécutent en utilisant les règles de matcher standard contre le nom de base du fichier modifié.
data.csv après n’importe quel changement, y compris un appel d’outil Bash ou un script externe réécrivant le fichier :
file_path de l’entrée JSON sur stdin. Sa garde grep teste la même chose que perl supprime, un CR à la fin d’une ligne, donc l’exécution après une normalisation quitte sans toucher le fichier. Une garde plus lâche boucle pour toujours, parce que perl -i réécrit le fichier même quand il ne substitue rien et Claude Code exécute le hook à nouveau après chaque réécriture. Enregistrez ce script à /path/to/normalize-line-endings.sh et rendez-le exécutable :
data.csv avec une commande Bash. Claude Code exécute le hook et le fichier se termine avec les fins de ligne LF.
Pour surveiller les fichiers que vous ne pouvez pas nommer à l’avance, retournez watchPaths d’un hook pour mettre à jour la liste de surveillance dynamiquement. Claude Code démarre l’observateur uniquement quand quelque chose nomme un fichier à surveiller, donc semez la liste avec un groupe FileChanged dont le matcher nomme au moins un fichier, ou avec un hook SessionStart ou CwdChanged qui retourne watchPaths. Le matcher filtre toujours quels groupes de hook s’exécutent quand un fichier surveillé change, donc donnez au groupe qui gère les chemins dynamiques un matcher omis, qui correspond à chaque fichier surveillé et n’ajoute rien à la liste de surveillance. Un matcher "*" correspond également à chaque fichier, mais Claude Code l’enregistre dans la liste de surveillance comme un fichier littéral nommé *.
Les hooks FileChanged ont accès à CLAUDE_ENV_FILE. Les variables écrites dans ce fichier persistent dans les commandes Bash suivantes jusqu’au prochain événement CwdChanged, quand Claude Code les efface.
Entrée FileChanged
En plus des champs d’entrée communs, les hooks FileChanged reçoiventfile_path et event.
Sortie FileChanged
En plus des champs de sortie JSON disponibles pour tous les hooks, les hooks FileChanged peuvent retournerwatchPaths pour mettre à jour dynamiquement quels chemins de fichier sont surveillés :
Les hooks FileChanged n’ont pas de contrôle de décision. Ils ne peuvent pas bloquer le changement de fichier de se produire.
Claude Code lit
watchPaths et systemMessage de leur sortie JSON et rejette continue. Dans les sessions interactives, il montre le systemMessage comme une brève notification de terminal. Le message n’atteint pas le flux de message SDK.
WorktreeCreate
S’exécute quand un worktree est en cours de création, que ce soit à partir declaude --worktree, à partir d’un sous-agent utilisant isolation: "worktree", ou pour une session en arrière-plan que Claude Code isole dans son propre worktree. Par défaut, Claude Code crée la copie de travail isolée avec git worktree. Configurer un hook WorktreeCreate remplace ce comportement git par défaut, vous permettant d’utiliser un système de contrôle de version différent comme SVN, Perforce, ou Mercurial.
Parce que le hook remplace le comportement par défaut entièrement, .worktreeinclude n’est pas traité. Si vous avez besoin de copier les fichiers de configuration locaux comme .env dans le nouveau worktree, faites-le à l’intérieur de votre script de hook.
Le hook doit retourner le chemin du répertoire worktree créé. Claude Code utilise ce chemin comme le répertoire de travail pour la session isolée. Voir Sortie WorktreeCreate pour savoir comment chaque type de hook retourne le chemin.
Claude Code agit sur le succès du hook et le chemin retourné, et rejette systemMessage et continue.
Cet exemple crée une copie de travail SVN et imprime le chemin pour que Claude Code l’utilise. Remplacez l’URL du référentiel par la vôtre :
name du worktree de l’entrée JSON sur stdin, extrait une copie fraîche dans un nouveau répertoire, et imprime le chemin du répertoire. Le echo sur la dernière ligne est ce que Claude Code lit comme le chemin du worktree. Redirigez toute autre sortie vers stderr pour qu’elle n’interfère pas avec le chemin.
Entrée WorktreeCreate
En plus des champs d’entrée communs, les hooks WorktreeCreate reçoivent le champname. C’est un identifiant slug pour le nouveau worktree, soit spécifié par l’utilisateur, soit auto-généré, par exemple bold-oak-a3f2.
Sortie WorktreeCreate
Les hooks WorktreeCreate n’utilisent pas le modèle de décision permettre/bloquer standard. Au lieu de cela, le succès ou l’échec du hook détermine le résultat. Le hook doit retourner le chemin du répertoire worktree créé :- Hooks de commande (
type: "command") : imprimez le chemin comme la dernière ligne non-vide de stdout. Claude Code supprime les codes d’échappement ANSI avant de lire cette ligne, donc les bannières de démarrage du shell imprimées avant votreechosont ignorées. Redirigez toute autre sortie du hook vers stderr. - Hooks HTTP (
type: "http") : retournez{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }dans le corps de la réponse.
. ou .. dedans. Si le chemin résultant n’est pas un répertoire que Claude Code peut entrer, la session imprime une erreur nommant le chemin et quitte avec le code 1.
Claude Code refuse un chemin absolu qui contient des segments . ou .., et n’importe quel chemin qui passe par un lien symbolique en dessous de la racine du référentiel, parce qu’un lien symbolique commis au référentiel pourrait rediriger le worktree en dehors de lui. L’erreur nomme le composant rejeté. Retournez un chemin normalisé qui ne passe pas par un lien symbolique à l’intérieur du référentiel. Avant v2.1.216, la création du worktree suivait le chemin du hook sans ce dépistage.
WorktreeRemove
S’exécute quand un worktree est en cours de suppression. C’est la contrepartie de nettoyage de WorktreeCreate. L’événement se déclenche quand :- vous quittez une session
--worktreeet choisissez de la supprimer - un sous-agent avec
isolation: "worktree"se termine - vous supprimez une session en arrière-plan dont le worktree le hook a créé
git worktree remove. Si vous avez configuré un hook WorktreeCreate, associez-le à un hook WorktreeRemove pour contrôler le nettoyage des worktrees qu’il crée :
- Pas de hook WorktreeRemove : quand vous quittez une session
--worktreeet choisissez la suppression, Claude Code revient àgit worktree remove --forcesur le chemin que votre hook WorktreeCreate a retourné, donc un worktree que git reconnaît est supprimé. Un worktree que git ne reconnaît pas, par exemple un que votre hook a créé avec un système de contrôle de version non-git, reste sur le disque. Pour ce que la suppression d’une session en arrière-plan fait avec un worktree créé par hook, voir les règles de suppression de la vue agent. - Le hook quitte 0 : le worktree compte comme supprimé. Claude Code ne lit rien d’autre du hook, donc assurez-vous que votre hook a supprimé le répertoire.
- Le hook quitte non-zéro : la suppression échoue si le répertoire à
worktree_pathexiste toujours après, et le worktree reste sur le disque sans fallback git. Un hook qui a supprimé le répertoire avant de quitter non-zéro compte comme supprimé. Pour savoir comment l’échec est signalé, voir Entrée WorktreeRemove.
systemMessage et continue.
Pour une suppression de session en arrière-plan, Claude Code vérifie le chemin du worktree stocké avant d’exécuter le hook et refuse un chemin qui est un lien symbolique ou passe par un en dessous de la racine du référentiel. Le hook s’exécute pour un worktree qui contient toujours des fichiers uniquement quand vous confirmez la suppression dans la vue agent ; pour un tel worktree, claude rm garde la session et le worktree à la place. Avant v2.1.216, le hook s’exécutait sur le chemin stocké sans ces vérifications.
Claude Code passe le chemin retourné par WorktreeCreate comme worktree_path dans l’entrée du hook. Cet exemple lit ce chemin et supprime le répertoire :
Entrée WorktreeRemove
En plus des champs d’entrée communs, les hooks WorktreeRemove reçoivent le champworktree_path, qui est le chemin absolu du worktree en cours de suppression.
worktree_path existe toujours après, la suppression échoue :
- Le worktree reste sur le disque, et la commande du hook et stderr vont au journal de débogage.
- Si vous supprimiez une session en arrière-plan, la session reste aussi. Le message de refus dans la vue agent signale comment le hook s’est terminé, comme
exited 1, cite le début de son stderr, et dit si la suppression de la session à nouveau supprime le répertoire de toute façon.
PreCompact
S’exécute avant que Claude Code soit sur le point d’exécuter une opération de compaction. La valeur du matcher indique si la compaction a été déclenchée manuellement ou automatiquement :
Quittez avec le code 2 pour bloquer la compaction. Pour un
/compact manuel, le message stderr est montré à l’utilisateur. Vous pouvez également bloquer en retournant JSON avec "decision": "block".
Bloquer la compaction automatique a des effets différents selon quand elle se déclenche. Si la compaction a été déclenchée de manière proactive avant la limite de contexte, Claude Code la saute et la conversation continue sans compaction. Si la compaction a été déclenchée pour récupérer d’une erreur de limite de contexte déjà retourné par l’API, l’erreur sous-jacente surface et la requête actuelle échoue.
Claude Code rejette les champs systemMessage et continue d’un hook PreCompact.
Entrée PreCompact
En plus des champs d’entrée communs, les hooks PreCompact reçoiventtrigger et custom_instructions. Pour manual, custom_instructions contient ce que l’utilisateur passe dans /compact et est null quand il ne passe rien. Pour auto, custom_instructions est null.
PostCompact
S’exécute après que Claude Code termine une opération de compaction. Utilisez cet événement pour réagir à l’état compacté nouveau, par exemple pour enregistrer le résumé généré ou mettre à jour l’état externe. Claude Code rejette les champssystemMessage et continue d’un hook PostCompact.
Les mêmes valeurs de matcher s’appliquent que pour PreCompact :
Entrée PostCompact
En plus des champs d’entrée communs, les hooks PostCompact reçoiventtrigger et compact_summary. Le champ compact_summary contient le résumé de conversation généré par l’opération de compaction.
PreModelSwitch
S’exécute avant que Claude Code applique un changement de modèle que vous ou un client avez demandé. Utilisez-le pour bloquer un changement, exiger une confirmation, ou montrer quel changement coûtera avant qu’il se produise. PreModelSwitch nécessite Claude Code v2.1.251 ou ultérieur. Claude Code l’exécute pour ces requêtes :/model <name>et le sélecteur/model- Le sélecteur de modèle
Option+PouAlt+P - Le paramètre Model dans
/config - Activer le mode rapide quand cela change le modèle de la session
- Une requête
set_model, ou un changement de modèle dans une requêteapply_flag_settings, d’un hôte Agent SDK ou Remote Control
[1m]. Un alias comme opus, un ID de modèle daté, et un ID spécifique au fournisseur comme un ID de modèle Amazon Bedrock correspondent tous au nom canonique unique auquel ils se résolvent, donc claude-opus-5 couvre chaque orthographe d’Opus 5.
Quand Claude Code ne peut pas déterminer un nom canonique pour la cible, par exemple un ID de modèle personnalisé que seule votre passerelle LLM connaît, il exécute chaque hook PreModelSwitch indépendamment du matcher. Un hook qui bloque devrait donc vérifier to_model de son entrée plutôt que de compter uniquement sur le matcher.
Écrivez le matcher comme un nom exact, une liste séparée par | comme claude-opus-4-6|claude-opus-5, ou une expression régulière comme .*opus.*. Cet exemple utilise un matcher de nom exact et vérifie également to_model de l’entrée du hook, donc il refuse un changement vers Opus 4.6 en quittant avec le code 2 et laisse n’importe quelle autre cible passer :
- macOS/Linux
- Windows (PowerShell)
La commande vérifie
to_model avec jq :/model claude-opus-4-6 à partir d’une session exécutant un modèle différent. Claude Code garde le modèle actuel et signale qu’un hook PreModelSwitch a bloqué le changement, avec votre message comme raison.
Entrée PreModelSwitch
En plus des champs d’entrée communs, les hooks PreModelSwitch reçoivent les champs du tableau ci-dessous. Les cinq derniers décrivent quel changement de modèle coûte de renvoyer la conversation au nouveau modèle, donc un hook peut montrer ce chiffre avant que le changement se produise.
Cet exemple montre l’entrée pour
/model opus dans une session exécutant Sonnet 5 :
Contrôle de décision PreModelSwitch
Les hooksPreModelSwitch peuvent annuler le changement, demander à l’utilisateur de le confirmer, ou le laisser procéder. Le code de sortie 2 ou un decision: "block" de haut niveau annule le changement.
Pour un contrôle plus fin, retournez permissionDecision et permissionDecisionReason dans un objet hookSpecificOutput, comme sur PreToolUse. PreModelSwitch accepte "allow", "deny", et "ask". Il n’accepte pas "defer", updatedInput, ou additionalContext. Le tableau ci-dessous décrit les deux champs :
Seul
/model dans une session interactive peut montrer l’invite "ask". Sur chaque autre surface, y compris le mode non-interactif avec le drapeau -p, /config, et les requêtes set_model, Claude Code traite "ask" comme un refus.
Cet exemple demande à l’utilisateur de confirmer et cite le nombre de tokens de context_tokens :
deny > ask > allow.
Claude Code montre à l’utilisateur n’importe quel systemMessage que votre hook retourne indépendamment de la décision, donc un hook de rapport de coûts peut retourner {"systemMessage": "..."} et quitter 0.
Un hook PreModelSwitch qui ne répond pas avant son délai d’expiration bloque le changement. Sur PreToolUse, par contraste, un hook de commande qui expire laisse l’appel d’outil continuer. Le délai d’expiration par défaut pour cet événement est 30 secondes. PreModelSwitch exécute uniquement les hooks command, http, et mcp_tool, donc les défauts prompt et agent ne s’appliquent pas.
Un hook qui quitte avec un code autre que 0 ou 2 et n’imprime pas de décision JSON ne bloque pas : Claude Code montre son stderr et applique le changement, comme décrit sous Autres codes de sortie.
PostModelSwitch
S’exécute après que le modèle de la session change. Utilisez-le pour donner à Claude des conseils spécifiques au modèle sans éditer chaque CLAUDE.md, par exemple une instruction à l’échelle de l’organisation qui s’applique sur certains modèles. PostModelSwitch nécessite Claude Code v2.1.251 ou ultérieur. Il ne peut pas bloquer, parce que le modèle a déjà changé. Claude Code exécute les hooks PostModelSwitch après n’importe lequel de ces changements :- Un changement que vous ou un client avez demandé
- Un fallback de modèle automatique, qui change le modèle de la session
- Un paramètre comme
opusplanentrant ou quittant le mode plan - Claude Code restaurant le modèle quand vous reprenez une session
/model opus à partir d’une session Sonnet, puis demandez à Claude quels conseils il a sur le modèle actuel.
Entrée PostModelSwitch
Les hooks PostModelSwitch reçoivent les mêmes champs que PreModelSwitch, avechook_event_name défini à "PostModelSwitch" et deux valeurs source supplémentaires : "auto" pour un fallback automatique ou un autre changement que Claude Code a fait seul, et "resume" pour le modèle restauré quand vous reprenez une session.
requested_model est null quand source est "auto". Quand source est "resume", c’est le paramètre de modèle sauvegardé que Claude Code a restauré.
Contrôle de décision PostModelSwitch
Claude Code prend la sortie standard en texte brut de votre hook avec le code de sortie 0, ouadditionalContext de la sortie JSON, et la livre à Claude avec la requête suivante après le changement. En plus des champs de sortie JSON disponibles pour tous les hooks, vous pouvez retourner :
Si le hook n’a pas terminé dans les cinq secondes après que vous envoyiez la requête suivante, Claude Code envoie cette requête sans la sortie et l’attache à la requête suivante à la place. Si le modèle change plusieurs fois avant la requête suivante, Claude Code livre uniquement la sortie pour le modèle cible du dernier changement.
SessionEnd
S’exécute quand une session Claude Code se termine. Utile pour les tâches de nettoyage, l’enregistrement des statistiques de session, ou la sauvegarde de l’état de la session. Supporte les matchers pour filtrer par raison de sortie. Le champreason dans l’entrée du hook indique pourquoi la session s’est terminée :
Entrée SessionEnd
En plus des champs d’entrée communs, les hooks SessionEnd reçoivent un champreason indiquant pourquoi la session s’est terminée. Voir le tableau de raison ci-dessus pour toutes les valeurs.
systemMessage.
Les hooks SessionEnd ont un délai d’expiration par défaut de 1,5 secondes. Il s’applique quand vous quittez, exécutez /clear, ou changez de sessions avec /resume interactif. Vous pouvez donner à un hook plus de temps de deux façons :
timeoutpar hook : définisseztimeoutdans la configuration de ce hook. Le budget global augmente automatiquement pour correspondre autimeoutpar hook le plus élevé dans vos fichiers de paramètres, jusqu’à 60 secondes. Si vous augmentez le budget de cette façon, un hook sans son propretimeoutgarde toujours le défaut. Les délais d’expiration définis sur les hooks fournis par plugin ne lèvent pas le budget.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: définissez cette variable d’environnement en millisecondes pour remplacer le budget explicitement. La valeur que vous définissez devient également le délai d’expiration pour chaque hook sans son propretimeout.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS levait uniquement le budget global, et un hook sans son propre timeout était toujours annulé après 1,5 secondes.
Elicitation
S’exécute quand un serveur MCP demande l’entrée de l’utilisateur en cours de tâche. Par défaut, Claude Code montre un dialogue interactif pour que l’utilisateur réponde. Les hooks peuvent intercepter cette requête et répondre par programmation, ignorant entièrement le dialogue. Le champ matcher correspond au nom du serveur MCP.Entrée Elicitation
En plus des champs d’entrée communs, les hooks Elicitation reçoiventmcp_server_name, message, et les champs optionnels mode, url, elicitation_id, et requested_schema.
Pour l’élicitation en mode formulaire, le cas le plus courant :
Sortie Elicitation
Pour répondre par programmation sans montrer le dialogue, retournez un objet JSON avechookSpecificOutput :
Le code de sortie 2 refuse l’élicitation. Claude Code ne montre votre message stderr nulle part.
Claude Code agit sur
hookSpecificOutput de la sortie JSON d’un hook Elicitation et rejette systemMessage et continue.
ElicitationResult
S’exécute après qu’un utilisateur réponde à une élicitation MCP. Les hooks peuvent observer, modifier, ou bloquer la réponse avant qu’elle ne soit renvoyée au serveur MCP. Le champ matcher correspond au nom du serveur MCP.Entrée ElicitationResult
En plus des champs d’entrée communs, les hooks ElicitationResult reçoiventmcp_server_name, action, et les champs optionnels mode, elicitation_id, et content.
Sortie ElicitationResult
Pour remplacer la réponse de l’utilisateur, retournez un objet JSON avechookSpecificOutput :
Le code de sortie 2 bloque la réponse, changeant l’action effective à
decline. Claude Code ne montre votre message stderr nulle part.
Claude Code agit sur hookSpecificOutput de la sortie JSON d’un hook ElicitationResult et rejette systemMessage et continue.
Hooks basés sur des prompts
En plus des hooks de commande, HTTP et MCP tool, Claude Code supporte les hooks basés sur des prompts (type: "prompt") qui utilisent un LLM pour évaluer s’il faut autoriser ou bloquer une action, et les hooks d’agent (type: "agent") qui lancent un vérificateur agentique avec accès aux outils. Tous les événements ne supportent pas tous les types de hooks.
Les événements qui supportent les cinq types de hooks (command, http, mcp_tool, prompt et agent) :
PermissionDeniedPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
PermissionRequest supporte les hooks command, http, mcp_tool et prompt mais pas les hooks agent. Si vous configurez un hook d’agent sur cet événement, Claude Code le saute et le flux de permission se poursuit sans changement. Pour autoriser ou refuser à partir d’un hook, retournez l’objet de décision à partir d’un hook de commande ou HTTP.
Les événements qui supportent les hooks command, http et mcp_tool mais pas prompt ou agent :
ConfigChangeCwdChangedDirectoryAddedElicitationElicitationResultFileChangedInstructionsLoadedMessageDisplayNotificationPostCompactPostModelSwitchPreCompactPreModelSwitchSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart et Setup supportent les hooks command et mcp_tool, et les champs des hooks MCP tool décrivent quand leurs hooks mcp_tool s’exécutent. Ils ne supportent pas les hooks http, prompt ou agent.
Comment fonctionnent les hooks basés sur des prompts
Au lieu d’exécuter une commande Bash, les hooks basés sur des prompts :- Envoient l’entrée du hook et votre prompt à un modèle Claude, par défaut celui que Claude Code utilise pour la fonctionnalité en arrière-plan
- Le LLM répond avec JSON structuré contenant une décision
- Claude Code traite automatiquement la décision
Configuration des hooks de prompt
Définisseztype à "prompt" et fournissez une chaîne prompt au lieu d’une command. Utilisez le placeholder $ARGUMENTS pour injecter les données d’entrée JSON du hook dans votre texte de prompt.
Ce hook Stop demande au LLM d’évaluer si toutes les tâches sont complètes avant d’autoriser Claude à terminer :
Schéma de réponse
Le LLM doit répondre avec JSON contenant :
Ce qui se passe sur
ok: false dépend de l’événement :
StopetSubagentStop: la raison est renvoyée à Claude comme sa prochaine instruction et le tour continue, sauf si la réponse définit égalementimpossible: true, auquel cas Claude Code autorise l’arrêt et le tour se terminePreToolUse: l’appel d’outil est refusé ; par défaut le tour se termine et la raison de refus apparaît dans le chat comme une ligne d’avertissement. DéfinissezcontinueOnBlock: truepour renvoyer la raison à Claude comme l’erreur de l’outil afin qu’il puisse s’ajuster et continuer, équivalent à un hook de commande avecpermissionDecision: "deny". Avant v2.1.210, la raison de refus était renvoyée à Claude comme l’erreur de l’outil et le tour continuaitPostToolUse: par défaut le tour se termine et la raison apparaît dans le chat comme une ligne d’avertissement. DéfinissezcontinueOnBlock: truepour renvoyer la raison à Claude et continuer le tour à la placePostToolBatch,UserPromptSubmitetUserPromptExpansion: le tour se termine et la raison apparaît comme une ligne d’avertissement. Ces événements terminent le tour surdecision: "block"indépendamment decontinuePostToolUseFailureetTaskCreated: la raison est retournée à Claude comme une erreur d’outil et le tour continue, indépendamment decontinueOnBlockTaskCompleted: lorsqu’il se déclenche parce qu’une tâche est marquée comme complétée pendant un tour, la raison est retournée à Claude comme une erreur d’outil et le tour continue, indépendamment decontinueOnBlock. Lorsqu’il se déclenche parce qu’un coéquipier s’arrête, il se comporte commeTeammateIdleet arrête le coéquipier par défautTeammateIdle: par défaut le coéquipier s’arrête et la raison apparaît comme une ligne d’avertissement. DéfinissezcontinueOnBlock: truepour renvoyer la raison au coéquipier et le garder actif à la placePermissionRequest:ok: falsen’a aucun effet. Pour refuser une approbation d’un hook, utilisez un hook de commande retournanthookSpecificOutput.decision.behavior: "deny"PermissionDenied:ok: falsen’a aucun effet car le refus a déjà eu lieu. La seule sortie que cet événement lit esthookSpecificOutput.retry, que les hooks de prompt et d’agent ne peuvent pas définir. Ils s’exécutent sur cet événement, mais leur sortie est ignorée. Utilisez un hook de commande pour retournerretry
Vérifier plusieurs conditions avant d’arrêter
Ce hookStop utilise un prompt détaillé pour vérifier trois conditions avant d’autoriser Claude à s’arrêter. Les hooks SubagentStop utilisent le même format pour évaluer si un subagent doit s’arrêter. Si le modèle retourne "ok": false parce que la condition n’est pas encore satisfaite, Claude continue de travailler avec la raison fournie comme sa prochaine instruction :
Hooks basés sur des agents
Les hooks basés sur des agents (type: "agent") sont comme les hooks basés sur des prompts mais avec accès aux outils multi-tours. Au lieu d’un seul appel LLM, un hook d’agent lance un subagent qui peut lire des fichiers, rechercher du code et inspecter la codebase pour vérifier les conditions. Les hooks d’agent supportent les mêmes événements que les hooks basés sur des prompts, sauf PermissionRequest.
Comment fonctionnent les hooks d’agent
Lorsqu’un hook d’agent se déclenche :- Claude Code lance un subagent avec votre prompt et l’entrée JSON du hook
- Le subagent peut utiliser des outils comme Read, Grep et Glob pour enquêter
- Après jusqu’à 50 tours, le subagent retourne une décision structurée
{ "ok": true/false } - Claude Code autorise l’action si
okesttrue. Siokestfalse, Claude Code traite le blocage de la même manière qu’un hook de prompt aveccontinueOnBlock: truesur cet événement, comme indiqué sous Schéma de réponse
Configuration des hooks d’agent
Définisseztype à "agent" et fournissez une chaîne prompt, en utilisant $ARGUMENTS comme placeholder pour l’entrée JSON du hook. Les champs de configuration sont les mêmes que les hooks de prompt, sauf que les hooks d’agent ont un délai d’expiration par défaut plus long de 60 secondes et aucun champ continueOnBlock.
Le schéma de réponse est { "ok": true } pour autoriser ou { "ok": false, "reason": "..." } pour bloquer. Sur ok: false, Claude Code traite un hook d’agent de la même manière qu’il traite un hook de prompt avec continueOnBlock: true sur le même événement ; les hooks d’agent n’ont pas de champ continueOnBlock et ne supportent pas le champ impossible du hook de prompt.
Ce hook Stop vérifie que tous les tests unitaires réussissent avant d’autoriser Claude à terminer :
Exécuter les hooks en arrière-plan
Par défaut, les hooks bloquent l’exécution de Claude jusqu’à ce qu’ils se terminent. Pour les tâches longues comme les déploiements, les suites de tests ou les appels API externes, définissez"async": true pour exécuter le hook en arrière-plan tandis que Claude continue de travailler. Les hooks asynchrones ne peuvent pas bloquer ou contrôler le comportement de Claude : les champs de réponse comme decision, permissionDecision et continue n’ont aucun effet, car l’action qu’ils auraient contrôlée s’est déjà produite.
Configurer un hook asynchrone
Ajoutez"async": true à la configuration d’un hook de commande pour l’exécuter en arrière-plan sans bloquer Claude. Ce champ n’est disponible que sur les hooks type: "command".
Ce hook exécute un script de test après chaque appel d’outil Write. Claude continue de travailler immédiatement tandis que run-tests.sh s’exécute. Lorsque le script se termine, sa sortie est livrée au tour de conversation suivant :
timeout sur celui-ci. Claude Code applique toujours le timeout sur un hook que vous exécutez avec asyncRewake.
Claude Code livre les résultats d’un hook asynchrone uniquement pendant que la session s’exécute :
- En mode non-interactif avec le drapeau
-p, Claude Code tue tout hook asynchrone encore en cours d’exécution lors du démontage et le finalise avec le résultatcancelled - Si le travail de votre hook doit survivre à une session
claude -p, démarrez un processus complètement détaché à partir de celui-ci
Comment les hooks asynchrones s’exécutent
Lorsqu’un hook asynchrone se déclenche, Claude Code démarre le processus du hook et continue immédiatement sans attendre qu’il se termine. Le hook reçoit la même entrée JSON via stdin qu’un hook synchrone. Après la sortie du processus en arrière-plan, Claude Code livre les champsadditionalContext et systemMessage de la réponse JSON du hook à Claude au tour de conversation suivant. Contrairement au systemMessage d’un hook synchrone, aucun de ces champs ne vous est montré.
Claude Code valide que la réponse JSON respecte le même schéma de sortie que les hooks synchrones, et supprime tout champ dont la valeur a le mauvais type, comme un systemMessage qui n’est pas une chaîne de caractères, au lieu de le livrer. Exécutez avec --debug pour voir un avertissement nommant chaque champ supprimé. Avant la v2.1.202, une sortie JSON malformée d’un hook asynchrone pouvait faire planter la session, et le plantage s’est reproduit chaque fois que la session a été reprise.
Les notifications d’achèvement des hooks asynchrones sont supprimées par défaut. Pour les voir, activez le mode verbeux avec Ctrl+O ou démarrez Claude Code avec --verbose.
Exécuter les tests après les modifications de fichiers
Ce hook démarre une suite de tests en arrière-plan chaque fois que Claude écrit un fichier, puis rapporte les résultats à Claude lorsque les tests se terminent. Enregistrez ce script dans.claude/hooks/run-tests-async.sh dans votre projet et rendez-le exécutable avec chmod +x :
.claude/settings.json dans la racine de votre projet. Le drapeau async: true permet à Claude de continuer à travailler pendant que les tests s’exécutent :
Limitations
Les hooks asynchrones ont des contraintes supplémentaires par rapport aux hooks synchrones :- La sortie du hook est livrée au tour de conversation suivant. Si la session est inactive, la réponse attend jusqu’à la prochaine interaction utilisateur. Exception : un hook
asyncRewakequi quitte avec le code 2 réveille Claude immédiatement même lorsque la session est inactive. - Chaque exécution crée un processus en arrière-plan séparé. Il n’y a pas de déduplication sur plusieurs déclenchements du même hook asynchrone.
Considérations de sécurité
Avertissement
Confiance de l’espace de travail
Claude Code vérifie la confiance de l’espace de travail avant d’exécuter tout hook à partir d’un fichier de paramètres. Ce qui compte comme approuvé dépend du type de session :- Session interactive : Claude Code retient les hooks de tous les fichiers de paramètres, y compris votre propre
~/.claude/settings.json, jusqu’à ce que vous acceptiez le dialogue de confiance de l’espace de travail pour le dossier, ou pour un répertoire parent dont la confiance s’étend à celui-ci - Session
-pou SDK : Claude Code n’affiche jamais le dialogue et traite le dossier comme approuvé, donc les hooks validés dans le.claude/settings.jsond’un référentiel s’exécutent dans un dossier que vous n’avez jamais approuvé
claude -p sur un référentiel que vous n’avez pas écrit, examinez ses fichiers de paramètres .claude/, commencez par --bare, ou désactivez les hooks pour cette exécution avec --settings '{"disableAllHooks": true}'. Les hooks de frontmatter dans un sous-agent de projet suivent une règle plus stricte que les hooks de fichier de paramètres. Ce qui s’exécute avant que vous approuviez un dossier énumère chaque type de contenu de référentiel par type de session.
Meilleures pratiques de sécurité
Gardez ces pratiques à l’esprit lors de l’écriture de hooks :- Validez et nettoyez les entrées : ne faites jamais confiance aux données d’entrée aveuglément
- Citez toujours les variables shell : utilisez
"$VAR"pas$VAR - Bloquez la traversée de répertoires : vérifiez les
..dans les chemins de fichiers - Utilisez les chemins absolus : spécifiez les chemins complets pour les scripts. En forme exec, utilisez
${CLAUDE_PROJECT_DIR}et le chemin n’a pas besoin de guillemets. En forme shell, enveloppez-le dans des guillemets doubles - Ignorez les fichiers sensibles : évitez
.env,.git/, les clés, etc.
Outil PowerShell sur Windows
Sur Windows, vous pouvez exécuter les hooks individuels dans PowerShell en définissant"shell": "powershell" sur un hook de commande. Claude Code détecte automatiquement pwsh.exe, l’exécutable PowerShell 7 et versions ultérieures, et bascule vers powershell.exe pour Windows PowerShell 5.1.
${CLAUDE_PROJECT_DIR} ou $env:CLAUDE_PROJECT_DIR. À partir de la v2.1.198, Claude Code réécrit les placeholders ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} et ${CLAUDE_PLUGIN_DATA} dans une commande PowerShell en forme shell vers la forme ${env:NAME} de PowerShell, que le hook soit défini dans settings.json, un plugin ou une skill. PowerShell résout ensuite la valeur à partir de l’environnement exporté après l’analyse, donc le placeholder fonctionne à l’intérieur des chaînes entre guillemets doubles mais pas à l’intérieur des chaînes entre guillemets simples, où PowerShell n’étend jamais les variables.
Avant la v2.1.198, cette réécriture s’appliquait uniquement aux hooks de plugin. Sur les versions antérieures, un hook settings.json a besoin de la forme $env: ou de la forme exec, où ${CLAUDE_PROJECT_DIR} est substitué dans chaque élément args indépendamment de l’endroit où le hook est défini.
N’écrivez pas l’orthographe nue $CLAUDE_PROJECT_DIR dans un hook PowerShell. PowerShell l’analyse comme une variable locale indéfinie et la résout en $null, ce qui laisse le chemin du script sans son préfixe de racine de projet. Claude Code ne réécrit pas cette forme ; il enregistre plutôt un avertissement dans le journal de débogage.
L’exemple ci-dessous montre un hook settings.json qui exécute un script de projet avec la forme $env:, qui fonctionne sur chaque version :
Déboguer les hooks
Les détails d’exécution des hooks sont écrits dans le fichier journal de débogage. Démarrez Claude Code avecclaude --debug-file <path> pour écrire le journal à un emplacement connu, ou exécutez claude --debug et lisez le journal à ~/.claude/debug/<session-id>.txt. Le drapeau --debug n’imprime pas sur le terminal.
Par exemple, un hook PostToolUse sur Write dont la commande affiche hook-ran produit des entrées comme :
CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose pour voir des lignes de journal supplémentaires telles que les comptes de matcher de hook et la correspondance de requête.
Pour dépanner les problèmes courants comme les hooks qui ne se déclenchent pas, les hooks Stop qui continuent à bloquer, ou les erreurs de configuration, consultez Limitations et dépannage dans le guide. Pour une procédure de diagnostic plus large couvrant /context, /doctor et la précédence des paramètres, consultez Déboguer votre configuration.