En bref

Les hooks Claude Code sont des commandes shell exécutées automatiquement à des points précis du cycle de vie d’une session. Contrairement aux instructions données dans un CLAUDE.md, les hooks sont déterministes : Claude ne peut pas choisir de les ignorer. Ils s’exécutent toujours, quelles que soient les instructions reçues par le modèle.

Cas d’usage principaux : logger toutes les commandes Bash exécutées par Claude, bloquer l’écriture sur des fichiers sensibles, envoyer une notification desktop quand Claude attend une saisie, ou ré-injecter du contexte dans la session après une compaction.

22 événements sont disponibles à ce jour. Deux ont été ajoutés récemment : InstructionsLoaded (v2.1.69) et StopFailure (v2.1.78).


Prérequis

  • Claude Code installé — version v2.1.78 minimum recommandée (plusieurs bugs hooks corrigés entre v2.1.72 et v2.1.78)
  • Accès au fichier settings.json : ~/.claude/settings.json (global) ou .claude/settings.json dans votre projet
  • Aucune dépendance supplémentaire pour les hooks de type command — jq recommandé pour parser le JSON stdin

Comment fonctionnent les hooks

Le mécanisme de base

Un hook reçoit un payload JSON sur stdin au moment où l’événement se déclenche. Il répond via son code de sortie et son stdout :

Code de sortieEffet
0Succès — stdout parsé pour JSON de décision — exécution continue
2Erreur bloquante — stderr transmis à Claude comme feedback — action bloquée
AutreErreur non-bloquante — stderr visible uniquement en mode verbose

Quatre types de handlers sont disponibles : command (shell), http (POST vers une URL), prompt (appel LLM single-turn, Haiku par défaut), agent (sous-agent multi-turns). La majorité des cas d’usage se couvrent avec command.

Les 22 événements, par catégorie

Cycle de vie de la session

ÉvénementDéclenchementBloquable
SessionStartDébut ou reprise de sessionNon
InstructionsLoadedChargement d’un CLAUDE.md ou règleNon
SessionEndFin de sessionNon
PreCompactAvant compaction du contexteNon
PostCompactAprès compactionNon
ConfigChangeModification d’un fichier de config pendant la sessionOui*

*ConfigChange sur policy_settings ne peut pas être bloqué (sécurité entreprise).

Prompts et outils

ÉvénementDéclenchementBloquable
UserPromptSubmitSoumission d’un prompt utilisateurOui
PreToolUseAvant exécution d’un outilOui
PostToolUseAprès exécution réussie d’un outilNon**
PostToolUseFailureAprès échec d’un outilNon
PermissionRequestQuand une dialog de permission apparaîtOui

**PostToolUse avec exit 2 montre l’erreur à Claude, mais ne peut pas annuler une action déjà exécutée.

Agents et coordination

ÉvénementDéclenchementBloquable
SubagentStartLancement d’un sous-agentNon (async)
SubagentStopFin d’un sous-agentOui
StopFin de réponse de ClaudeOui
StopFailureFin de tour suite à une erreur APINon
TeammateIdleTeammate (Agent Teams) passe en idleOui
TaskCompletedTâche marquée complétéeOui
NotificationNotification envoyée par Claude CodeNon

Worktrees et MCP

ÉvénementDéclenchementBloquable
WorktreeCreateCréation d’un worktree gitOui
WorktreeRemoveSuppression d’un worktreeNon
ElicitationServeur MCP demande une saisie utilisateurOui
ElicitationResultRéponse à une elicitation MCPOui

Configuration dans settings.json

Structure de base :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt",
            "timeout": 600
          }
        ]
      }
    ]
  }
}

Le champ matcher est un pattern regex appliqué au champ filtré de l’événement (tool_name pour PreToolUse/PostToolUse, source pour SessionStart, etc.). Utilisez "" ou "*" pour intercepter tous les déclenchements.

Trois emplacements de fichier selon la portée souhaitée :

  • ~/.claude/settings.json — s’applique à tous les projets, non partageable
  • .claude/settings.json — projet uniquement, committable dans le dépôt
  • .claude/settings.local.json — projet uniquement, gitignored

