Tutoriel Hooks Claude Code : 5 hooks de production créés de zéro
Claude Code exécute la bonne action environ 95 % du temps. Les 5 % restants incluent le force-push sur main, l’omission de votre formateur de code et le commit de code qui échoue au lint. Les hooks éliminent ces 5 % en ajoutant des portes déterministes à 17 points du cycle de vie dans le workflow de Claude. Ils se déclenchent à chaque fois, sans exception, quelle que soit la formulation du prompt ou le comportement du modèle.
En résumé : Les hooks sont des commandes shell déclenchées par les événements du cycle de vie de Claude Code. Les hooks PreToolUse inspectent et bloquent les actions (code de sortie 2 = bloquer, sortie 0 = autoriser). Les hooks PostToolUse valident et formatent après coup. Configurez-les dans .claude/settings.json avec un matcher regex et un tableau hooks imbriqué. Ce tutoriel construit cinq hooks de production : auto-formateur, porte de sécurité, exécuteur de tests, alerte de notification et vérification de qualité pré-commit.
Points clés
- Développeurs solo : Commencez par l’auto-formateur (Hook 1) et la porte de sécurité (Hook 2). Ces deux hooks préviennent les erreurs les plus courantes de Claude Code sans aucune maintenance continue.
- Responsables d’équipe : Committez les hooks dans
.claude/settings.jsonde votre dépôt. Chaque membre de l’équipe bénéficie automatiquement des mêmes portes de sécurité et vérifications de qualité. - Ingénieurs sécurité : Le code de sortie 2 bloque l’action. Le code de sortie 1 ne fait qu’enregistrer un avertissement. Chaque hook de sécurité PreToolUse doit utiliser
exit 2, sinon il n’offre aucune application effective.
Que sont les hooks ?
Les hooks sont des commandes shell qui s’exécutent à des événements spécifiques du cycle de vie durant une session Claude Code. Ils s’exécutent en dehors du LLM en tant que scripts ordinaires déclenchés par les actions de Claude, et non comme des prompts interprétés par le modèle.
Quatre catégories principales couvrent les cas d’utilisation les plus courants (Claude Code prend en charge 17 types d’événements au total) :
- Événements de session :
SessionStartetStopse déclenchent au début ou à la fin d’une session. Utilisez-les pour l’initialisation, le nettoyage et les notifications. - Événements d’outils :
PreToolUseetPostToolUsese déclenchent avant et après que Claude utilise un outil (écriture d’un fichier, exécution d’une commande bash ou recherche de code). Ce sont les hooks les plus puissants car ils peuvent inspecter et bloquer des actions spécifiques. - Événements de notification :
Notificationse déclenche quand Claude génère une notification. Utile pour router les alertes vers Slack, les notifications de bureau ou les systèmes de journalisation. - Événements de sous-agents :
SubagentStopse déclenche quand un sous-agent (créé via l’outil Agent) termine son exécution. Les hooks se déclenchent aussi pour les actions des sous-agents, donc vos portes de sécurité s’appliquent de manière récursive.
La sémantique des codes de sortie est importante. Sortie 0 signifie succès (continuer). Sortie 2 signifie bloquer l’action. Sortie 1 signifie une erreur non bloquante du hook où l’action continue quand même. Chaque hook critique pour la sécurité doit utiliser exit 2 pour appliquer effectivement sa porte.
Bases de la configuration des hooks
Les hooks se trouvent dans vos fichiers de paramètres :
- Au niveau du projet :
.claude/settings.jsonà la racine de votre dépôt (partagé avec votre équipe) - Au niveau utilisateur :
~/.claude/settings.json(vos hooks personnels, appliqués globalement)
La structure JSON :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/your/script.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/path/to/another-script.sh"
}
]
}
]
}
}
Chaque entrée possède un matcher (regex correspondant aux noms d’outils comme Bash, Write, Edit, Read, Glob, Grep ou Agent) et un tableau hooks de définitions de hooks. Chaque hook spécifie un type ("command" pour les commandes shell) et la command à exécuter. Le matcher Write|Edit correspond aux deux types d’outils.
Vous pouvez également gérer les hooks de manière interactive avec la commande /hooks au sein d’une session Claude Code.
Quand un hook se déclenche, Claude Code transmet le contexte via des variables d’environnement ($FILE_PATH pour les opérations sur les fichiers) et stdin (un objet JSON contenant le nom de l’outil, les paramètres et les métadonnées de session). Votre script lit ces informations pour prendre des décisions.
5 hooks pratiques
Chaque hook ci-dessous résout un vrai problème que j’ai rencontré en utilisant Claude Code comme outil de développement principal. Tous les exemples utilisent le schéma de hooks imbriqué correct.
1. Auto-formatage à l’édition de fichier
Claude écrit du code fonctionnellement correct qui enfreint occasionnellement les règles de formatage de votre projet. Plutôt que de demander à Claude de reformater, exécutez votre formateur automatiquement après chaque écriture de fichier.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]]; then black --quiet \"$FILE_PATH\" 2>/dev/null; elif [[ \"$FILE_PATH\" == *.js ]] || [[ \"$FILE_PATH\" == *.ts ]]; then npx prettier --write \"$FILE_PATH\" 2>/dev/null; fi'"
}
]
}
]
}
}
Claude Code définit $FILE_PATH avec le chemin du fichier modifié. Le hook vérifie l’extension et exécute le formateur approprié. Les fichiers Python passent par black, les fichiers JavaScript et TypeScript par prettier. Le 2>/dev/null supprime les sorties bruyantes pour ne voir que les vraies erreurs.
Pour les projets plus importants, déplacez la commande en ligne vers un script autonome pour plus de lisibilité.
2. Porte de sécurité pour les commandes dangereuses
Les hooks PreToolUse sur l’outil Bash inspectent la commande que Claude est sur le point d’exécuter et la bloquent si elle correspond à un motif dangereux. J’ai écrit ce hook après que Claude a fait un force-push sur main durant une session de refactoring.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"rm\\s+-rf\\s+/|git\\s+push\\s+(-f|--force)\\s+(origin\\s+)?main|git\\s+reset\\s+--hard|DROP\\s+TABLE|:(){ :|:& };:\"; then echo \"BLOCKED: Dangerous command detected: $CMD\" >&2; exit 2; fi'"
}
]
}
]
}
}
Quand ce hook se termine avec le code 2, Claude Code annule la commande en attente. Le message d’erreur s’affiche à la fois dans votre terminal et dans le contexte de Claude, afin que le modèle comprenne pourquoi l’action a échoué et suggère une alternative plus sûre.
Motifs bloqués :
- rm -rf / (suppression récursive depuis la racine)
- git push --force main et git push -f main (force-push sur la branche main)
- git reset --hard (destruction du travail non commité)
- DROP TABLE (destruction accidentelle de base de données)
- Fork bombs
Personnalisez cette liste pour votre environnement. Les bases de données de production nécessitent des motifs SQL destructifs. Les déploiements basés sur CLI nécessitent des gardes sur les commandes de déploiement.
3. Exécution des tests après modification
Quand Claude modifie un fichier Python, exécutez automatiquement les tests correspondants. Cela détecte les régressions immédiatement plutôt que de les découvrir trois modifications de fichiers plus tard.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]] && [[ \"$FILE_PATH\" != *test_* ]]; then TEST_FILE=\"tests/test_$(basename \"$FILE_PATH\")\"; if [[ -f \"$TEST_FILE\" ]]; then python -m pytest \"$TEST_FILE\" -x --tb=short 2>&1 | tail -20; fi; fi'"
}
]
}
]
}
}
Le hook vérifie si le fichier modifié est un fichier source Python (et non un fichier de test lui-même), recherche un fichier de test correspondant en utilisant la convention de nommage avec le préfixe test_, et l’exécute s’il existe. Le drapeau -x s’arrête au premier échec, et tail -20 garde la sortie concise.
Remarque : Ce hook suppose un répertoire tests/ plat avec la convention de nommage en préfixe test_. Adaptez la construction du chemin pour les répertoires de tests imbriqués ou les conventions de nommage différentes.
Ce hook est particulièrement utile lors des sessions de refactoring où Claude touche plusieurs fichiers. La boucle de retour immédiate empêche l’enchaînement « corriger une chose, en casser trois autres » qui se produit quand les tests ne sont exécutés qu’à la fin.
4. Notification en fin de session
Les sessions Claude Code longues peuvent durer plusieurs minutes. Plutôt que de surveiller le terminal, recevez une notification quand la session se termine.
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code session ended\" with title \"Claude Code\"'"
}
]
}
]
}
}
Cet exemple macOS utilise osascript pour déclencher une notification native. Pour Linux, remplacez la ligne osascript par notify-send "Claude Code" "Session ended". Pour les notifications Slack, utilisez un webhook :
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
-d '{"text": "Claude Code session ended"}'
J’utilise la variante Slack pour les tâches en arrière-plan lancées avec & <task> (le mode arrière-plan de Claude Code). La notification de bureau gère les sessions interactives.
5. Vérification de qualité avant le commit
Avant que Claude n’exécute git commit, validez que le code passe le linting. Cela détecte les problèmes que le formatage seul ne repère pas : imports inutilisés, variables non définies, erreurs de types.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"^git\\s+commit\"; then if ! LINT_OUTPUT=$(ruff check . --select E,F,W 2>&1); then echo \"LINT FAILED -- fix before committing:\" >&2; echo \"$LINT_OUTPUT\" >&2; exit 2; fi; fi'"
}
]
}
]
}
}
Ce hook ne s’active que lorsque la commande Bash commence par git commit. Il exécute ruff (un linter Python rapide) avec les règles d’erreurs, pyflakes et avertissements. Si des problèmes existent, le commit est bloqué (exit 2) et Claude voit la sortie du lint, ce qui l’amène généralement à corriger les problèmes et réessayer.
Vous pouvez superposer plusieurs vérifications de qualité : mypy pour la vérification de types, bandit pour l’analyse de sécurité, ou les scripts de validation personnalisés de votre projet. Les hooks PreToolUse sur les commandes Bash vous offrent une porte programmable avant toute action shell.
Conseils de débogage des hooks
Les hooks échouent silencieusement plus souvent qu’on ne le pense. Cinq techniques que j’utilise pour les déboguer :
- Testez les scripts indépendamment d’abord. Envoyez un exemple JSON dans votre script manuellement :
echo '{"tool_input":{"command":"git commit -m test"}}' | bash your-hook.sh. Si ça échoue en dehors de Claude Code, ça échouera aussi dedans. - Utilisez stderr pour la sortie de débogage. Tout ce que votre hook écrit sur stderr apparaît dans le contexte de Claude. Écrivez
echo "DEBUG: matched $CMD" >&2pendant le développement, puis supprimez-le une fois le hook stabilisé. - Surveillez les échecs de jq. Si votre chemin JSON est incorrect,
jqretournenullsilencieusement et vos conditions ne correspondront pas. Testez vos expressionsjqavec de vraies entrées d’outils. - Vérifiez les codes de sortie. Exit 2 bloque les actions. Exit 1 ne fait qu’avertir. Un hook PreToolUse qui utilise accidentellement
exit 1n’offre aucune application effective tout en donnant l’impression de fonctionner. Commencez de manière permissive (exit 0 par défaut) et utilisezexit 2uniquement pour les motifs spécifiquement bloqués. - Gardez les hooks rapides. Les hooks s’exécutent de manière synchrone. Un hook qui prend 5 secondes ajoute 5 secondes à chaque utilisation d’outil correspondante. Je maintiens tous mes hooks sous les 2 secondes, idéalement sous les 500 millisecondes.
Prochaines étapes
Ces cinq hooks couvrent les fondamentaux : formatage, sécurité, tests, notifications et portes de qualité. Une fois à l’aise avec ces patterns, vous pouvez créer des hooks pour l’injection de contexte (ajout d’instructions spécifiques au projet au démarrage de la session), les gardes de récursion (prévention des boucles infinies de sous-agents) et l’orchestration de workflows (enchaînement de processus multi-étapes).
Pour l’architecture des hooks, les 17 événements du cycle de vie et les patterns avancés, consultez la section hooks de mon guide complet : Guide Claude Code : Comment fonctionnent les hooks ?
J’ai également écrit sur les origines de mes 95 hooks de production dans Hooks Claude Code : Pourquoi chacun de mes 95 hooks existe, qui couvre les incidents ayant motivé chacun d’entre eux.
FAQ
Les hooks peuvent-ils empêcher Claude Code d’exécuter une commande ?
Oui. Les hooks PreToolUse bloquent toute action d’outil en se terminant avec le code 2. Claude Code annule l’action en attente et affiche la sortie stderr du hook au modèle. Le code de sortie 1 est une erreur non bloquante du hook où l’action continue quand même. Cette distinction est essentielle : chaque hook de sécurité doit utiliser exit 2, pas exit 1. Claude voit la raison du rejet et suggère une alternative plus sûre.
Où placer les fichiers de configuration des hooks ?
Les configurations de hooks vont dans .claude/settings.json pour les hooks au niveau du projet (commités dans votre dépôt, partagés avec votre équipe) ou ~/.claude/settings.json pour les hooks au niveau utilisateur (personnels, appliqués à chaque projet). Les hooks au niveau du projet ont priorité quand les deux existent. Je recommande d’utiliser des chemins absolus pour les fichiers de scripts afin d’éviter les problèmes de répertoire de travail.
Les hooks fonctionnent-ils avec les sous-agents ?
Oui. Les hooks se déclenchent aussi pour les actions des sous-agents. Si Claude crée un sous-agent via l’outil Agent, vos hooks PreToolUse et PostToolUse s’exécutent pour chaque outil utilisé par le sous-agent. Sans ce comportement, un sous-agent pourrait contourner vos portes de sécurité. L’événement SubagentStop vous permet d’exécuter un nettoyage ou une validation quand un sous-agent termine sa tâche.