Cycle de vie des hooks
Les hooks se déclenchent à des points spécifiques pendant une session Claude Code. 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
Comment un hook se résout
Pour voir comment ces éléments s’assemblent, considérez ce hookPreToolUse qui bloque les commandes shell destructrices. Le matcher se limite aux appels d’outil Bash et la condition if se limite davantage aux 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 :
Bash "rm -rf /tmp/build". 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 :
Pour plus de détails sur la résolution des fichiers de paramètres, consultez paramètres. Les administrateurs d’entreprise peuvent utiliser
allowManagedHooksOnly pour bloquer les hooks utilisateur, projet et plugin. Les hooks des plugins forcément activés dans les paramètres gérés enabledPlugins sont exempts, donc les administrateurs peuvent distribuer les hooks vérifiés via un marketplace d’organisation. Consultez Configuration des hooks.
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.
Les séparateurs par virgule et la tolérance des espaces blancs environnants nécessitent Claude Code v2.1.191 ou ultérieur.
Les traits d’union dans l’ensemble de correspondance exacte nécessitent Claude Code v2.1.195 ou ultérieur. Sur les versions antérieures, un nom avec trait d’union comme code-reviewer est évalué comme une expression régulière non ancrée, donc il se déclenche également pour senior-code-reviewer ; ancrez-le comme ^code-reviewer$ sur ces versions pour correspondre uniquement à ce nom.
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 :
Le matcher s’exécute sur un champ de l’entrée JSON que Claude Code envoie à votre hook sur stdin. Pour les événements d’outil, ce champ est
tool_name. 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 :
UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay et CwdChanged ne supportent pas les matchers et se déclenchent toujours à chaque occurrence. Si vous ajoutez un champ matcher à ces événements, 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__brave-search est évalué comme une expression régulière non ancrée et correspond à chaque outil de ce serveur. La forme mcp__brave-search__.* fonctionne sur chaque version.
Les outils d’un serveur MCP fourni par un plugin utilisent un segment de serveur limité qui inclut le nom du plugin : 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 déjà connecté. 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 une décision oui/non 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.
args, et les hooks HTTP sont dédupliqués par URL.
Les gestionnaires s’exécutent dans le répertoire courant avec l’environnement de Claude Code. La variable d’environnement $CLAUDE_CODE_REMOTE est définie à "true" dans les environnements web distants et n’est pas définie dans le CLI local. À partir de v2.1.199, $CLAUDE_CODE_BRIDGE_SESSION_ID est défini à 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.
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.
Le filtre échoue également ouvert, exécutant votre hook indépendamment du modèle, lorsque la commande Bash ne peut pas être analysée. 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 : les réponses non-2xx, les défaillances de connexion et les délais d’expiration produisent tous des erreurs non-bloquantes qui permettent à l’exécution de continuer. Pour bloquer un appel d’outil ou refuser une permission, retournez une réponse 2xx avec un corps JSON contenant decision: "block" ou un hookSpecificOutput avec permissionDecision: "deny".
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 :
La sortie textuelle de l’outil est traitée comme stdout d’un hook de commande : si elle s’analyse comme une sortie JSON valide, elle est traitée comme une décision, sinon elle est affichée en tant que texte brut. Si le serveur nommé n’est pas connecté, ou si l’outil retourne
isError: true, le hook produit une erreur non-bloquante et l’exécution continue.
Les hooks de l’outil MCP sont disponibles sur chaque événement de hook une fois que Claude Code s’est connecté à vos serveurs MCP. SessionStart et Setup se déclenchent généralement avant que les serveurs ne finissent de se connecter, donc les hooks sur ces événements doivent s’attendre à l’erreur « non connecté » à la première exécution.
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é :
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. 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. Change à chaque mise à jour du plugin.${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.
args comme un argument sans tokenisation shell, donc les chemins avec des espaces ou des caractères spéciaux n’ont besoin d’aucun guillemet. En forme shell, enveloppez chaque placeholder entre guillemets doubles.
- 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. Ces hooks sont limités au cycle de vie du composant et ne s’exécutent que lorsque ce composant est actif. Tous les événements de hook sont supportés. Pour les subagents, les hooksStop sont automatiquement convertis en SubagentStop puisque c’est l’événement qui se déclenche lorsqu’un subagent se termine.
Les hooks utilisent le même format de configuration que les hooks basés sur les paramètres mais sont limités à la durée de vie du composant et nettoyés lorsqu’il se termine.
Ce skill définit un hook PreToolUse qui exécute un script de validation de sécurité avant chaque commande Bash :
Le menu /hooks
Tapez /hooks dans Claude Code pour ouvrir un navigateur en lecture seule pour vos hooks configurés. Le menu affiche chaque événement de hook avec un nombre de hooks configurés, vous permet d’explorer les matchers et affiche les détails complets de chaque gestionnaire de hook. Utilisez-le pour vérifier la configuration, vérifier à partir de quel fichier de paramètres un hook provient ou inspecter la commande, le prompt ou l’URL d’un hook.
Le menu affiche les cinq types de hooks : command, prompt, agent, http et mcp_tool. Chaque hook est étiqueté avec un préfixe [type] et une source indiquant où il a été défini :
User: de~/.claude/settings.jsonProject: de.claude/settings.jsonLocal: de.claude/settings.local.jsonPlugin: duhooks/hooks.jsond’un pluginSession: enregistré en mémoire pour la session actuelleBuilt-in: enregistré en interne par Claude Code
Désactiver ou supprimer les hooks
Pour supprimer un hook, supprimez son entrée du fichier de paramètres JSON. Pour désactiver temporairement tous les hooks sans les supprimer, définissez"disableAllHooks": true dans votre fichier de paramètres. 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.
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 à partir de v2.1.139. 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. 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 reçoivent un champ model, et sa présence n’est pas garantie. Il n’y a pas de variable d’environnement $CLAUDE_MODEL. Un processus de hook hérite de l’environnement parent, il peut donc lire $ANTHROPIC_MODEL si vous le définissez dans votre shell, mais cette valeur ne change pas lorsque vous changez de modèle avec /model pendant une session. Un ensemble de variables n’est pas hérité : Claude Code supprime les variables d’exportateur OTEL_* de chaque sous-processus qu’il génère, y compris les hooks.
Par exemple, un hook PreToolUse pour une commande Bash reçoit ceci sur stdin :
tool_name et tool_input 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. Exit 0 signifie succès. Claude Code analyse stdout pour les champs de sortie JSON. La sortie JSON n’est traitée que sur exit 0. Pour la plupart des événements, stdout est écrit dans le journal de débogage mais n’est pas affiché dans la transcription. Les exceptions sontUserPromptSubmit, UserPromptExpansion et SessionStart, où stdout est ajouté comme contexte que Claude peut voir et sur lequel agir.
Exit 2 signifie une erreur bloquante. Claude Code ignore stdout et tout JSON qu’il contient. À la place, le texte stderr est renvoyé à Claude comme message d’erreur. L’effet dépend de l’événement : PreToolUse bloque l’appel d’outil, UserPromptSubmit rejette le prompt, et ainsi de suite. Consultez comportement du code de sortie 2 pour la liste complète.
Tout autre code de sortie est une erreur non-bloquante pour la plupart des événements de hook. La transcription affiche un avis <hook name> hook error suivi de la première ligne de stderr, afin que vous puissiez identifier la cause sans --debug. L’exécution continue et le stderr complet est écrit dans le journal de débogage.
Par exemple, un script de commande de hook qui bloque les commandes Bash dangereuses :
Comportement du code de sortie 2 par événement
Le code de sortie 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, Setup et SubagentStart, le stderr du code de sortie 2 s’affiche dans la transcription comme un avis <hook name> hook error, de la même manière qu’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.
À partir de Claude Code v2.1.199, SessionStart, Setup et SubagentStart affichent le stderr du code de sortie 2 dans la transcription. Les versions antérieures l’écrivaient uniquement dans le journal de débogage.
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 :- 2xx avec un corps vide : succès, équivalent à exit code 0 sans sortie
- 2xx avec un corps en texte brut : succès, le texte est ajouté comme contexte
- 2xx avec un corps JSON : succès, analysé en utilisant le même schéma sortie JSON que les hooks de commande
- Statut non-2xx : erreur non-bloquante, l’exécution continue
- Défaillance de connexion ou délai d’expiration : erreur non-bloquante, l’exécution continue
Sortie JSON
Les codes de sortie vous permettent d’autoriser ou de bloquer, 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.Vous devez choisir une approche par hook, pas les deux : soit utiliser les codes de sortie seuls pour signaler, soit quitter 0 et imprimer JSON pour un contrôle structuré. Claude Code ne traite JSON que sur exit 0. Si vous quittez 2, tout JSON est ignoré.
additionalContext, systemMessage et stdout brut, sont plafonnées à 10 000 caractères. La sortie qui dépasse cette limite est enregistrée dans un fichier et remplacée par un aperçu et un chemin de fichier, de la même manière que les grands résultats d’outils sont gérés.
L’objet JSON supporte trois types de champs :
- Champs universels comme
continuefonctionnent sur tous les événements. Ceux-ci sont listés dans le tableau ci-dessous. 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 indépendamment du type d’événement :
Émettre des notifications de terminal
Le champterminalSequence nécessite Claude Code v2.1.141 ou ultérieur.
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
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. Sur Windows, construisez la chaîne d’échappement dans PowerShell ou un script et émettez le même objet JSON.
terminalSequence est le remplacement pris en charge pour les hooks qui écrivaient précédemment des séquences d’échappement directement sur /dev/tty. La liste blanche est restreinte aux séquences qui ne peuvent pas déplacer le curseur ou modifier les couleurs, afin qu’un hook ne puisse jamais corrompre une invite à l’écran.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, Setup 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
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 complet dans un fichier du répertoire de session et transmet à Claude le chemin du fichier avec un court aperçu à la place.
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.
Une fois injecté, le texte est enregistré dans la transcription de session. Pour les événements mid-session comme PostToolUse ou UserPromptSubmit, la reprise avec --continue ou --resume 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 à la reprise. Les hooks SessionStart s’exécutent à nouveau à la reprise avec source défini sur "resume", 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
Utilisé par
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange et PreCompact. La seule valeur 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 session à travers la boucle agentique jusqu’à la fin de 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 lorsque 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 codebase, ou pour configurer les variables d’environnement. Pour le contexte statique qui ne nécessite pas de script, utilisez CLAUDE.md à la place. SessionStart s’exécute à chaque session, donc gardez ces hooks rapides. Seuls les hookstype: "command" et type: "mcp_tool" sont supportés.
La valeur du matcher correspond à la façon dont la session a été initiée :
Entrée SessionStart
En plus des champs d’entrée communs, les hooks SessionStart reçoiventsource et optionnellement model, agent_type et session_title :
Contrôle de décision SessionStart
Tout texte que votre script de hook imprime sur stdout est ajouté comme contexte pour Claude. En plus des champs de sortie JSON disponibles pour tous les hooks, vous pouvez retourner ces champs spécifiques à l’événement :suppressOutput ou sessionTitle.
Utilisez reloadSkills lorsqu’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 :
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 déclarations 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
Se déclenche uniquement lorsque vous lancez Claude Code avec--init-only, ou avec --init ou --maintenance en mode non-interactif avec le drapeau -p. Il ne se déclenche 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 session. Pour l’initialisation par session, utilisez SessionStart à la place.
La valeur du matcher correspond au drapeau CLI qui a déclenché le hook :
--init-only exécute les hooks Setup et les hooks SessionStart avec le matcher startup, puis quitte sans démarrer une conversation. --init et --maintenance déclenchent les hooks Setup uniquement lorsqu’ils sont combinés avec -p ; dans une session interactive, ces deux drapeaux ne déclenchent actuellement pas les hooks Setup.
Parce que Setup ne se déclenche 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 une skill qui teste ${CLAUDE_PLUGIN_DATA}/node_modules et exécute npm install si absent. Consultez le répertoire de données persistantes pour savoir où stocker les dépendances installées.
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. Tout code de sortie non-zéro, y compris 2, affiche stderr à l’utilisateur comme un avis<hook name> hook error, et l’exécution continue. En mode non-interactif, la sortie du hook n’apparaît que lorsque vous lancez avec --verbose.
Pour transmettre des informations au contexte de Claude, retournez additionalContext dans la sortie JSON ; le stdout brut est écrit uniquement dans le journal de débogage. En plus des champs de sortie JSON disponibles pour tous les hooks, vous pouvez retourner ces champs spécifiques à l’événement :
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" et type: "mcp_tool" sont supportés.
InstructionsLoaded
Se déclenche lorsqu’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 lorsque les fichiers sont chargés avec paresse, par exemple lorsque Claude accède à un sous-répertoire qui contient un CLAUDE.md imbriqué ou lorsque 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é.
Le matcher s’exécute sur 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 paresseux.
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. Utilisez cet événement pour la journalisation d’audit, le suivi de conformité ou l’observabilité.UserPromptSubmit
S’exécute lorsque l’utilisateur soumet un prompt, avant que Claude ne le traite. Cela vous permet d’ajouter du contexte supplémentaire basé sur le prompt/conversation, de valider les prompts ou de bloquer certains types de prompts. 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élai par défaut de 600 secondes pour ces types sur d’autres événements. Parce que ce hook s’exécute avant chaque prompt et bloque le traitement du modèle jusqu’à son achèvement, 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.
Un hook UserPromptSubmit qui atteint son délai d’expiration est annulé et sa sortie, y compris tout additionalContext, est supprimée. Le prompt atteint toujours Claude sans ce contexte. À partir de v2.1.196, la transcription affiche un avis nommant le hook, le délai d’expiration qui s’est déclenché et que la sortie a été supprimée. Les versions antérieures annulent le hook sans avis.
Un hook de rappel Agent SDK sur UserPromptSubmit qui atteint son délai d’expiration bloque le prompt avec un message nommant le hook et le délai d’expiration, car 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.
Contrôle de décision UserPromptSubmit
Les hooksUserPromptSubmit peuvent contrôler si un prompt utilisateur est traité et ajouter du contexte. Tous les champs de sortie JSON sont disponibles.
Il y a deux façons d’ajouter du contexte à la conversation sur exit code 0 :
- Stdout en texte brut : tout texte non-JSON écrit sur stdout est ajouté comme contexte
- JSON avec
additionalContext: utilisez le format JSON ci-dessous pour plus de contrôle. Le champadditionalContextest ajouté comme contexte
additionalContext est injectée comme un rappel système que Claude lit sans entrée de transcription visible.
Pour bloquer un prompt, retournez un objet JSON avec decision défini à "block" :
UserPromptExpansion
S’exécute lorsqu’une commande slash tapée par l’utilisateur se développe en un prompt 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 lorsque 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 slash command de type prompt.
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.
MessageDisplay
S’exécute pendant qu’un message d’assistant se diffuse à 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 affiche à 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’outil 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 diffuse du texte ; les messages sans texte, comme les réponses d’appel d’outil uniquement, 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 se diffuse, 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 modifier ce qui est stocké dans la transcription ou envoyé à Claude.
Cet exemple supprime la mise en forme 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 du
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 Le script a besoin de
.claude/hooks/plain-display.sh dans votre projet et rendez-le exécutable avec chmod +x :jq sur votre PATH.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 de l’outil et avant le traitement de l’appel d’outil. Correspond au nom de l’outil :Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode et tout nom d’outil MCP.
Utilisez Contrôle de décision PreToolUse pour autoriser, refuser, demander ou différer l’appel d’outil.
Entrée PreToolUse
En plus des champs d’entrée communs, les hooks PreToolUse reçoiventtool_name, tool_input et tool_use_id. Les champs tool_input dépendent de l’outil :
Exécute les commandes shell.
Crée ou écrase un fichier.
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 sur le web.
Lance un subagent.
Dans
PostToolUse, tool_response pour un appel Agent complété porte le texte final du subagent ainsi que la télémétrie d’utilisation. Lisez ces champs pour enregistrer le coût par subagent à partir d’un hook :
Pour les subagents en arrière-plan, l’outil retourne immédiatement après le lancement du subagent, donc
tool_response ne porte aucun champ d’utilisation. Il a status: "async_launched", agentId, description, prompt, outputFile et resolvedModel à la place.
Le champ resolvedModel nomme le modèle sur lequel le subagent fonctionne réellement, qui peut différer de la valeur model dans tool_input, comme lorsque availableModels ou un autre remplacement s’applique. Il nécessite Claude Code v2.1.174 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 ne quitte le mode plan. Claude écrit le plan dans un fichier sur le disque avant d’appeler l’outil, donc l’
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 transmettre 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 au niveau supérieur, PreToolUse retourne sa décision à l’intérieur d’un objet hookSpecificOutput. Cela lui donne un contrôle plus riche : quatre résultats (autoriser, refuser, demander ou différer) plus la capacité de modifier l’entrée de l’outil avant l’exécution.
Lorsque plusieurs hooks PreToolUse retournent des décisions différentes, la précédence est
deny > defer > ask > allow.
Lorsqu’un hook retourne "ask", le dialogue de permission affiché à l’utilisateur inclut un libellé identifiant d’où provient le hook : par exemple, [User], [Project], [Plugin] ou [Local]. Cela aide les utilisateurs à comprendre quelle source de configuration demande une confirmation.
AskUserQuestion et ExitPlanMode nécessitent une interaction utilisateur et bloquent normalement en mode non-interactif avec le drapeau -p. 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 afin que l’outil s’exécute sans inviter. 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.
Les outils connecteur que votre organisation a défini à ask invitent même lorsqu’un hook retourne "allow".
À 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 dialogue d’approbation avec "allow", avec ou sans updatedInput, car Claude Code ne peut pas confirmer que le hook a collecté l’interaction dont l’outil a besoin.
PreToolUse utilisait auparavant les champs
decision et reason au niveau supérieur, mais ceux-ci sont dépréciés pour cet événement. Utilisez hookSpecificOutput.permissionDecision et hookSpecificOutput.permissionDecisionReason à la place. Les valeurs dépréciées "approve" et "block" correspondent à "allow" et "deny" respectivement. Les autres événements comme PostToolUse et Stop continuent d’utiliser decision et reason au niveau supérieur comme format actuel.Différer un appel d’outil pour plus tard
"defer" est pour les intégrations qui exécutent claude -p en tant que sous-processus et lisent sa sortie JSON, comme une application Agent SDK ou une interface utilisateur personnalisée construite sur Claude Code. Il 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 une question à l’utilisateur, mais il n’y a pas de terminal pour répondre. Le cycle 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>. 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. Si la réponse n’est pas prête lorsque vous reprenez, le hook peut retourner "defer" à nouveau et le processus quitte de la même manière. Le processus appelant contrôle quand casser la boucle en retournant finalement "allow" ou "deny" du hook.
"defer" ne fonctionne que lorsque Claude fait un seul appel d’outil dans le tour. Si Claude fait plusieurs appels d’outil à la fois, "defer" est ignoré avec un avertissement et l’outil procède à travers le flux de permission normal. La contrainte existe car la reprise ne peut réexécuter qu’un seul outil : il n’y a aucun moyen de différer un appel d’une batch sans laisser les autres non résolus.
Si l’outil différé n’est plus disponible lorsque vous reprenez, le processus quitte avec stop_reason: "tool_deferred_unavailable" et is_error: true avant que le hook ne se déclenche. Cela se produit lorsqu’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 afin que vous puissiez identifier quel outil a disparu.
--resume restaure le mode de permission qui était actif lorsque l’outil a été différé, donc vous n’avez pas besoin de passer --permission-mode à nouveau. Les exceptions sont plan et bypassPermissions, qui ne sont jamais reportés. Passer --permission-mode explicitement lors de la reprise remplace la valeur restaurée.PermissionRequest
S’exécute lorsque l’utilisateur est montré un dialogue de permission. Utilisez Contrôle de décision PermissionRequest pour autoriser ou refuser au nom de l’utilisateur. 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. Un tableau optionnel permission_suggestions contient les options « toujours autoriser » que l’utilisateur verrait normalement dans le dialogue de permission. La différence est quand le hook se déclenche : les hooks PermissionRequest s’exécutent lorsqu’un dialogue de permission est sur le point d’être montré à l’utilisateur, tandis que les hooks PreToolUse s’exécutent avant l’exécution de l’outil indépendamment du statut de permission.
Contrôle de décision PermissionRequest
Les hooksPermissionRequest peuvent autoriser 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 :
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ù la modification est écrite.
setMode avec bypassPermissions ne prend effet que si la session a été lancée 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, et le mode n’est pas désactivé par permissions.disableBypassPermissionsMode. Sinon la mise à jour est un non-op. bypassPermissions n’est jamais persisté comme defaultMode indépendamment de destination.destination sur chaque entrée détermine si la modification 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, ce qui équivaut à l’utilisateur sélectionnant cette option « toujours autoriser » dans le dialogue.
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.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.
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 :
PostToolUseFailure
S’exécute lorsqu’une exécution d’outil échoue : l’outil a levé une erreur ou un outil MCP a retourné un résultat d’erreur. Utilisez ceci pour enregistrer les défaillances, 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’outil 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 ne s’exécutent, donc ils ne déclenchent ni PreToolUse ni cet événement. Les refus de permission déclenchent PreToolUse mais pas cet événement ; consultez 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 au niveau supérieur :
Contrôle de décision PostToolUseFailure
Les hooksPostToolUseFailure peuvent fournir du contexte à Claude après l’échec d’un 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 une batch ait été résolu, avant que Claude Code n’envoie la demande suivante au modèle.PostToolUse se déclenche une fois par outil, ce qui signifie qu’il se déclenche simultanément lorsque Claude fait des appels d’outil parallèles. PostToolBatch se déclenche exactement une fois avec la batch complète, donc c’est le bon endroit pour injecter du contexte qui dépend de l’ensemble des outils qui ont fonctionné plutôt que sur un seul outil. 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 la batch :
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 blocs 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 volumineuses, donc analysez uniquement les champs dont vous avez besoin.
La forme
tool_response diffère de celle de PostToolUse. PostToolUse transmet l’objet Output structuré de l’outil, comme {filePath: "...", success: true} pour Write ; PostToolBatch transmet 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 agentique avant l’appel du modèle suivant.
PermissionDenied
S’exécute lorsque le classificateur du mode auto refuse un appel d’outil. Ce hook ne se déclenche que en mode auto : il ne s’exécute pas lorsque vous refusez manuellement un dialogue de permission, lorsqu’un hookPreToolUse bloque un appel ou lorsqu’une règle deny correspond. Utilisez-le pour enregistrer les refus du classificateur, ajuster la configuration ou indiquer 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.
Contrôle de décision PermissionDenied
Les hooks PermissionDenied peuvent indiquer 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 indiquant au modèle qu’il peut réessayer l’appel d’outil. Le refus lui-même n’est pas inversé. 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.
Notification
S’exécute lorsque Claude Code envoie des notifications. Correspond au type de notification. Omettez le matcher pour exécuter les hooks pour tous les types de notification.
Les types
agent_needs_input et agent_completed nécessitent Claude Code v2.1.198 ou ultérieur.
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 lorsque Claude a besoin d’approbation de permission et une notification différente lorsque 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 s’appliquent.
SubagentStart
S’exécute lorsqu’un subagent Claude Code est lancé via l’outil Agent. 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 subagents personnalisés, c’est le champ name du frontmatter de l’agent, pas le nom du fichier.
Pour les subagents fournis par un plugin, l’identifiant de type d’agent est l’identifiant limité au plugin comme my-plugin:reviewer, pas le nom brut du frontmatter. Le deux-points place un nom limité au 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 subagent et agent_type avec le nom de l’agent que le matcher filtre.
SubagentStop
S’exécute lorsqu’un subagent Claude Code a terminé sa réponse. 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 subagent stockée dans un dossier subagents/ imbriqué. Le champ last_assistant_message contient le contenu textuel de la réponse finale du subagent, donc les hooks peuvent y accéder sans analyser le fichier de transcription.
Les hooks SubagentStop reçoivent également les tableaux background_tasks et session_crons décrits sous Entrée Stop, disponibles dans Claude Code v2.1.145 ou ultérieur. Les deux tableaux sont limités à la session parent, pas au subagent.
hookSpecificOutput.additionalContext avec hookEventName défini à "SubagentStop", pour les commentaires sans erreur qui gardent le subagent en cours d’exécution. Retourner decision: "block" avec une reason garde le subagent en cours d’exécution et livre reason au subagent comme sa prochaine instruction. Pour injecter du contexte dans la session parent après qu’un subagent retourne, utilisez un hook PostToolUse sur l’outil Agent à la place.
TaskCreated
S’exécute lorsqu’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âches ou empêcher certaines tâches d’être créées.
Lorsqu’un hook TaskCreated quitte avec le code 2, la tâche n’est pas créée et le message stderr est renvoyé au modèle comme commentaire. Pour arrêter complètement le coéquipier au lieu de le relancer, retournez JSON avec {"continue": false, "stopReason": "..."}. 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
Les hooks TaskCreated supportent deux façons de contrôler la création de tâche :- Code de sortie 2 : la tâche n’est pas créée et le message stderr est renvoyé au modèle comme commentaire.
- JSON
{"continue": false, "stopReason": "..."}: arrête complètement le coéquipier, correspondant au comportement du hookStop. LestopReasonest affiché à l’utilisateur.
TaskCompleted
S’exécute lorsqu’une tâche est marquée comme complétée. Cela se déclenche dans deux situations : lorsqu’un agent marque explicitement une tâche comme complétée via l’outil TaskUpdate, ou lorsqu’un coéquipier d’une équipe d’agents termine son tour avec des tâches en cours. Utilisez ceci pour appliquer les critères d’achèvement comme passer les tests ou les vérifications de lint avant qu’une tâche ne puisse se fermer. Lorsqu’un hookTaskCompleted quitte avec le code 2, la tâche n’est pas marquée comme complétée et le message stderr est renvoyé au modèle comme commentaire. Pour arrêter complètement le coéquipier au lieu de le relancer, retournez JSON avec {"continue": false, "stopReason": "..."}. 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 l’achèvement de la 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": "..."}: arrête complètement le coéquipier, correspondant au comportement du hookStop. LestopReasonest affiché à l’utilisateur.
Stop
S’exécute lorsque l’agent Claude Code principal a terminé sa réponse. Ne s’exécute pas si l’arrêt s’est produit en raison d’une interruption utilisateur. Les erreurs API déclenchent StopFailure à la place.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 lorsque Claude Code continue déjà en raison d’un hook stop. Vérifiez cette valeur ou traitez la transcription pour empêcher de bloquer sur une condition qui ne se résoudra jamais. Claude Code remplace le hook et termine le tour après 8 blocages consécutifs.
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.
Les tableaux background_tasks et session_crons, disponibles dans Claude Code v2.1.145 ou ultérieur, permettent aux hooks de distinguer « la session est terminée » de « la session est en pause en attente du réveil du travail en arrière-plan ». Les deux tableaux sont présents lorsque le registre des tâches est accessible et sont vides lorsque 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é 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 :
additionalContext lorsque 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 affichée :
StopFailure
S’exécute à la place de Stop lorsque le tour se termine en raison d’une erreur API. La sortie et le code de sortie sont ignorés. Utilisez ceci pour enregistrer les défaillances, envoyer des alertes ou prendre des mesures de récupération lorsque Claude ne peut pas terminer une réponse en raison de limites de débit, de 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, optionnellement error_details et optionnellement last_assistant_message. Le champ error identifie le type d’erreur et est utilisé pour le filtrage du matcher.
TeammateIdle
S’exécute lorsqu’un coéquipier d’une équipe d’agents est sur le point de devenir inactif après avoir terminé son tour. Utilisez ceci pour appliquer des portes de qualité avant qu’un coéquipier ne cesse de travailler, comme exiger des vérifications de lint réussies ou vérifier que les fichiers de sortie existent. Lorsqu’un hookTeammateIdle quitte avec le code 2, le coéquipier reçoit le message stderr comme commentaire et continue de travailler au lieu de devenir inactif. Pour arrêter complètement le coéquipier au lieu de le relancer, retournez JSON avec {"continue": false, "stopReason": "..."}. 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 complètement le coéquipier, correspondant au comportement du hookStop. LestopReasonest affiché à l’utilisateur.
ConfigChange
S’exécute lorsqu’un fichier de configuration change pendant une session. Utilisez ceci pour auditer les modifications de paramètres, appliquer les politiques de sécurité ou bloquer les modifications non autorisées aux fichiers de configuration. Les hooks ConfigChange se déclenchent pour les modifications des fichiers de paramètres, les paramètres de politique gérée et les fichiers de skill. Le champsource dans l’entrée vous indique quel type de configuration a changé, et le champ optionnel file_path fournit le chemin vers le fichier modifié.
Le matcher filtre sur la source de configuration :
Cet exemple enregistre toutes les modifications 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 vers le fichier spécifique qui a été modifié.
Contrôle de décision ConfigChange
Les hooks ConfigChange peuvent bloquer les modifications de configuration de prendre effet. Utilisez le code de sortie 2 ou unedecision JSON pour empêcher la modification. Lorsqu’elle est bloquée, les nouveaux paramètres ne sont pas appliqués à la session en cours d’exécution.
policy_settings ne peuvent pas être bloquées. Les hooks se déclenchent toujours pour les sources policy_settings, vous pouvez donc les utiliser pour la journalisation d’audit, mais toute décision de blocage est ignorée. Cela garantit que les paramètres gérés par l’entreprise prennent toujours effet.
CwdChanged
S’exécute lorsque le répertoire de travail change pendant une session, par exemple lorsque 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’associe 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 pour la session, tout comme dans les hooks SessionStart.
CwdChanged ne supporte pas les matchers et se déclenche à chaque changement de répertoire.
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 fichiers FileChanged surveille :
Les hooks CwdChanged n’ont pas de contrôle de décision. Ils ne peuvent pas bloquer le changement de répertoire.
FileChanged
S’exécute lorsqu’un fichier surveillé change sur le disque. Utile pour recharger les variables d’environnement lorsque les fichiers de configuration du projet sont modifiés. Lematcher 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 : lorsqu’un fichier surveillé change, la même valeur filtre quels groupes de hook s’exécutent en utilisant les règles de matcher standard par rapport au basename du fichier modifié.
CLAUDE_ENV_FILE. Les variables écrites dans ce fichier persistent dans les commandes Bash suivantes pour la session, tout comme dans les hooks SessionStart.
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 fichiers 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.
WorktreeCreate
S’exécute lorsqu’un worktree est en cours de création, soit à partir declaude --worktree soit à partir d’un subagent utilisant isolation: "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 absolu du répertoire du worktree créé. Claude Code utilise ce chemin comme répertoire de travail pour la session isolée. Consultez Sortie WorktreeCreate pour savoir comment chaque type de hook retourne le chemin.
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 depuis 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 chemin du worktree. Redirigez toute autre sortie vers stderr afin 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 généré automatiquement, par exemple bold-oak-a3f2.
Sortie WorktreeCreate
Les hooks WorktreeCreate n’utilisent pas le modèle de décision autoriser/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 absolu du répertoire du 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.
-p il s’arrêtait pendant environ 30 secondes avant de quitter avec le code 0.
WorktreeRemove
S’exécute lorsqu’un worktree est en cours de suppression, soit lorsque vous quittez une session--worktree et choisissez de la supprimer, soit lorsqu’un subagent avec isolation: "worktree" se termine. C’est la contrepartie de nettoyage de WorktreeCreate.
Pour les worktrees basés sur git, Claude Code gère le nettoyage automatiquement avec git worktree remove. Si vous avez configuré un hook WorktreeCreate pour un système de contrôle de version non-git, associez-le à un hook WorktreeRemove pour gérer le nettoyage. Sans lui, le répertoire du worktree est laissé sur le disque.
Claude Code transmet le chemin que WorktreeCreate a retourné 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.
PreCompact
S’exécute avant que Claude Code ne 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 affiché à l’utilisateur. Vous pouvez également bloquer en retournant JSON avec "decision": "block".
Le blocage de la compaction automatique a des effets différents selon le moment où il 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 remonte et la demande actuelle échoue.
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 transmet dans /compact. Pour auto, custom_instructions est vide.
PostCompact
S’exécute après que Claude Code complète une opération de compaction. Utilisez cet événement pour réagir au nouvel état compacté, par exemple pour enregistrer le résumé généré ou mettre à jour l’état externe. Les mêmes valeurs de matcher s’appliquent que pourPreCompact :
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.
SessionEnd
S’exécute lorsqu’une session Claude Code se termine. Utile pour les tâches de nettoyage, la journalisation des statistiques de session ou l’enregistrement de l’état de 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. Consultez le tableau des raisons ci-dessus pour toutes les valeurs.
/clear et au basculement de sessions via /resume interactif. Si un hook a besoin de plus de temps, définissez un timeout par hook dans la configuration du hook. Le budget global est automatiquement augmenté au délai d’expiration par hook le plus élevé configuré dans les fichiers de paramètres, jusqu’à 60 secondes. Les délais d’expiration définis sur les hooks fournis par les plugins ne relèvent pas le budget. Pour remplacer le budget explicitement, définissez la variable d’environnement CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS en millisecondes.
Elicitation
S’exécute lorsqu’un serveur MCP demande une entrée utilisateur en milieu de tâche. Par défaut, Claude Code affiche un dialogue interactif pour que l’utilisateur réponde. Les hooks peuvent intercepter cette demande et répondre par programmation, en ignorant complètement 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 afficher le dialogue, retournez un objet JSON avechookSpecificOutput :
Le code de sortie 2 refuse l’élicitation et affiche stderr à l’utilisateur.
ElicitationResult
S’exécute après qu’un utilisateur répond à 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 en
decline.
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) :
PermissionDeniedPermissionRequestPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
command, http et mcp_tool mais pas prompt ou agent :
ConfigChangeCwdChangedElicitationElicitationResultFileChangedInstructionsLoadedNotificationPostCompactPreCompactSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart et Setup supportent les hooks command et mcp_tool. 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, Haiku par défaut
- 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. Claude Code envoie le prompt combiné et l’entrée à un modèle Claude rapide, qui retourne une décision JSON.
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 continuePreToolUse: l’appel d’outil est refusé et la raison est retournée à Claude comme l’erreur de l’outil, équivalent à un hook de commande avecpermissionDecision: "deny"PostToolUse: 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 decontinuePostToolUseFailure,TaskCreatedetTaskCompleted: la raison est retournée à Claude comme une erreur d’outil, similaire àPreToolUseTeammateIdle: 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 "ok" est false, 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.
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 traite la décision de la même manière qu’un hook de prompt
Configuration des hooks d’agent
Définisseztype à "agent" et fournissez une chaîne prompt. Les champs de configuration sont les mêmes que les hooks de prompt, avec un délai d’expiration par défaut plus long :
Le schéma de réponse est le même que les hooks de prompt :
{ "ok": true } pour autoriser ou { "ok": false, "reason": "..." } pour bloquer.
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 pendant jusqu’à 120 secondes. Lorsque le script se termine, sa sortie est livrée au tour de conversation suivant :
timeout définit le temps maximum en secondes pour le processus en arrière-plan. S’il n’est pas spécifié, les hooks asynchrones utilisent la même valeur par défaut de 10 minutes que les hooks synchrones.
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, si le hook a produit une réponse JSON avec un champadditionalContext, ce contenu est livré à Claude comme contexte au tour de conversation suivant. Un champ systemMessage vous est montré, pas à Claude.
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 plusieurs contraintes par rapport aux hooks synchrones :- Seuls les hooks
type: "command"supportentasync. Les hooks basés sur des prompts ne peuvent pas s’exécuter de manière asynchrone. - Les hooks asynchrones ne peuvent pas bloquer les appels d’outil ou retourner des décisions. Au moment où le hook se termine, l’action qui l’a déclenché a déjà procédé.
- 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
Les hooks de commande s’exécutent avec les permissions complètes de votre utilisateur système.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. Les hooks lancent PowerShell directement, donc cela fonctionne indépendamment de la définition de CLAUDE_CODE_USE_POWERSHELL_TOOL. 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, y compris les hooks qui ont correspondu, leurs codes de sortie et la sortie complète stdout et stderr, 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.
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.