Claude Code Hooks expliqués : la couche déterministe autour de votre agent
Que sont les hooks de Claude Code ? Les hooks sont des commandes shell définies par l’utilisateur (auxquelles s’ajoutent des points de terminaison HTTP, des outils MCP et des prompts de modèle) que Claude Code exécute automatiquement à des moments fixes de son cycle de vie : avant un appel d’outil, après une modification, au démarrage de la session, lorsque Claude termine sa réponse.1 Là où CLAUDE.md fournit au modèle des instructions qu’il suivra probablement, les hooks s’exécutent que le modèle coopère ou non. Tapez /hooks dans n’importe quelle session pour voir chaque événement du cycle de vie et ce qui y est rattaché.
{.answer-block}
La plupart des développeurs utilisent Claude Code avec deux couches de contrôle : les permissions, qui régissent ce que l’agent a le droit de faire, et CLAUDE.md, qui décrit ce qu’il devrait faire. Les hooks constituent la troisième couche, et la seule qui garantit quoi que ce soit. Ci-dessous : le modèle mental, chaque événement du cycle de vie présent dans la documentation actuelle, le contrat exact d’entrée/sortie, la configuration, cinq modèles fonctionnels et un cadre de décision. Chaque détail de l’API a été vérifié par rapport à la référence et au guide officiels des hooks au 1er juillet 2026 — ce système évolue vite, donc en cas de divergence entre cet article et la référence, c’est la référence qui fait foi. (Vous débutez avec Claude Code ? Commencez par la configuration en 5 minutes ou le parcours pour débuter avec Claude Code.)
En bref : les hooks reçoivent du JSON sur stdin et répondent par des codes de sortie ou du JSON sur stdout. Le code 0 autorise, le code 2 bloque (sur les événements qui peuvent bloquer) et le code 1 — le code d’échec habituel sous Unix — ne bloque rien, ce qui constitue le plus gros piège des hooks.2 Configurez-les dans settings.json sous des noms d’événements comme PreToolUse et Stop, filtrés par des matchers. Utilisez les hooks pour tout ce qui doit toujours se produire ; utilisez CLAUDE.md pour tout ce que le modèle doit simplement savoir.
Le modèle mental : des garanties autour d’un noyau non déterministe
Un agent de codage est un système probabiliste. Demandez-lui d’exécuter Prettier après chaque modification et il le fera — la plupart du temps. Il peut sauter l’étape lorsque la modification paraît anodine, lorsque le contexte s’allonge ou lorsque votre formulation est interprétée autrement. CLAUDE.md, les skills et les prompts ne sont que des suggestions : de grande qualité, généralement suivies, jamais garanties.
Les hooks constituent l’enveloppe déterministe autour de ce noyau. La définition officielle : « des commandes shell définies par l’utilisateur, des points de terminaison HTTP ou des prompts de LLM qui s’exécutent automatiquement à des moments précis du cycle de vie de Claude Code », offrant « un contrôle déterministe sur le comportement de Claude Code, garantissant que certaines actions se produisent toujours plutôt que de compter sur le LLM pour choisir de les exécuter ».3 Le formateur se déclenche à chaque modification. Le garde-fou des commandes évalue chaque appel Bash. La barrière d’achèvement contrôle chaque fin de réponse.
L’application des règles est réelle, pas cosmétique : les hooks PreToolUse se déclenchent avant toute vérification du mode de permission, si bien qu’un hook qui renvoie permissionDecision: "deny" bloque l’outil même en mode bypassPermissions ou sous --dangerously-skip-permissions. L’inverse n’est pas vrai — un hook qui renvoie "allow" ne peut pas assouplir les règles de refus définies dans les paramètres. Les hooks peuvent durcir la politique au-delà de ce que les permissions autorisent, jamais l’affaiblir.4
Le cycle de vie : chaque événement de hook
Au 1er juillet 2026, la référence documente 30 événements de hook.1 Ils se répartissent en trois cadences : une fois par session (SessionStart, SessionEnd), une fois par tour (UserPromptSubmit, Stop, StopFailure), et à chaque appel d’outil au sein de la boucle agentique (PreToolUse, PostToolUse). Les autres se déclenchent dans des conditions précises — changements de configuration, compactage, sous-agents, interactions MCP.
| Événement | Se déclenche | Un usage concret |
|---|---|---|
SessionStart |
La session démarre ou reprend | Injecter la branche git et les tickets ouverts comme contexte |
Setup |
--init-only, ou --init/--maintenance en mode -p |
Installer les dépendances en CI avant l’exécution de l’agent |
UserPromptSubmit |
Vous soumettez un prompt, avant que Claude ne le traite | Ajouter la date du jour ; rejeter les prompts contenant des secrets |
UserPromptExpansion |
Une commande saisie se déploie en prompt | Auditer ou opposer un veto aux expansions de skill/commande |
PreToolUse |
Avant l’exécution d’un appel d’outil | Bloquer les commandes shell destructrices |
PermissionRequest |
Une boîte de dialogue de permission apparaît | Approuver automatiquement les commandes de confiance pour ne pas être sollicité |
PermissionDenied |
Le classificateur du mode automatique refuse un appel d’outil | Renvoyer retry: true pour que le modèle puisse réessayer |
PostToolUse |
Après la réussite d’un appel d’outil | Formater automatiquement chaque fichier modifié |
PostToolUseFailure |
Après l’échec d’un appel d’outil | Journaliser les commandes en échec pour le triage |
PostToolBatch |
Après un lot d’appels d’outils parallèles, avant le prochain appel de modèle | Créer un point de contrôle ou stopper la boucle agentique |
Notification |
Claude Code envoie une notification | Alerte sur le bureau lorsque Claude a besoin d’une saisie |
MessageDisplay |
Pendant l’affichage du texte du message de l’assistant | Caviarder à l’écran (affichage uniquement ; transcription inchangée) |
SubagentStart |
Un sous-agent est lancé | Injecter un contexte propre au type d’agent |
SubagentStop |
Un sous-agent se termine | Valider la sortie du sous-agent avant son renvoi |
TaskCreated |
Une tâche est créée via TaskCreate |
Imposer des règles de nommage ou de portée des tâches |
TaskCompleted |
Une tâche est marquée comme terminée | Vérifier les critères d’acceptation avant que l’achèvement ne soit entériné |
Stop |
Claude termine sa réponse | Barrière d’achèvement : empêcher la fin tant que les tests ne passent pas |
StopFailure |
Le tour se termine à cause d’une erreur d’API | Alerter sur rate_limit ou billing_error (journalisation seule ; sortie ignorée) |
TeammateIdle |
Un coéquipier de l’équipe d’agents est sur le point de passer en veille | Faire avancer les coéquipiers dans une file d’attente |
InstructionsLoaded |
Un fichier CLAUDE.md ou .claude/rules/*.md se charge dans le contexte |
Journaliser quelles instructions sont entrées dans la session |
ConfigChange |
Un fichier de configuration change en cours de session | Bloquer les modifications de paramètres non autorisées |
CwdChanged |
Le répertoire de travail change | Recharger les environnements de type direnv |
FileChanged |
Un fichier surveillé change sur le disque | Rafraîchir les variables d’environnement quand .env change |
WorktreeCreate |
Un worktree est créé via --worktree ou isolation: "worktree" |
Remplacer le provisionnement git worktree par défaut |
WorktreeRemove |
Un worktree est supprimé | Nettoyage personnalisé à la sortie de session ou de sous-agent |
PreCompact |
Avant le compactage du contexte | Sauvegarder l’état que vous ne pouvez pas vous permettre de perdre |
PostCompact |
Une fois le compactage terminé | Réinjecter le contexte critique |
Elicitation |
Un serveur MCP demande une saisie de l’utilisateur | Remplir automatiquement les formulaires en exécution headless |
ElicitationResult |
Après que vous avez répondu à une élicitation MCP | Valider ou remplacer la réponse avant son renvoi |
SessionEnd |
La session se termine | Archiver les journaux, libérer les ressources |
Vous n’aurez besoin de la plupart d’entre eux que rarement. Presque toute configuration en production repose sur cinq d’entre eux : PreToolUse, PostToolUse, UserPromptSubmit, SessionStart et Stop. Les autres existent pour le jour où vous en aurez besoin.
Le contrat : du JSON en entrée, des codes de sortie ou du JSON en sortie
Les hooks de type command reçoivent du JSON sur stdin et répondent via des codes de sortie, stdout et stderr. (Les hooks HTTP reçoivent le même JSON dans un corps de requête POST et répondent via le corps de la réponse.)2
Chaque événement fournit une enveloppe commune — session_id, transcript_path, cwd et hook_event_name, avec permission_mode sur la plupart des événements — plus des champs propres à l’événement. Un hook PreToolUse pour une commande Bash reçoit :
{
"session_id": "abc123",
"transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
"cwd": "/home/user/my-project",
"permission_mode": "default",
"hook_event_name": "PreToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" }
}
Les autres événements changent la fin : UserPromptSubmit transporte prompt, SessionStart transporte source (startup/resume/clear/compact), Stop transporte stop_hook_active et last_assistant_message. Les hooks déclenchés au sein de sous-agents reçoivent en outre agent_id et agent_type.2
Codes de sortie
Trois issues possibles :2
- Code 0 — succès. Claude Code analyse stdout à la recherche de champs de sortie JSON. Pour la plupart des événements, stdout ne va que dans le journal de débogage ; pour
UserPromptSubmit,UserPromptExpansionetSessionStart, le stdout brut est ajouté comme contexte visible par Claude. - Code 2 — erreur bloquante. Stdout (y compris tout JSON) est ignoré ; stderr est renvoyé à Claude comme message d’erreur. Ce que « bloquer » signifie dépend de l’événement.
- Tout autre code de sortie — erreur non bloquante. La transcription affiche un avis
<hook name> hook error, et l’exécution se poursuit.
Cette dernière ligne mérite d’être soulignée : le code 1 ne bloque rien. La documentation le signale explicitement — Claude Code traite le code 1 comme une erreur non bloquante et poursuit, alors même que 1 est le code d’échec habituel sous Unix. Les hooks de politique doivent faire exit 2.2
Ce que fait le code 2, par événement :2
| Événement | Effet du code 2 |
|---|---|
PreToolUse |
Bloque l’appel d’outil |
PermissionRequest |
Refuse la permission |
UserPromptSubmit |
Bloque le traitement et efface le prompt |
UserPromptExpansion |
Bloque l’expansion |
Stop / SubagentStop |
Empêche l’arrêt ; la conversation continue |
TeammateIdle |
Empêche le coéquipier de passer en veille |
TaskCreated / TaskCompleted |
Annule la création / empêche l’achèvement |
ConfigChange |
Bloque le changement de configuration (sauf policy_settings) |
PreCompact |
Bloque le compactage |
PostToolBatch |
Arrête la boucle agentique avant le prochain appel de modèle |
Elicitation / ElicitationResult |
Refuse l’élicitation / transforme la réponse en refus |
WorktreeCreate |
Tout code de sortie non nul interrompt la création du worktree |
Tout le reste ne peut pas bloquer. PostToolUse et PostToolUseFailure montrent stderr à Claude (l’outil s’est déjà exécuté) ; SessionStart, Notification, SessionEnd, CwdChanged, FileChanged, PostCompact, SubagentStart et Setup ne montrent stderr qu’à l’utilisateur ; StopFailure, InstructionsLoaded, MessageDisplay et PermissionDenied ignorent le code de sortie — pour PermissionDenied, le seul levier est le JSON retry: true.2
Sortie JSON
Pour un contrôle plus fin que « bloquer ou se taire », sortez avec le code 0 et imprimez un objet JSON sur stdout. Une règle d’emblée : des codes de sortie ou du JSON, jamais les deux — le JSON n’est traité qu’avec le code 0, et le code 2 le rejette.5
Des champs universels fonctionnent sur tous les événements : continue: false arrête entièrement Claude (avec stopReason affiché à l’utilisateur), suppressOutput masque stdout de la transcription, systemMessage affiche un avertissement à l’utilisateur, et terminalSequence émet une séquence d’échappement de terminal autorisée (notification sur le bureau, titre de fenêtre, sonnerie). Les champs de décision sont propres à chaque événement :5
| Événements | Modèle de décision | Champs clés |
|---|---|---|
UserPromptSubmit, UserPromptExpansion, PostToolUse, PostToolUseFailure, PostToolBatch, Stop, SubagentStop, ConfigChange, PreCompact |
decision de premier niveau |
decision: "block" + reason (affiché à Claude). Omettez decision pour autoriser |
PreToolUse |
hookSpecificOutput |
permissionDecision : "allow" | "deny" | "ask" | "defer", plus permissionDecisionReason et updatedInput pour réécrire les arguments de l’outil avant l’exécution |
PermissionRequest |
hookSpecificOutput |
decision.behavior : "allow" | "deny", decision.updatedInput facultatif |
PermissionDenied |
hookSpecificOutput |
retry: true indique au modèle qu’il peut réessayer |
PostToolUse |
hookSpecificOutput |
updatedToolOutput remplace le résultat de l’outil |
Stop / SubagentStop |
hookSpecificOutput |
additionalContext : retour non-erreur qui poursuit la conversation sans compter comme une erreur de hook |
SessionStart, Setup, SubagentStart |
Contexte uniquement | additionalContext, plus initialUserMessage, sessionTitle, watchPaths, reloadSkills propres à SessionStart. Aucun blocage |
MessageDisplay |
hookSpecificOutput |
displayContent remplace uniquement le texte à l’écran |
Elicitation / ElicitationResult |
hookSpecificOutput |
action : "accept" | "decline" | "cancel", plus content |
WorktreeRemove, Notification, SessionEnd, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChanged |
Aucun | Effets de bord uniquement |
Deux détails qui piègent les gens. D’abord, PreToolUse fait exception au modèle du decision de premier niveau : il utilisait historiquement decision/reason au premier niveau, mais ceux-ci sont dépréciés pour cet événement ("approve"/"block" correspondent à "allow"/"deny") ; utilisez hookSpecificOutput.permissionDecision.5 Ensuite, lorsque plusieurs hooks PreToolUse sont en désaccord, l’ordre de priorité est deny > defer > ask > allow.5
Configuration : settings.json, matchers, portée
La configuration des hooks s’imbrique sur trois niveaux : choisissez un événement, ajoutez un groupe de matchers pour filtrer quand il se déclenche, et définissez un ou plusieurs gestionnaires de hooks à exécuter.6
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "/path/to/lint-check.sh" }
]
}
]
}
}
L’emplacement où vous le placez détermine la portée : ~/.claude/settings.json s’applique à tous vos projets, .claude/settings.json est limité au projet et peut être versionné, .claude/settings.local.json est limité au projet et ignoré par git, et l’ordre de priorité standard des paramètres s’applique — la politique gérée l’emporte sur le local, qui l’emporte sur le projet, qui l’emporte sur l’utilisateur.9 Les hooks peuvent aussi être livrés dans des plugins (hooks/hooks.json) et dans le frontmatter de skills ou d’agents, et les administrateurs d’entreprise peuvent imposer des hooks gérés que les utilisateurs ne peuvent pas outrepasser.6
Les matchers sont évalués selon leurs caractères : "*", "" ou un matcher omis correspond à tout ; une valeur ne contenant que des lettres, des chiffres, _, -, des espaces, des virgules et | est une chaîne exacte ou une liste (Bash, Edit|Write) ; tout le reste devient une expression régulière JavaScript non ancrée, de sorte que Edit.* correspond à la fois à Edit et à NotebookEdit — ancrez avec ^Edit$ lorsque vous visez exactement un seul outil. Les matchers sont sensibles à la casse, et chaque événement établit la correspondance sur son propre champ : le nom de l’outil pour les événements d’outil, source pour SessionStart, le type d’agent pour SubagentStart, le type de notification pour Notification.6 Pour un filtrage plus précis sur les événements d’outil, le champ if propre à chaque gestionnaire accepte une règle de permission comme "Bash(git *)" — mais il fonctionne au mieux (il échoue en mode ouvert sur des commandes non analysables), donc utilisez des règles de permission, pas if, pour des garanties strictes.6
Les gestionnaires se déclinent en cinq types : command (shell), http (point de terminaison POST), mcp_tool, prompt (évaluation de modèle en un seul tour) et agent (un sous-agent avec accès Read/Grep/Glob ; expérimental). Délais d’expiration par défaut : 600 secondes pour command/http/mcp_tool (abaissés à 30 pour UserPromptSubmit et à 10 pour MessageDisplay), 30 pour prompt, 60 pour agent — remplacez ces valeurs par hook avec timeout.6 Tous les hooks correspondants s’exécutent en parallèle, les gestionnaires identiques étant dédoublonnés, et $CLAUDE_PROJECT_DIR oriente les scripts vers la racine de votre projet.
Vérifiez avec /hooks : un navigateur en lecture seule qui affiche chaque événement, ses hooks configurés et le fichier de paramètres dont chacun provient. Pour changer quoi que ce soit, modifiez le JSON (ou demandez à Claude de le faire). Pour tout désactiver temporairement, réglez "disableAllHooks": true.6
Cinq modèles
Génériques et minimaux. Le tutoriel sur les hooks construit des versions de production plus complètes de plusieurs d’entre eux, et Les hooks pour le développement Apple les applique à la chaîne d’outils iOS.
1. Formatage automatique après les modifications (PostToolUse)
Directement tiré du guide officiel — chaque fichier que Claude touche est formaté, sans exception :3
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
]
}
]
}
}
Remplacez la commande par ruff format, gofmt ou swiftformat selon les exigences de votre stack.
2. Bloquer les commandes dangereuses (PreToolUse, code 2)
#!/bin/bash
# .claude/hooks/guard-bash.sh — register on PreToolUse, matcher "Bash"
command=$(jq -r '.tool_input.command // empty')
case "$command" in
*"rm -rf"* | *"git push --force"* | *"DROP TABLE"*)
echo "Blocked: matches a destructive pattern. Propose a safer alternative." >&2
exit 2 ;;
esac
exit 0
Le code 2 bloque l’appel et renvoie stderr à Claude, qui rectifie sa trajectoire au lieu de réessayer aveuglément. L’équivalent en JSON — permissionDecision: "deny" accompagné d’une raison — fait de même, avec la possibilité d’évoluer vers "ask" (escalade vers l’humain) ou updatedInput (réécriture de la commande).5
3. Injecter du contexte au démarrage de la session (SessionStart)
Le stdout brut d’un hook SessionStart devient un contexte visible par Claude — aucun JSON requis :1
#!/bin/bash
# .claude/hooks/session-context.sh — register on SessionStart
echo "Current branch: $(git branch --show-current)"
echo "Recent commits:"
git log --oneline -5
echo "Uncommitted files: $(git status --porcelain | wc -l | tr -d ' ')"
exit 0
Utilisez ceci pour l’état dynamique. Les conventions statiques ont leur place dans CLAUDE.md, que la documentation elle-même recommande pour le contexte ne nécessitant pas de script.1
4. Une barrière d’achèvement sur Stop
Stop se déclenche lorsque Claude termine sa réponse. Le bloquer force l’agent à continuer de travailler jusqu’à ce qu’une condition soit remplie :
#!/bin/bash
# .claude/hooks/stop-gate.sh — register on Stop
input=$(cat)
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
exit 0 # already continuing because of this hook; don't loop forever
fi
if ! npm test --silent > /tmp/stop-gate.log 2>&1; then
jq -n '{decision: "block", reason: "Tests are failing. Fix them before finishing. Log: /tmp/stop-gate.log"}'
fi
exit 0
La vérification de stop_hook_active est importante : Claude Code plafonne strictement un hook Stop à 8 blocages consécutifs, et une barrière qui ne vérifie jamais si elle a déjà déclenché une continuation les épuisera d’un seul coup.7 Pour un pilotage plus doux, renvoyez hookSpecificOutput.additionalContext au lieu de decision: "block" — même continuation, mais un retour identifié plutôt qu’une erreur de hook. Et pour des conditions ponctuelles, la commande intégrée /goal est un hook Stop fondé sur un prompt, limité à la session et sans aucune configuration.1
5. Le répartiteur : un point d’entrée, de nombreux petits hooks
Enregistrer dix hooks, c’est dix entrées settings.json qui divergent d’une machine et d’un projet à l’autre. L’alternative : enregistrer un répartiteur par événement et router par convention.
#!/bin/bash
# .claude/hooks/dispatch.sh — register once per event you care about
input=$(cat)
event=$(echo "$input" | jq -r '.hook_event_name')
dir="$CLAUDE_PROJECT_DIR/.claude/hooks/$event"
[ -d "$dir" ] || exit 0
for hook in "$dir"/*.sh; do
[ -x "$hook" ] || continue
echo "$input" | "$hook" || exit $?
done
exit 0
Ajouter un garde-fou revient désormais à un chmod +x sur un nouveau fichier dans .claude/hooks/PreToolUse/ — settings.json ne change jamais, chaque script reste assez petit pour être testé isolément, et le premier code 2 se propage. Une réserve : le répartiteur sérialise ce que Claude Code exécuterait en parallèle, et il convient surtout aux hooks à code de sortie — un hook qui émet du JSON devrait rester autonome, puisque stdout doit contenir exactement un seul objet JSON.5
Hook, CLAUDE.md, skill ou mémoire
Quatre mécanismes, quatre rôles :
| Mécanisme | Rôle | Règle de choix |
|---|---|---|
| Hook | Application | Si le sauter doit être impossible — formatage, sécurité, barrières — c’est un hook |
| CLAUDE.md | Orientation | Si c’est une convention que le modèle doit connaître à chaque session — stack, style, commandes — c’est CLAUDE.md |
| Skill | Capacité | Si c’est une procédure avec ses propres instructions et scripts, invoquée au moment pertinent, c’est un skill |
| Mémoire | Rappel | Si c’est un fait appris au cours d’une session dont les sessions futures ont besoin, c’est de la mémoire |
Le mode d’échec fonctionne dans les deux sens. Coder des conventions sous forme de hooks vous vaut des scripts fragiles qui imposent des choses qu’une simple phrase d’orientation gère très bien. Coder une politique sous forme de prose dans CLAUDE.md vous vaut un agent qui force-push sur main le jour précis où cela compte. Le test : quel est le coût si le modèle l’ignore une seule fois ? Désagrément → CLAUDE.md. Incident → hook.
Ce que les hooks ne peuvent pas faire
Des limites honnêtes, toutes tirées de la documentation officielle :7
- Les hooks ne peuvent pas appeler d’outils ni de commandes slash. Les hooks de type command parlent stdout, stderr et codes de sortie — rien d’autre. Le contexte renvoyé via
additionalContextest injecté sous forme de texte brut. PostToolUsene peut rien annuler. L’outil s’est déjà exécuté. La prévention réside dansPreToolUse.Stopse déclenche à chaque fin de réponse, pas seulement à « tâche terminée », et jamais lors des interruptions de l’utilisateur (les erreurs d’API déclenchentStopFailureà la place). La logique de la barrière doit tolérer les arrêts en cours de tâche.PermissionRequestne se déclenche pas en mode headless (-p). UtilisezPreToolUsepour les décisions de permission automatisées.PreToolUsene voit pas les fichiers référencés par@. Les fichiers intégrés via@dans votre prompt n’impliquent aucun appel d’outil ; utilisez des règles de refusReadpour protéger des chemins par cette voie.1- Le
updatedInputen parallèle n’est pas déterministe. Lorsque plusieurs hooks PreToolUse réécrivent les arguments du même outil, le dernier à terminer l’emporte. Laissez un seul hook responsable de chaque réécriture. - Les délais d’expiration annulent le hook. 600 secondes par défaut pour les hooks de type command (30 pour
UserPromptSubmit, 10 pourMessageDisplay) ; une barrière lente qui expire est une barrière qui ne s’est pas exécutée. - La sortie est plafonnée à 10 000 caractères — le surplus est écrit dans un fichier et remplacé par un aperçu.
- Les hooks s’exécutent avec l’ensemble de vos permissions utilisateur. L’avertissement même de la référence : ils « peuvent modifier, supprimer ou accéder à tous les fichiers auxquels votre compte utilisateur a accès. Examinez et testez toutes les commandes de hook avant de les ajouter à votre configuration ».8 Mettez vos variables entre guillemets, utilisez des chemins absolus, évitez les fichiers sensibles.
- Un hook défectueux dégrade chaque session jusqu’à ce qu’il soit corrigé. Déboguez avec la vue de transcription (
Ctrl+O),claude --debug-file /tmp/claude.logou/debugen cours de session ; un piège classique est un profil shell qui affiche du texte au démarrage et corrompt la sortie JSON de votre hook.7
FAQ
Que sont les hooks de Claude Code ?
Les hooks sont des commandes définies par l’utilisateur — scripts shell, points de terminaison HTTP, outils MCP ou prompts de modèle — que Claude Code exécute automatiquement à des moments précis du cycle de vie.3 Ils reçoivent le JSON de l’événement sur stdin et répondent par des codes de sortie ou du JSON : bloquer un appel d’outil, injecter du contexte, réécrire des arguments, maintenir l’agent au travail. Contrairement aux instructions de CLAUDE.md, ils s’exécutent à chaque fois, quel que soit le comportement du modèle.
Quelle est la différence entre les hooks PreToolUse et les permissions ?
Les règles de permission sont déclaratives : des modèles statiques allow/deny/ask que Claude Code évalue lui-même. Les hooks PreToolUse sont programmables : votre code inspecte l’intégralité de l’entrée de l’outil et décide. Les hooks se déclenchent avant les vérifications du mode de permission, si bien que le "deny" d’un hook tient même en mode bypassPermissions — mais le "allow" d’un hook ne peut pas outrepasser une règle de refus des paramètres.4 Utilisez des règles de permission pour tout ce qu’un modèle peut exprimer ; recourez à un hook lorsque la décision nécessite de la logique, un état externe ou une réécriture de l’entrée.
Les hooks fonctionnent-ils en mode headless (-p) ?
Oui — avec une exception documentée : les hooks PermissionRequest ne se déclenchent pas en mode non interactif, donc les décisions de permission automatisées relèvent de PreToolUse.7 Le mode headless débloque aussi une option que les sessions interactives ignorent : permissionDecision: "defer", qui met en pause un appel d’outil pour qu’un processus englobant (une application Agent SDK, une UI personnalisée) puisse recueillir une saisie et reprendre la session plus tard.5
Pourquoi mon hook s’exécute-t-il sans rien bloquer ?
Presque toujours une violation du contrat. Le code 1 ne bloque pas — seul le code 2 le fait, et uniquement sur les événements qui prennent en charge le blocage.2 Les décisions JSON ne sont analysées qu’avec le code 0 — un script qui imprime {"decision": "block"} puis sort avec le code 2 voit son JSON rejeté. Et les matchers sont sensibles à la casse — bash ne correspond jamais à Bash. Confirmez l’enregistrement avec /hooks, puis testez en injectant un JSON d’exemple dans le script via un pipe et en vérifiant echo $?.7
Sources
Vérifié par rapport à la documentation officielle le 1er juillet 2026. L’API des hooks a changé de façon notable au fil des versions Claude Code v2.1.x (nouveaux événements, nouveaux champs, sémantique des matchers), traitez donc les détails sensibles à la version comme valables « à cette date ».
À lire aussi sur ce site : la section sur les hooks du guide Claude Code pour la vue d’ensemble du système, y compris les hooks de type prompt et agent, le tutoriel sur les hooks pour cinq réalisations de production avec des configurations complètes, Les hooks pour le développement Apple pour les modèles iOS appliqués, et le guide de démarrage rapide si vous n’avez pas encore installé Claude Code.
-
Anthropic, “Hooks reference — Hook lifecycle and hook events.” code.claude.com/docs/en/hooks#hook-events ↩↩↩↩↩↩
-
Anthropic, “Hooks reference — Hook input and output; exit code output; exit code 2 behavior per event.” code.claude.com/docs/en/hooks#exit-code-output ↩↩↩↩↩↩↩↩
-
Anthropic, “Automate actions with hooks.” code.claude.com/docs/en/hooks-guide ↩↩↩
-
Anthropic, “Hooks guide — Hooks and permission modes.” code.claude.com/docs/en/hooks-guide#hooks-and-permission-modes ↩↩
-
Anthropic, “Hooks reference — JSON output and decision control.” code.claude.com/docs/en/hooks#json-output ↩↩↩↩↩↩↩
-
Anthropic, “Hooks reference — Configuration: hook locations, matcher patterns, hook handler fields, the /hooks menu.” code.claude.com/docs/en/hooks#configuration ↩↩↩↩↩↩
-
Anthropic, “Hooks guide — Limitations and troubleshooting.” code.claude.com/docs/en/hooks-guide#limitations-and-troubleshooting ↩↩↩↩↩
-
Anthropic, “Hooks reference — Security considerations.” code.claude.com/docs/en/hooks#security-considerations ↩
-
Anthropic, “Claude Code settings.” code.claude.com/docs/en/settings ↩