Exemples pratiques

Logging des commandes Bash exécutées par Claude :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [{ "type": "command", "command": "jq -r '.tool_input.command' >> ~/.claude/command-log.txt" }]
      }
    ]
  }
}

Bloquer l’écriture sur des fichiers protégés :

#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Bloqué : $FILE_PATH correspond au pattern '$pattern'" >&2
    exit 2
  fi
done
exit 0
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh" }]
      }
    ]
  }
}

Notification desktop quand Claude attend :

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [{ "type": "command", "command": "notify-send 'Claude Code' 'Attention requise'" }]
      }
    ]
  }
}

Ré-injecter du contexte après compaction (SessionStart avec matcher compact) :

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [{ "type": "command", "command": "echo 'Rappel : utiliser Bun, pas npm. Run bun test avant commit.'" }]
      }
    ]
  }
}

Anti-boucle infinie pour les hooks Stop : vérifier stop_hook_active avant d’agir. Si true, sortir immédiatement avec exit 0, sinon le hook se déclenche en boucle.


Pièges courants

Hooks vs MCP : deux mécanismes distincts

Les hooks et les serveurs MCP répondent à des besoins différents et ne sont pas interchangeables :

MCP ServersHooks
ObjectifExposer de nouveaux outils à ClaudeAutomatiser des événements du cycle de vie
InvocationClaude appelle l’outil quand il le juge utileAutomatique, déterministe
Usage typiqueIntégration API externe, accès base de donnéesValidation sécurité, logging, notification
Configurationmcp_servers dans settingshooks dans settings

Un hook ne peut pas créer un nouvel outil pour Claude. Un serveur MCP ne peut pas intercepter un PreToolUse.

Stdout : seulement deux événements injectent du contexte

C’est la confusion la plus fréquente. Seuls SessionStart et UserPromptSubmit transmettent le stdout du hook dans le contexte de Claude. Pour tous les autres événements, le stdout est parsé comme JSON de décision — il n’est pas lu par le modèle.

Nos tests montrent que le stdout de SessionStart est visible par Claude jusqu’à au moins 509 KB sans troncation, avec un ralentissement perceptible au démarrage à ce volume. La borne supérieure reste non vérifiée. Nos observations indiquent que Claude décrit ce contenu comme un “system-reminder”, mais le mécanisme d’injection exact (position par rapport aux CLAUDE.md, format de wrapping) n’est pas documenté officiellement.

Priorité des deny rules sur les hooks PermissionRequest

Un hook PermissionRequest avec "behavior": "allow" ne peut pas contourner les règles deny définies dans les managed policies. Les deny rules ont toujours la priorité. Ce comportement a été clarifié à partir de la v2.1.77 (une version antérieure permettait le contournement — bug corrigé).

Timeout SessionEnd : 1.5s par défaut

Le hook SessionEnd a un timeout de 1.5 secondes par défaut. Il est configurable via la variable d’environnement CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS (ajouté en v2.1.78). Nos tests montrent qu’écrire 50 lignes dans un fichier prend environ 3ms — largement dans le timeout. Les opérations réseau ou les scripts lourds doivent être lancés en mode async ou évités sur cet événement.

SessionEnd et /exit : attention au matcher

Le matcher other sur SessionEnd ne se déclenche pas lors d’un /exit dans le terminal. C’est le matcher prompt_input_exit qui couvre ce cas. Nos tests ont confirmé ce comportement.


Ce qu’il faut retenir

  • Les hooks sont déterministes : Claude ne peut pas les ignorer, contrairement aux instructions d’un CLAUDE.md.
  • 22 événements couvrent l’ensemble du cycle de vie : session, outils, agents, MCP, worktrees.
  • Seuls SessionStart et UserPromptSubmit injectent le stdout du hook dans le contexte de Claude.
  • claudeMdExcludes et les hooks sont deux mécanismes complémentaires pour contrôler le comportement de Claude Code.
  • La configuration dans ~/.claude/settings.json s’applique globalement ; .claude/settings.json est projet-spécifique et committable.

Sources