Autres types de hooks
Sur cette page
PreToolUse et PostToolUse ne se déclenchent qu’autour d’un appel d’outil. Or beaucoup d’automatisations utiles se rattachent à d’autres moments de la vie d’une session : son démarrage, sa fin, l’instant où vous soumettez un prompt, le moment où Claude s’arrête, une compaction, un changement de modèle, un fichier qui change sur le disque. Claude Code expose une trentaine d’événements. Cette leçon les classe, explique ce que chacun reçoit, et donne la méthode pour découvrir la forme exacte des données avant d’écrire une ligne de logique.
L’inventaire des événements
| Moment | Événement | Ce qu’on en fait |
|---|---|---|
| Session | SessionStart (matchers startup, resume, clear, compact, fork) | Charger du contexte, des variables d’environnement, réinjecter après compaction |
Setup | Préparation unique en CI (claude --init-only, -p --init) | |
SessionEnd (matchers clear, resume, logout, prompt_input_exit, other) | Nettoyage, statistiques ; 1,5 s de budget | |
InstructionsLoaded, ConfigChange, CwdChanged, DirectoryAdded, FileChanged | Journaliser quel CLAUDE.md a chargé, auditer une modification de réglages, recharger l’environnement, réagir à un fichier qui change | |
| Tour | UserPromptSubmit | Enrichir ou bloquer un prompt, nommer la session ; 30 s de délai par défaut |
UserPromptExpansion | Bloquer l’expansion d’une commande tapée | |
Stop, StopFailure | Empêcher Claude de s’arrêter tant qu’une condition n’est pas remplie ; réagir à une erreur d’API | |
Notification (matchers permission_prompt, idle_prompt…) | Prévenir ailleurs que dans le terminal | |
PreCompact (manual, auto), PostCompact | Bloquer ou accompagner une compaction | |
PreModelSwitch, PostModelSwitch | Interdire un modèle, ajouter du contexte après un changement | |
| Outil | PreToolUse, PostToolUse, PostToolUseFailure, PostToolBatch | Bloquer, réécrire, donner du feedback, arrêter la boucle avant le prochain appel au modèle |
PermissionRequest, PermissionDenied | Répondre à une demande de permission à la place de l’utilisateur ; réagir à un refus du mode Auto | |
| Agents | SubagentStart, SubagentStop (matcher = type d’agent), TeammateIdle, TaskCreated, TaskCompleted | Suivre et contrôler les sous-agents et les équipes |
| Autres | WorktreeCreate, WorktreeRemove, Elicitation, ElicitationResult, MessageDisplay | Remplacer la création de worktree par Git, répondre à un serveur MCP qui pose une question, modifier l’affichage d’un message |
Un hook Stop peut vous prévenir par un signal sonore qu’une tâche longue est terminée. Un hook SessionStart injecte un état que Claude ne devinerait pas, comme la branche courante ou les tickets en cours. Un hook UserPromptSubmit intervient avant même que Claude ne voie votre message : c’est le point d’entrée naturel pour enrichir ou filtrer ce que vous envoyez, et il se déclenche aussi quand une tâche planifiée ou un sous-agent en arrière-plan relance la session.
Le défi : des entrées qui changent
Cette richesse a une contrepartie. L’entrée stdin de vos commandes change selon l’événement, et le tool_input des événements d’outils change selon l’outil. Un script écrit pour Read en supposant un file_path ne trouvera rien de tel branché sur Bash, dont le champ est command. Comparez l’entrée d’un PostToolUse sur Write :
{
"session_id": "9ecf22fa-…",
"cwd": "/code/projet",
"permission_mode": "auto",
"hook_event_name": "PostToolUse",
"tool_name": "Write",
"tool_input": { "file_path": "/code/projet/README.md", "content": "…" },
"tool_response": { "filePath": "/code/projet/README.md", "type": "create" },
"tool_use_id": "toolu_01ABC…",
"duration_ms": 12
}
et celle d’un hook Stop :
{
"session_id": "af9f50b6-…",
"cwd": "/code/projet",
"hook_event_name": "Stop",
"stop_hook_active": false,
"last_assistant_message": "J'ai corrigé la validation et les tests passent.",
"background_tasks": [],
"session_crons": []
}
Seuls les champs communs survivent : session_id, cwd, transcript_path, hook_event_name, et selon l’événement permission_mode, effort, agent_id. Tout le reste est spécifique. Stop fournit last_assistant_message, le texte de la dernière réponse, pour qu’un hook de lecture à voix haute ou de notification n’ait pas à parser la transcription, et background_tasks pour distinguer « la session est finie » de « elle attend un travail en arrière-plan ». Notification fournit message, title et notification_type. SessionStart fournit source, SessionEnd fournit reason, UserPromptSubmit fournit prompt, SubagentStop fournit agent_type, agent_transcript_path et son propre last_assistant_message.
Le verdict varie aussi. Les événements d’outils utilisent hookSpecificOutput.permissionDecision ; UserPromptSubmit, PostToolUse, Stop et PreCompact un decision: "block" de premier niveau avec reason ; PermissionRequest un objet decision.behavior ; SessionStart, SubagentStart et PostModelSwitch n’ont pas de décision et ne font qu’ajouter du contexte (additionalContext) ; Notification, SessionEnd, PostCompact, InstructionsLoaded n’en ont aucune et servent aux effets de bord. Le tableau Decision control de la référence résume tout cela ; consultez-le avant d’écrire un hook sur un événement nouveau.
Astuce : le hook de débogage
Plutôt que de deviner, faites-vous montrer les données. Ce hook écrit chaque événement d’outil, sur une ligne, dans un fichier :
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"command": "jq -c . >> \"$CLAUDE_PROJECT_DIR\"/.claude/hook-events.jsonl"
}
]
}
]
}
}
Le matcher * capture tous les outils, jq -c . compacte chaque JSON sur une ligne. Installez-le, menez une session ordinaire en déclenchant l’outil qui vous intéresse, puis ouvrez le fichier : vous y lisez la structure réelle, avec les noms de champs exacts, sur laquelle écrire votre condition. Déclinez-le sur n’importe quel événement (Stop, Notification, SessionStart) pour en découvrir la forme. Prenez l’habitude d’y recourir en premier : dix minutes d’observation vous épargneront une heure d’hypothèses. En complément, claude --debug journalise chaque exécution de hook dans ~/.claude/debug/<session-id>.txt, et la référence donne un exemple d’entrée pour chaque événement.
Au-delà de la commande shell
Les événements ne se limitent pas aux hooks command. Un hook http envoie le même JSON à une URL et lit le verdict dans la réponse, ce qui permet à une équipe de centraliser un service d’audit. Un hook mcp_tool appelle un outil d’un serveur MCP. Les hooks prompt et agent de la leçon précédente portent du jugement là où une règle ne suffit pas. Et les hooks ne vivent pas que dans les fichiers de réglages : une skill ou un sous-agent peut en déclarer dans son en-tête, actifs le temps de leur exécution, et un plugin les embarque dans hooks/hooks.json.
À vous de jouer
- Installez le hook de débogage, faites trois actions différentes (lecture, modification, commande), puis lisez
.claude/hook-events.jsonl: repéreztool_inputpour chaque outil. - Déclinez-le sur
Stopet surNotification, puis comparez les entrées. - Écrivez un hook
UserPromptSubmitqui ajoute la branche Git courante en contexte :echo "Branche : $(git branch --show-current)"suffit, puisque le stdout de cet événement est transmis à Claude. - Écrivez un hook
PreModelSwitchqui refuse tout passage à un modèle dont le nom contienthaiku, avecpermissionDecision: "deny".
Sources officielles : Hooks reference · Automate actions with hooks
Points clés à retenir
- Une trentaine d’événements couvrent la session, le tour, les outils, les agents et le reste du cycle de vie
- Chaque événement reçoit des champs propres (
tool_inputpar outil,last_assistant_messagesurStop,sourcesurSessionStart…) et accepte un format de verdict propre - Un hook de débogage (
jq -c . >> fichier) révèle la structure exacte avant d’écrire la logique ;claude --debugjournalise chaque exécution - Les hooks
http,mcp_tool,promptetagentcomplètent les hookscommand; skills, sous-agents et plugins peuvent en déclarer