Guide des Hooks
Les hooks Claude Code depuis les bases : codes de sortie, sortie JSON, commandes asynchrones, endpoints HTTP, matchers PreToolUse et PostToolUse, patterns de production.
Arrêtez de configurer. Commencez à construire.
Templates SaaS avec orchestration IA.
Problème : Tu approuves chaque écriture de fichier à la main. Puis chaque commande. Puis chaque passe de formatage. Vingt interruptions plus tard, la fonctionnalité que tu construisais a glissé hors de ta tête.
Victoire rapide : Ajoute ça dans .claude/settings.json et ne réapprouve plus jamais un formatage Prettier :
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
}
]
}
]
}
}Chaque fichier écrit par Claude se formate maintenant automatiquement. Zéro clic. Zéro changement de contexte.
Les 12 Événements du Cycle de Vie des Hooks
Chaque événement Claude Code peut déclencher un hook. Un hook exécute une commande shell, ou envoie un prompt LLM, quand cet événement se produit. La liste complète :
| Hook | Quand il se déclenche | Peut bloquer ? | Meilleur usage |
|---|---|---|---|
| SessionStart | La session commence ou reprend | NON | Charger le contexte, définir les variables d'env |
| UserPromptSubmit | Tu appuies sur Entrée | OUI | Injection de contexte, validation |
| PreToolUse | Avant l'exécution de l'outil | OUI | Blocage sécurité, auto-approbation (étend le système de permissions) |
| PermissionRequest | La boîte de dialogue de permission apparaît | OUI | Auto-approuver/refuser |
| PostToolUse | Après le succès de l'outil | NON* | Auto-format, lint, log |
| PostToolUseFailure | Après l'échec de l'outil | NON | Gestion des erreurs |
| SubagentStart | Lancement d'un sous-agent | NON | Initialisation du sous-agent |
| SubagentStop | Le sous-agent termine | OUI | Validation du sous-agent |
| Stop | Claude finit de répondre | OUI | Application des tâches |
| PreCompact | Avant la compaction | NON | Sauvegarde de transcript |
| Setup | Avec --init/--maintenance | NON | Configuration initiale |
| SessionEnd | La session se termine | NON | Nettoyage, journalisation |
| Notification | Claude envoie une notification | NON | Alertes bureau, TTS |
*Un hook PostToolUse peut renvoyer un message à Claude, mais il ne peut pas annuler l'outil qui a déjà été exécuté.
Codes de Sortie : Le Mécanisme de Contrôle
Les codes de sortie sont la façon dont un hook parle à Claude :
| Code de sortie | Ce qui se passe |
|---|---|
| 0 | Succès — le hook a fonctionné, stdout traité pour JSON |
| 2 | Blocage — opération stoppée, stderr envoyé à Claude |
| Autre | Erreur — stderr affiché à l'utilisateur, l'exécution continue |
Le code de sortie 2 est celui qui bloque. Renvoie-le depuis un hook PreToolUse et l'outil ne s'exécute jamais. Renvoie-le depuis un hook Stop et Claude doit continuer au lieu de terminer.
Types de Hooks : Command, HTTP, Prompt et Agent
Il existe quatre types de gestionnaires. Choisis celui qui correspond au travail.
Les hooks Command exécutent des scripts shell :
{
"type": "command",
"command": "python validator.py",
"timeout": 30
}Les hooks HTTP envoient une requête POST à un endpoint et reçoivent du JSON en retour : Nouveau - Fév 2026
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}Claude Code envoie le JSON de l'événement comme corps de la requête (Content-Type: application/json), et la réponse suit le même schéma de sortie JSON qu'un hook command. Quelques différences :
- Erreurs non bloquantes : Une réponse non-2xx, un échec de connexion ou un timeout n'arrêteront pas l'exécution. Pour bloquer vraiment un appel d'outil, renvoie une réponse 2xx avec
decision: "block"dans le corps JSON. - Variables d'env dans les headers : Référence les variables avec
$VAR_NAMEou${VAR_NAME}dans les valeurs de headers. Seuls les noms listés dansallowedEnvVarssont résolus. Tout le reste devient une chaîne vide. - Déduplication : Les hooks HTTP se dédupliquent par URL. Les hooks Command par chaîne de commande.
- Config uniquement : Tu dois modifier le JSON des paramètres directement. Le menu interactif
/hooksne supporte que les hooks command.
Les hooks Prompt utilisent l'évaluation LLM (bien adapté pour Stop et SubagentStop) :
{
"type": "prompt",
"prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks complete.",
"timeout": 30
}Le LLM répond avec {"ok": true} ou {"ok": false, "reason": "..."}.
Les hooks Agent démarrent un sous-agent qui peut lire des fichiers (Read, Grep, Glob) pour une vérification plus approfondie :
{
"type": "agent",
"prompt": "Verify all test files have corresponding implementation files",
"timeout": 60
}Un hook agent explore d'abord le codebase, puis décide. C'est plus complet qu'un hook prompt. C'est aussi plus lent (timeout par défaut : 60s contre 30s pour les prompts).
Hooks Asynchrones (Non Bloquants) Nouveau - Jan 2026
Définis async: true et le hook s'exécute en arrière-plan au lieu de bloquer Claude. Anthropic a livré ça en janvier 2026 :
{
"type": "command",
"command": "node backup-script.js",
"async": true,
"timeout": 30
}Idéal pour :
- La journalisation et l'analytique
- La création de sauvegardes (PreCompact)
- Les notifications
- Tout effet de bord qui ne doit pas ralentir les choses
Pas adapté pour :
- Le blocage de sécurité (PreToolUse avec code de sortie 2)
- Les décisions d'auto-approbation (PermissionRequest)
- Tout hook dont Claude a besoin du résultat
Hooks HTTP : Envoyer vers des Endpoints Nouveau - Fév 2026
Pointe un hook HTTP vers une URL web que tu possèdes et l'événement y parvient au lieu d'aller dans un script local. Ça ouvre des patterns que les hooks command ne pouvaient pas gérer proprement :
- Services de validation distants qui appliquent des politiques d'équipe
- Journalisation centralisée vers un système d'audit partagé
- Intégrations webhook avec Slack, PagerDuty ou des tableaux de bord personnalisés
- Architectures microservice où la logique des hooks tourne en parallèle de ton API
Hook HTTP Basique
Route toutes les commandes Bash vers un endpoint de validation :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "http",
"url": "http://localhost:8080/hooks/pre-tool-use",
"timeout": 30,
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"]
}
]
}
]
}
}Ce qui arrive sur ton serveur est exactement le payload stdin qu'un hook command lirait, livré via le corps POST. Réponds dans le format de sortie JSON et Claude Code le parse de la même façon.
Gestion des Réponses HTTP
Les hooks HTTP lisent les réponses différemment. Pas de codes de sortie. Juste le statut HTTP et le corps de la réponse :
| Réponse | Comportement |
|---|---|
| 2xx + corps vide | Succès, équivalent au code de sortie 0 sans sortie |
| 2xx + corps texte | Succès, le texte est ajouté comme contexte à Claude |
| 2xx + corps JSON | Succès, parsé avec le même schéma de sortie JSON que les hooks command |
| Statut non-2xx | Erreur non bloquante, l'exécution continue |
| Échec de connexion / timeout | Erreur non bloquante, l'exécution continue |
Le point à surveiller : Un hook HTTP ne peut pas bloquer sur les codes de statut seuls. Renvoie un 4xx ou un 5xx et tu obtiens une erreur journalisée pendant que l'exécution continue. Un vrai blocage, ou un vrai refus de permission, nécessite une réponse 2xx dont le corps JSON contient les champs de décision :
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Blocked by security policy"
}
}Authentification Sécurisée par Header
Le champ headers peut interpoler des variables d'environnement, mais seulement quand la variable apparaît dans allowedEnvVars. Ça empêche les secrets de fuiter par accident :
{
"type": "http",
"url": "https://hooks.example.com/validate",
"headers": {
"Authorization": "Bearer $API_KEY",
"X-Team-Id": "$TEAM_ID"
},
"allowedEnvVars": ["API_KEY", "TEAM_ID"]
}Un $VAR qui n'est pas sur la liste blanche devient silencieusement une chaîne vide. Pas d'avertissement. Pas d'erreur. Juste vide. Vérifie la liste blanche deux fois avant de déployer.
Sortie JSON : Contrôle Avancé
Quand un code de sortie est trop brut, un hook peut renvoyer une réponse JSON structurée à la place.
Décisions PreToolUse
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Safe read operation",
"updatedInput": { "command": "modified-command" },
"additionalContext": "Context for Claude"
}
}"allow": Contourne le système de permissions"deny": Bloque l'outil, dit à Claude pourquoi"ask": Demande confirmation à l'utilisateurupdatedInput: Modifie les paramètres de l'outil avant l'exécution
Décisions PermissionRequest
{
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {
"behavior": "allow",
"updatedInput": { "command": "npm run lint" }
}
}
}Application Stop/SubagentStop
{
"decision": "block",
"reason": "Tests failing. Fix them before completing."
}Hook 1 : Auto-Format à la Sauvegarde
Exécute les formateurs après chaque écriture de fichier :
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit|MultiEdit",
"hooks": [
{
"type": "command",
"command": "npx prettier --write \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
},
{
"type": "command",
"command": "npx eslint --fix \"$CLAUDE_TOOL_INPUT_FILE_PATH\""
}
]
}
]
}
}Les deux hooks s'exécutent en parallèle. Format et lint terminent tous les deux avant que la réponse de Claude apparaisse.
Hook 2 : Injection de Contexte de Session
Charge le contexte au démarrage de la session :
{
"hooks": {
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "echo '## Git Status' && git status --short && echo '## TODOs' && grep -r 'TODO:' src/ | head -5"
}
]
}
]
}
}Persister les Variables d'Environnement
Un hook SessionStart ou Setup peut définir des variables d'environnement qui persistent pour toute la session :
#!/bin/bash
if [ -n "$CLAUDE_ENV_FILE" ]; then
echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
echo 'export API_KEY=your-key' >> "$CLAUDE_ENV_FILE"
fi
exit 0Hook 3 : Blocage de Sécurité
Arrête les opérations dangereuses avec PreToolUse :
#!/usr/bin/env python3
import json
import sys
import re
DANGEROUS_PATTERNS = [
r'\brm\s+.*-[a-z]*r[a-z]*f',
r'sudo\s+rm',
r'chmod\s+777',
r'git\s+push\s+--force.*main',
]
input_data = json.load(sys.stdin)
if input_data.get('tool_name') == 'Bash':
command = input_data.get('tool_input', {}).get('command', '')
for pattern in DANGEROUS_PATTERNS:
if re.search(pattern, command, re.IGNORECASE):
print("BLOCKED: Dangerous pattern", file=sys.stderr)
sys.exit(2)
sys.exit(0)Hook 4 : Auto-Approbation des Commandes Sûres
Utilise PermissionRequest pour sauter le prompt quand une commande est connue comme sûre :
{
"hooks": {
"PermissionRequest": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/auto-approve.py"
}
]
}
]
}
}#!/usr/bin/env python3
import json
import sys
SAFE_PREFIXES = ['npm test', 'npm run lint', 'git status', 'ls']
input_data = json.load(sys.stdin)
command = input_data.get('tool_input', {}).get('command', '')
for prefix in SAFE_PREFIXES:
if command.startswith(prefix):
output = {
"hookSpecificOutput": {
"hookEventName": "PermissionRequest",
"decision": {"behavior": "allow"}
}
}
print(json.dumps(output))
sys.exit(0)
sys.exit(0) # Normal flow for other commandsHook 5 : Sauvegarde de Transcript
PreCompact est le moment de prendre un instantané d'un transcript. Marque le hook async: true puisqu'une sauvegarde ne doit jamais bloquer Claude :
{
"hooks": {
"PreCompact": [
{
"hooks": [
{
"type": "command",
"command": "python .claude/hooks/backup-transcript.py",
"async": true
}
]
}
]
}
}Les matchers manual et auto séparent /compact de la compaction automatique.
Hook 6 : Application de Complétion des Tâches
Un hook Stop empêche Claude de déclarer un travail terminé trop tôt :
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "prompt",
"prompt": "Check if all tasks are complete: $ARGUMENTS. Return {\"ok\": false, \"reason\": \"...\"} if work remains."
}
]
}
]
}
}Le guide Stop Hook couvre les versions basées sur des commandes de ce pattern.
Hook 7 : Activation de Skill
Un hook d'activation de skill intervient sur le prompt avant que Claude le voie et ajoute des recommandations de skills à la fin. Chaque mot-clé que tu tapes est vérifié par rapport à un ensemble de règles, donc le skill de tuning Postgres arrive dans le contexte dès que tu mentionnes une requête lente. Vingt et une catégories de skills utilisent déjà ce pattern dans le SkillActivationHook du Code Kit :
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"command": "node .claude/hooks/SkillActivationHook/skill-activation-prompt.mjs"
}
]
}
]
}
}Hook 8 : Sauvegardes par Seuil via StatusLine
StatusLine est le seul endroit où tu obtiens des métriques de contexte en direct. C'est donc le bon endroit pour lancer une sauvegarde quand tu atteins un seuil :
{
"statusLine": {
"type": "command",
"command": "node .claude/hooks/ContextRecoveryHook/statusline-monitor.mjs"
}
}Critique : Le champ remaining_percentage intègre déjà un buffer d'autocompact fixe de 33K tokens. Pour obtenir le vrai nombre de "libre jusqu'à l'autocompact", fais le calcul toi-même :
const AUTOCOMPACT_BUFFER_TOKENS = 33000;
const autocompactBufferPct = (AUTOCOMPACT_BUFFER_TOKENS / windowSize) * 100;
const freeUntilCompact = Math.max(0, pctRemainTotal - autocompactBufferPct);Deux systèmes de déclenchement tournent en parallèle dans le script de sauvegarde. Les tokens sont le déclencheur principal. Les pourcentages servent de filet de sécurité en dessous :
// Token-based triggers (primary - works across all window sizes)
const TOKEN_FIRST_BACKUP = 50000; // First backup at 50k tokens used
const TOKEN_UPDATE_INTERVAL = 10000; // Update every 10k tokens after
if (currentTotalTokens >= TOKEN_FIRST_BACKUP) {
if (lastBackupTokens < TOKEN_FIRST_BACKUP) {
runBackup(
sessionId,
`tokens_${Math.round(currentTotalTokens / 1000)}k_first`,
);
} else if (currentTotalTokens - lastBackupTokens >= TOKEN_UPDATE_INTERVAL) {
runBackup(
sessionId,
`tokens_${Math.round(currentTotalTokens / 1000)}k_update`,
);
}
}
// Percentage-based triggers (safety net, especially for 200k windows)
const THRESHOLDS = [30, 15, 5];
for (const threshold of THRESHOLDS) {
if (state.lastFree > threshold && freeUntilCompact <= threshold) {
runBackup(sessionId, `crossed_${threshold}pct`);
}
}
if (freeUntilCompact < 5 && freeUntilCompact < state.lastFree) {
runBackup(sessionId, "continuous");
}PreCompact ne se déclenche qu'au moment de la compaction. Les sauvegardes basées sur StatusLine font des instantanés de session à l'avance, pendant que rien n'est encore cassé. Le déclencheur par tokens compte surtout sur les grandes fenêtres comme 1M, où un déclencheur par pourcentage arriverait beaucoup trop tard pour aider.
Architecture : Structure en Trois Fichiers
Le système de sauvegarde se divise en trois fichiers avec des rôles séparés :
.claude/hooks/ContextRecoveryHook/
├── backup-core.mjs # Shared backup logic (parsing, formatting, saving)
├── statusline-monitor.mjs # Threshold detection + display (calls backup-core)
└── conv-backup.mjs # PreCompact trigger (calls backup-core)| Fichier | Déclencheur | Responsabilité |
|---|---|---|
backup-core.mjs | Appelé par les autres | Parser le transcript, formater en markdown, sauvegarder le fichier, mettre à jour l'état |
statusline-monitor.mjs | StatusLine (continu) | Surveiller les tokens/% de contexte, détecter les déclencheurs, afficher le statut |
conv-backup.mjs | Hook PreCompact | Gérer l'événement de pré-compaction |
Un seul fichier possède le vrai travail de sauvegarde. Modifie le rendu markdown, le nommage des fichiers ou l'enregistrement de l'état dans backup-core.mjs, et les deux appelants héritent du changement gratuitement.
Nommage des Fichiers de Sauvegarde
Les sauvegardes utilisent des noms de fichiers numérotés avec timestamps pour que l'historique soit facile à parcourir :
.claude/backups/1-backup-26th-Jan-2026-4-30pm.md
.claude/backups/2-backup-26th-Jan-2026-5-15pm.md
.claude/backups/3-backup-26th-Jan-2026-5-45pm.mdAffichage StatusLine
Si une sauvegarde existe pour la session en cours, la statusline affiche son chemin directement :
[!] 25.0% free (50.0K/200K)
-> .claude/backups/3-backup-26th-Jan-2026-5-45pm.mdTu sais d'un coup d'œil quel fichier recharger une fois la compaction terminée.
Suivi d'État
Les deux hooks partagent un fichier d'état à ~/.claude/claudefast-statusline-state.json :
{
"sessionId": "abc123",
"lastFreeUntilCompact": 25.5,
"currentBackupPath": ".claude/backups/3-backup-26th-Jan-2026-5-45pm.md"
}Workflow recommandé : Au moment où la compaction se déclenche, tape /clear pour repartir sur une base propre et recharge le chemin de sauvegarde que la statusline a affiché. Sinon le résumé auto-généré et tes notes restaurées se marcheront dessus.
Hook 9 : Hooks Setup pour l'Installation et la Maintenance
Un hook Setup s'exécute avant le démarrage de ta session, déclenché par des flags CLI spéciaux :
claude --init # Triggers Setup hook with matcher "init"
claude --init-only # Same as above, but exits after hook (CI-friendly)
claude --maintenance # Triggers Setup hook with matcher "maintenance"Configure-les avec des matchers dans settings.json :
{
"hooks": {
"Setup": [
{
"matcher": "init",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/setup_init.py",
"timeout": 120
}
]
},
{
"matcher": "maintenance",
"hooks": [
{
"type": "command",
"command": ".claude/hooks/setup_maintenance.py",
"timeout": 60
}
]
}
]
}
}Combine les flags avec les prompts : Ajoute un prompt directement sur le flag.
claude --init "/install"
Le hook s'exécute en premier, de façon déterministe. Puis la commande /install se déclenche, de façon agentique. Une invocation, deux modes d'exécution : une configuration scriptée suivie d'un agent qui raisonne dessus.
Le guide Setup Hooks couvre le pattern complet de bout en bout, y compris les setups justfile et les flux d'onboarding interactifs.
Emplacements de Configuration
| Emplacement | Portée | Priorité |
|---|---|---|
| Politique managée | Entreprise | La plus haute |
.claude/settings.json | Projet (partagé) | Haute |
.claude/settings.local.json | Projet (personnel) | Moyenne |
~/.claude/settings.json | Tous les projets | La plus basse |
Désactiver et Restreindre les Hooks
Désactiver Tous les Hooks
Si un hook pose problème ou si tu veux juste une base propre, passe disableAllHooks à true dans tes paramètres :
{
"disableAllHooks": true
}Chaque hook à chaque portée se tait. Utilisateur, projet, local. Utilise ça quand tu débogues un hook incontrôlable ou quand tu as besoin d'une base propre connue.
Restrictions de Hooks Managés
Les organisations qui ont besoin d'un contrôle centralisé peuvent définir allowManagedHooksOnly à true dans les paramètres managés :
{
"allowManagedHooksOnly": true
}Active-le et les seuls hooks que Claude Code exécutera sont ceux déclarés dans les paramètres managés plus les hooks SDK. Portée utilisateur, portée projet, portée plugin : les trois sont ignorées. Aucun hook local ne peut contourner la posture de sécurité de l'organisation.
Associe ce paramètre avec allowManagedPermissionRulesOnly, qui verrouille les règles de permission de la même façon. Ensemble, ils donnent aux admins le contrôle sur toute la surface : les permissions elles-mêmes, plus chaque hook qui les étend.
Syntaxe des Matchers
| Pattern | Correspond à |
|---|---|
"" ou omis | Tous les outils |
"Bash" | Uniquement Bash (exact, sensible à la casse) |
| `"Write | Edit"` |
"mcp__memory__.*" | Tous les outils MCP memory |
Critique : Ne laisse pas d'espaces autour du |. Et les chaînes de matcher sont sensibles à la casse.
Matchers Spécifiques aux Événements
SessionStart : startup, resume, clear, compactPreCompact : manual, autoSetup : init, maintenanceNotification : permission_prompt, idle_prompt, auth_success
Variables d'Environnement
| Variable | Description |
|---|---|
CLAUDE_PROJECT_DIR | Racine du projet (tous les hooks) |
CLAUDE_ENV_FILE | Persister les variables d'env (SessionStart, Setup) |
CLAUDE_CODE_REMOTE | "true" si web, vide si CLI |
Débogage
Le hook ne se déclenche pas ?
- Vérifie la syntaxe du matcher (sensible à la casse, pas d'espaces)
- Vérifie l'emplacement du fichier de paramètres
- Teste :
echo '{"session_id":"test"}' | python your-hook.py
La commande échoue ?
- Ajoute des logs :
command 2>&1 | tee ~/.claude/hook-debug.log - Lance avec debug :
claude --debug - Les hooks cassent sur d'autres systèmes d'exploitation ? Consulte les patterns de hooks cross-platform
Boucles infinies avec Stop ?
- Vérifie toujours le flag
stop_hook_activeen premier
Commence Avec Un Seul Hook
Choisis le point de friction qui fait le plus mal :
- Tu formates chaque fichier à la main ? Formateur PostToolUse
- Tu approuves des commandes sûres toute la journée ? Auto-approbation PermissionRequest
- La session s'ouvre sans contexte ? Injection SessionStart
- Tu perds ta progression à cause de la compaction ? Sauvegarde PreCompact
- Les tâches sont déclarées terminées trop tôt ? Application Stop
Un hook. Un point de friction éliminé. Puis itère. Si tu veux sauter la configuration, le Code Kit arrive avec 5 hooks déjà configurés : activation de skill, auto-formatage, récupération de contexte, automatisation des permissions et validation de code. Chacun est construit sur les patterns ci-dessus.
Arrêtez de configurer. Commencez à construire.
Templates SaaS avec orchestration IA.