Les hooks Claude Code : pourquoi chacun de mes 95 hooks existe
J’ai créé 95 hooks pour Claude Code. Chacun existe parce que quelque chose s’est mal passé d’abord. Le git-safety-guardian existe parce que Claude a fait un force-push sur main. Le recursion-guard existe parce qu’un sous-agent a engendré des enfants à l’infini. Le blog-quality-gate existe parce que j’ai publié un article avec 7 phrases à la voix passive et une note de bas de page orpheline.1
TL;DR
Les hooks Claude Code exécutent des commandes shell à des points spécifiques du cycle de vie pendant le développement assisté par IA. Les hooks fournissent des garanties déterministes (bloquer les commandes git dangereuses, injecter du contexte, imposer la qualité) par-dessus un système probabiliste (le LLM). Après avoir créé 95 hooks à travers mon infrastructure, j’ai constaté que les meilleurs hooks naissent d’incidents, pas de la planification. Cet article couvre l’architecture, les histoires d’origine de mes hooks les plus critiques, et les patterns que j’ai appris en 9 mois de développement de hooks.
L’architecture
Claude Code expose des événements de cycle de vie où les hooks peuvent intercepter, modifier ou bloquer le comportement :2
Événements de session
| Événement | Quand il se déclenche | Mes hooks |
|---|---|---|
| SessionStart | Une nouvelle session commence | session-start.sh — injecte la date, valide le venv, initialise l’état de récursion |
| SessionEnd | La session se termine | Nettoyage des fichiers temporaires |
Événements d’exécution d’outils
| Événement | Quand il se déclenche | Mes hooks |
|---|---|---|
| PreToolUse | Avant l’exécution de tout outil | git-safety-guardian.sh, recursion-guard.sh, credentials-check.sh |
| PostToolUse | Après l’exécution d’un outil | post-deliberation.sh, log-bash.sh |
Événements de réponse
| Événement | Quand il se déclenche | Mes hooks |
|---|---|---|
| UserPromptSubmit | L’utilisateur envoie un prompt | Injecteurs de contexte (date, branche, informations du modèle) |
| Stop | Claude termine sa réponse | deliberation-pride-check.sh, reviewer-stop-gate.sh |
Chaque hook reçoit du JSON sur stdin et communique via stdout :
{"decision": "block", "reason": "Force push to main is prohibited"}
Ou autorise silencieusement en sortant avec le code 0.3
Histoires d’origine : les hooks les plus importants
Hook 1 : git-safety-guardian.sh (PreToolUse:Bash)
L’incident : Lors d’une session Claude Code précoce, j’ai demandé à l’agent de « nettoyer l’historique git ». L’agent a exécuté git push --force origin main. Le force-push a écrasé trois jours de commits sur une branche partagée. J’ai récupéré depuis une sauvegarde locale, mais le processus de récupération de 4 heures m’a convaincu que le jugement probabiliste ne devrait jamais contrôler les opérations git destructives.
Le hook :
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Only check git commands
echo "$COMMAND" | grep -qE '\bgit\b' || exit 0
# Block force push to main/master
if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\b(main|master)\b'; then
cat << EOF
{"decision": "block", "reason": "Force push to main/master blocked by safety hook"}
EOF
fi
La leçon : Le hook n’essaie pas de comprendre l’intention. Il fait du pattern-matching sur la chaîne de commande. Simple, déterministe, impossible à contourner par un prompting astucieux. L’agent peut toujours faire un force-push sur les branches de fonctionnalité (parfois légitime), mais main/master sont protégés en permanence.4
Interceptions à vie : 8 tentatives de force-push interceptées en 9 mois.
Hook 2 : recursion-guard.sh (PreToolUse:Task)
L’incident : En construisant le système de délibération, j’ai lancé une session qui a engendré 3 sous-agents d’exploration. Chaque sous-agent, sans limites de création, a engendré ses propres sous-agents. La récursion a consommé des tokens API à 10 fois le rythme normal. J’ai tué la session manuellement après avoir remarqué l’accélération de la consommation de tokens.
Le hook :
#!/bin/bash
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"
MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12
# Validate integers safely (((VAR++)) crashes with set -e when VAR=0)
is_positive_int() {
[[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]]
}
La décision de conception clé : Les agents héritent d’un budget de création de leur parent plutôt que d’incrémenter la profondeur. Un agent racine avec un budget de 12 peut distribuer ce budget dans n’importe quelle forme d’arbre. Les limites basées sur la profondeur sont trop rigides (elles empêchent les chaînes profondes mais étroites qui sont parfaitement sûres).5
Blocages à vie : 23 tentatives de création incontrôlée.
Hook 3 : blog-quality-gate.sh (Stop)
L’incident : J’ai publié un article de blog avec 7 phrases à la voix passive, une note de bas de page référencée dans le texte mais absente de la section des références, et « was implemented by the team » comme phrase d’ouverture. L’article avait l’air soigné dans mon éditeur mais échouait aux contrôles de qualité basiques que n’importe quel relecteur humain aurait détectés.
Le hook : Exécute mon linter de blog à 12 modules sur tout fichier de contenu de blog modifié. Vérifie la voix passive, les notes de bas de page orphelines, les méta-descriptions manquantes, les blocs de code non étiquetés et l’intégrité des citations. Chaque constat est spécifique : « Ligne 47 : voix passive détectée dans “was implemented by the team.” Suggestion : “the team implemented.” »
Le parallèle avec le feedback humain : Le hook critique le travail, pas l’opérateur. Il dit « la ligne 47 contient de la voix passive », pas « vous écrivez mal ». Le même principe qui rend le feedback humain constructif rend le feedback automatisé utile.
Le pattern derrière 95 hooks
L’architecture pilotée par la configuration
Mes hooks ont évolué de valeurs codées en dur vers un comportement piloté par la configuration :
~/.claude/configs/
├── recursion-limits.json # Depth, spawn budgets, timeouts
├── deliberation-config.json # Consensus thresholds per task type
├── consensus-profiles.json # security=85%, docs=50%
├── circuit-breaker.json # Failure mode configurations
└── file-scope-rules.json # Path-scoped hook application
Déplacer les seuils dans des fichiers JSON de configuration a permis d’ajuster le comportement sans modifier les scripts bash. Quand j’avais besoin d’un consensus de sécurité à 85 % mais de seulement 50 % pour la documentation, le changement de configuration prenait 30 secondes. Un changement dans le code aurait nécessité édition, tests et redéploiement.6
Le pattern de superposition du cycle de vie
Mes 95 hooks forment un filet de sécurité à quatre couches :
Couche 1 : Prévention (PreToolUse) — Empêcher les problèmes avant qu’ils ne surviennent. git-safety-guardian, credentials-check, recursion-guard.
Couche 2 : Contexte (UserPromptSubmit, SessionStart) — Injecter les informations dont l’agent a besoin. Date, branche, contexte du projet, entrées mémoire, principes philosophiques.
Couche 3 : Validation (PostToolUse) — Vérifier que les actions complétées respectent les standards. Vérification du consensus post-délibération, journalisation des sorties.
Couche 4 : Qualité (Stop) — Contrôler la sortie finale. Pride check, quality gate, reviewer stop gate.
Chaque couche est indépendante. Si un hook PreToolUse échoue silencieusement, le hook Stop détecte tout de même les problèmes de qualité. La défense en profondeur, appliquée au comportement des agents IA.
Configuration
Les hooks résident dans .claude/settings.json :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "~/.claude/hooks/git-safety-guardian.sh",
"timeout": 5000
}
],
"PreToolUse:Task": [
{
"command": "~/.claude/hooks/recursion-guard.sh"
}
]
}
}
Le champ matcher filtre quels outils déclenchent le hook. PreToolUse:Task est un raccourci pour un matcher qui ne se déclenche que sur les invocations de l’outil Task. Les hooks asynchrones (async: true) s’exécutent en arrière-plan sans bloquer.7
Hiérarchie des portées
- Niveau utilisateur (
~/.claude/settings.json) — s’applique à tous les projets. Mes 95 hooks résident ici. - Niveau projet (
.claude/settings.json) — ajoute des hooks spécifiques au projet. - Frontmatter de skill/sous-agent — limité au cycle de vie d’un composant spécifique.8
Ce que je ferais différemment
Commencer avec 3 hooks, pas 25. Mon premier mois a produit 25 hooks, dont beaucoup ajoutaient du contexte que l’agent possédait déjà. La surcharge liée au chargement de 25 hooks à chaque appel d’outil était mesurable. J’ai finalement élagué pour ne garder que les hooks qui produisaient une vraie valeur (prévention d’incidents réels, détection de vrais problèmes de qualité).
Piloté par la configuration dès le premier jour. J’ai passé deux semaines à refactoriser les seuils codés en dur en fichiers JSON de configuration. Ce refactoring aurait été gratuit si j’avais commencé avec des fichiers de configuration.
Mettre en place l’infrastructure de test tôt. Les 20 premiers hooks n’avaient aucun test. Quand j’ai ajouté le harnais de test (48 tests d’intégration bash), j’ai découvert 3 hooks qui échouaient silencieusement sur des cas limites. Les tests auraient dû être livrés avec le hook n°1.
Points clés à retenir
Pour les développeurs qui débutent avec les hooks : - Commencez avec trois hooks : sécurité git (PreToolUse:Bash), injection de contexte (UserPromptSubmit), et contrôle qualité (Stop) ; n’en ajoutez d’autres que lorsque vous avez des incidents qui les justifient - Utilisez le cadre de timing décisionnel : l’architecture des hooks est irréversible (95 hooks en dépendent), investissez donc dans le modèle de cycle de vie avant d’écrire des hooks
Pour les équipes qui standardisent les hooks : - Standardisez les hooks au niveau utilisateur pour que chaque membre de l’équipe bénéficie des mêmes garde-fous de sécurité - Suivez les métriques des hooks (opérations bloquées, incidents interceptés) pour justifier le coût de maintenance - Examinez les journaux des hooks mensuellement pour identifier de nouveaux patterns à automatiser
Références
-
Infrastructure de hooks de l’auteur. 95 hooks répartis sur 6 événements de cycle de vie, développés sur 9 mois (2025-2026). ↩
-
Anthropic, « Documentation Claude Code », 2025. Événements du cycle de vie des hooks. ↩
-
Anthropic, « Documentation Claude Code », 2025. Format JSON d’entrée/sortie des hooks. ↩
-
git-safety-guardian.sh de l’auteur. 8 tentatives de force-push interceptées, suivies dans
~/.claude/state/. ↩ -
recursion-guard.sh de l’auteur. Modèle d’héritage de budget documenté dans
~/.claude/configs/recursion-limits.json. ↩ -
Architecture pilotée par la configuration de l’auteur. 14 fichiers JSON de configuration encodant tous les seuils et règles des hooks. ↩
-
Anthropic, « Documentation Claude Code », 2025. Configuration des hooks et exécution asynchrone. ↩
-
Anthropic, « Documentation Claude Code », 2025. Hiérarchie des portées des hooks. ↩