← Tous les articles

Claude Code Hooks expliqués : la couche déterministe autour de votre agent

Tiré du guide: Claude Code Comprehensive Guide

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, UserPromptExpansion et SessionStart, 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 additionalContext est injecté sous forme de texte brut.
  • PostToolUse ne peut rien annuler. L’outil s’est déjà exécuté. La prévention réside dans PreToolUse.
  • Stop se 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éclenchent StopFailure à la place). La logique de la barrière doit tolérer les arrêts en cours de tâche.
  • PermissionRequest ne se déclenche pas en mode headless (-p). Utilisez PreToolUse pour les décisions de permission automatisées.
  • PreToolUse ne 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 refus Read pour protéger des chemins par cette voie.1
  • Le updatedInput en 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 pour MessageDisplay) ; 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.log ou /debug en 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.


  1. Anthropic, “Hooks reference — Hook lifecycle and hook events.” code.claude.com/docs/en/hooks#hook-events 

  2. 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 

  3. Anthropic, “Automate actions with hooks.” code.claude.com/docs/en/hooks-guide 

  4. Anthropic, “Hooks guide — Hooks and permission modes.” code.claude.com/docs/en/hooks-guide#hooks-and-permission-modes 

  5. Anthropic, “Hooks reference — JSON output and decision control.” code.claude.com/docs/en/hooks#json-output 

  6. Anthropic, “Hooks reference — Configuration: hook locations, matcher patterns, hook handler fields, the /hooks menu.” code.claude.com/docs/en/hooks#configuration 

  7. Anthropic, “Hooks guide — Limitations and troubleshooting.” code.claude.com/docs/en/hooks-guide#limitations-and-troubleshooting 

  8. Anthropic, “Hooks reference — Security considerations.” code.claude.com/docs/en/hooks#security-considerations 

  9. Anthropic, “Claude Code settings.” code.claude.com/docs/en/settings 

Articles connexes

Codex CLI vs Claude Code 2026 : architecture, tarification et accès en Chine

Codex CLI vs Claude Code : sandboxing noyau contre 26 hooks de gouvernance, benchmarks Opus 4.7 vs GPT-5.4, tarification…

30 min de lecture

Hooks Claude Code : pourquoi chacun de mes 95 hooks existe

J'ai construit 95 hooks pour Claude Code. Chacun existe parce que quelque chose a mal tourné. Voici leurs origines et l'…

9 min de lecture