Intermédiaire 10 min

Claude Code Hooks : automatiser votre workflow

Guide complet sur les hooks de Claude Code. Déclenchez des scripts automatiquement avant ou après les actions de Claude pour sécuriser et optimiser votre workflow.

hooks automatisation workflow configuration

Les hooks, c’est quoi ?

Les hooks sont des commandes shell qui se déclenchent automatiquement à des moments précis du cycle de vie de Claude Code. Avant qu’il modifie un fichier, après qu’il exécute une commande, quand il termine une tâche : vous définissez des règles, Claude Code les exécute sans intervention.

Concrètement, c’est un système d’événements. Vous configurez des scripts qui réagissent aux actions de Claude Code. Quelques exemples de ce que ça permet :

  • Lancer Prettier automatiquement après chaque modification de fichier
  • Bloquer toute tentative de rm -rf / ou de git push --force
  • Exécuter les tests après chaque changement de code
  • Envoyer une notification Slack quand Claude Code termine une tâche longue
  • Interdire la modification de certains fichiers sensibles

Les hooks transforment Claude Code d’un outil interactif en un workflow automatisé avec des garde-fous.

Types d’événements

Chaque hook se déclenche sur un événement précis. Voici les événements disponibles :

PreToolUse

Se déclenche avant que Claude Code utilise un outil. C’est le point d’interception le plus puissant : vous pouvez inspecter ce que Claude s’apprête à faire et décider de le bloquer ou de le laisser passer.

Cas d’usage :

  • Bloquer l’écriture dans des fichiers protégés
  • Empêcher l’exécution de commandes dangereuses
  • Valider le contenu avant écriture

PostToolUse

Se déclenche après que Claude Code a utilisé un outil avec succès. Idéal pour les actions de post-traitement.

Cas d’usage :

  • Formater le code après modification (Prettier, ESLint, Black)
  • Lancer les tests après un changement
  • Mettre à jour un index ou un cache

Notification

Se déclenche quand Claude Code envoie une notification (typiquement quand il a besoin d’une action de votre part ou qu’il vous informe de quelque chose).

Cas d’usage :

  • Rediriger les notifications vers Slack, Discord, ou un webhook
  • Logger les notifications dans un fichier
  • Déclencher des alertes système

Stop

Se déclenche quand Claude Code termine son tour de réponse principal (la boucle agentique s’arrête).

Cas d’usage :

  • Récapituler les changements effectués
  • Lancer une suite de tests finale
  • Envoyer un résumé par email ou Slack
  • Auto-commit des changements

SubagentStop

Se déclenche quand un sous-agent (lancé via la commande /agent ou les tâches parallèles) termine son exécution. Même logique que Stop, mais pour les sous-agents.

Configuration

Les hooks se configurent en JSON, dans les fichiers de settings de Claude Code.

Où placer la configuration

Trois niveaux sont possibles :

FichierPortéeUsage
.claude/settings.jsonProjet (versionné)Hooks partagés avec l’équipe
~/.claude/settings.jsonUtilisateur (global)Hooks personnels, actifs partout
.claude/settings.local.jsonProjet (non versionné)Hooks locaux au projet, personnels

Structure JSON

La configuration suit cette structure :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Fichier sur le point d être modifié'",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}

Chaque entrée dans un type d’événement contient :

  • matcher : filtre optionnel pour cibler des outils ou des patterns spécifiques (détaillé plus loin)
  • hooks : tableau de hooks à exécuter
    • type : toujours "command" pour l’instant
    • command : la commande shell à exécuter
    • timeout : timeout en millisecondes (par défaut : 60000, soit 60 secondes)

Matchers : cibler les bons événements

Le champ matcher permet de filtrer précisément quand un hook se déclenche. Si vous omettez le matcher, le hook se déclenche pour tous les appels de l’événement correspondant.

Matcher sur le nom de l’outil

Pour PreToolUse et PostToolUse, le matcher correspond au nom de l’outil Claude Code :

{
  "matcher": "Write"
}

Les noms d’outils disponibles :

  • Write : écriture de fichier
  • Edit : modification de fichier (remplacement de texte)
  • Bash : exécution de commande shell
  • Read : lecture de fichier
  • Glob : recherche de fichiers par pattern
  • Grep : recherche dans le contenu des fichiers
  • WebFetch : requête HTTP
  • WebSearch : recherche web
  • NotebookEdit : modification de notebook Jupyter

Matcher avec regex

Le matcher supporte les expressions régulières. Par exemple, pour cibler toutes les écritures et modifications :

{
  "matcher": "Write|Edit"
}

Ou pour cibler les outils MCP d’un serveur spécifique :

{
  "matcher": "mcp__notion__.*"
}

Exemples pratiques

Auto-formater le code après chaque modification

Le hook le plus courant. Après chaque écriture ou modification de fichier, on lance le formateur :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}

La variable $CLAUDE_FILE_PATH contient le chemin du fichier concerné. Le || true évite qu’une erreur de formatage ne bloque Claude Code.

Pour ESLint avec auto-fix :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "npx eslint --fix \"$CLAUDE_FILE_PATH\" 2>/dev/null || true",
            "timeout": 15000
          }
        ]
      }
    ]
  }
}

Bloquer la modification de fichiers protégés

Empêcher Claude Code de toucher à certains fichiers critiques :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '(\\.env|package-lock\\.json|yarn\\.lock|pnpm-lock\\.yaml)$'; then echo 'BLOCK: Ce fichier est protégé et ne doit pas être modifié par Claude Code' >&2; exit 2; fi",
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

Le mot-clé BLOCK dans la sortie stderr combiné au code de sortie 2 indique à Claude Code de ne pas exécuter l’action.

Bloquer les commandes dangereuses

Intercepter les commandes Bash risquées avant leur exécution :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_COMMAND\" | grep -qE '(rm\\s+-rf\\s+/|git\\s+push\\s+--force|git\\s+push\\s+-f|git\\s+reset\\s+--hard)'; then echo 'BLOCK: Commande dangereuse interceptée' >&2; exit 2; fi",
            "timeout": 5000
          }
        ]
      }
    ]
  }
}

Lancer les tests après un changement de code

Exécuter automatiquement les tests quand Claude modifie un fichier source :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '\\.(ts|tsx|js|jsx)$' && ! echo \"$CLAUDE_FILE_PATH\" | grep -q 'node_modules'; then npm test -- --bail --findRelatedTests \"$CLAUDE_FILE_PATH\" 2>&1 | tail -20; fi",
            "timeout": 30000
          }
        ]
      }
    ]
  }
}

Le --findRelatedTests de Jest ne lance que les tests liés au fichier modifié, ce qui garde le hook rapide.

Notification à la fin d’une tâche

Envoyer une notification quand Claude Code termine :

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "curl -s -X POST 'https://hooks.slack.com/services/XXX/YYY/ZZZ' -H 'Content-Type: application/json' -d '{\"text\": \"Claude Code a terminé sa tâche\"}' > /dev/null",
            "timeout": 10000
          }
        ]
      }
    ]
  }
}

Sur macOS, vous pouvez aussi utiliser une notification système :

{
  "type": "command",
  "command": "osascript -e 'display notification \"Tâche terminée\" with title \"Claude Code\"'",
  "timeout": 5000
}

Sur Linux :

{
  "type": "command",
  "command": "notify-send 'Claude Code' 'Tâche terminée'",
  "timeout": 5000
}

Auto-commit après des changements réussis

Commiter automatiquement les modifications quand Claude Code termine :

{
  "hooks": {
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && if [ -n \"$(git status --porcelain)\" ]; then git add -A && git commit -m 'auto: changements appliqués par Claude Code'; fi",
            "timeout": 15000
          }
        ]
      }
    ]
  }
}

Attention : utilisez ceci avec précaution. Combinez-le avec des hooks de protection sur les fichiers sensibles pour éviter de commiter des secrets.

Communication des hooks

Les hooks communiquent avec Claude Code via trois canaux :

Sortie standard (stdout)

Tout ce que votre hook écrit sur stdout est renvoyé à Claude Code comme contexte utilisateur. Claude voit cette sortie et peut en tenir compte dans ses prochaines actions.

# Le résultat des tests est visible par Claude
npm test 2>&1

C’est utile pour donner du feedback : si les tests échouent, Claude Code voit les erreurs et peut corriger.

Sortie d’erreur (stderr)

La sortie stderr est utilisée pour les messages de blocage. Quand un hook veut empêcher une action :

echo "BLOCK: Raison du blocage" >&2
exit 2

Codes de sortie

CodeSignification
0Succès, l’action continue
1Erreur non fatale, l’action continue mais l’erreur est signalée
2Blocage : l’action est annulée (surtout pour PreToolUse)

Le code de sortie 2 est le mécanisme principal pour bloquer une action dans un hook PreToolUse. Claude Code voit le message BLOCK et comprend pourquoi l’action a été refusée.

Variables d’environnement disponibles

Les hooks reçoivent du contexte via des variables d’environnement. Les variables disponibles dépendent de l’événement et de l’outil :

VariableDescriptionDisponible dans
CLAUDE_FILE_PATHChemin du fichier concernéWrite, Edit, Read
CLAUDE_COMMANDCommande shell à exécuterBash
CLAUDE_PROJECT_DIRRacine du projetTous les hooks
CLAUDE_TOOL_NAMENom de l’outil déclenchéTous les hooks PreToolUse/PostToolUse
CLAUDE_TOOL_INPUTInput JSON complet de l’outilTous les hooks PreToolUse/PostToolUse

Vous pouvez parser CLAUDE_TOOL_INPUT pour accéder à tous les paramètres de l’outil :

# Extraire le chemin du fichier depuis l'input JSON
FILE=$(echo "$CLAUDE_TOOL_INPUT" | jq -r '.file_path // empty')

Débuguer les hooks

Tester un hook manuellement

Avant de configurer un hook, testez-le dans votre terminal :

# Simuler les variables d'environnement
export CLAUDE_FILE_PATH="src/index.ts"
export CLAUDE_COMMAND="npm test"
export CLAUDE_PROJECT_DIR="/home/user/mon-projet"

# Exécuter le hook
bash -c 'if echo "$CLAUDE_FILE_PATH" | grep -qE "\\.env$"; then echo "BLOCK" >&2; exit 2; fi'
echo $?  # Doit afficher 0 (pas de blocage pour src/index.ts)

Mode verbose

Lancez Claude Code en mode verbose pour voir l’exécution des hooks :

claude --verbose

Vous verrez dans les logs :

  • Quel hook se déclenche
  • La commande exécutée
  • La sortie du hook
  • Le code de retour

Problèmes courants

Le hook ne se déclenche pas

  • Vérifiez que le matcher correspond bien au nom de l’outil (respectez la casse : Write, pas write)
  • Vérifiez que le fichier de settings est au bon endroit
  • Relancez Claude Code pour recharger la configuration

Le hook se déclenche mais échoue

  • Testez la commande manuellement dans votre terminal
  • Vérifiez que les outils utilisés (prettier, eslint, jq) sont installés et dans le PATH
  • Vérifiez le timeout : un hook trop lent sera tué

Le hook bloque tout

  • Vérifiez la logique de votre condition : un grep sans -q ou un exit 2 mal placé peut tout bloquer
  • Ajoutez du logging temporaire : echo "DEBUG: FILE=$CLAUDE_FILE_PATH" >> /tmp/hooks.log

Bonnes pratiques

Gardez les hooks rapides

Un hook lent ralentit tout le workflow. Visez moins de 5 secondes par hook. Pour les tâches longues (suite de tests complète, build), limitez-vous aux hooks Stop qui ne bloquent pas l’interaction.

Rendez les hooks idempotents

Un hook peut être exécuté plusieurs fois sur le même fichier. Assurez-vous qu’il produit le même résultat à chaque exécution. Prettier est naturellement idempotent (formater un fichier déjà formaté ne change rien). Un hook qui ajoute du contenu à un fichier ne l’est pas.

Gérez les erreurs gracieusement

Ajoutez || true ou 2>/dev/null quand l’échec du hook ne doit pas bloquer Claude Code. Un formateur qui échoue sur un fichier binaire ne devrait pas arrêter le workflow.

# Bon : l'échec est silencieux
npx prettier --write "$CLAUDE_FILE_PATH" 2>/dev/null || true

# Mauvais : un fichier non supporté crashe tout
npx prettier --write "$CLAUDE_FILE_PATH"

Sécurisez les hooks

Les hooks ont accès complet à votre système. Quelques règles :

  • Ne mettez pas de secrets dans les commandes (utilisez des variables d’environnement ou des fichiers)
  • Validez les entrées : $CLAUDE_FILE_PATH pourrait contenir des caractères spéciaux
  • Limitez les permissions : si un hook n’a pas besoin d’écrire, ne lui donnez pas accès en écriture
  • Quotez toujours les variables : "$CLAUDE_FILE_PATH" et non $CLAUDE_FILE_PATH

Versionnez les hooks d’équipe

Mettez les hooks partagés dans .claude/settings.json et versionnez-les dans Git. Chaque membre de l’équipe bénéficie des mêmes protections (fichiers interdits, formatage automatique, commandes bloquées).

Les hooks personnels (notifications, préférences de formatage) vont dans ~/.claude/settings.json ou .claude/settings.local.json.

Combinez les hooks

Les hooks sont plus puissants combinés. Un setup robuste pour un projet TypeScript :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '(\\.env|\\.lock)$'; then echo 'BLOCK: Fichier protégé' >&2; exit 2; fi",
            "timeout": 5000
          }
        ]
      },
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_COMMAND\" | grep -qE '(rm\\s+-rf|--force|--hard)'; then echo 'BLOCK: Commande dangereuse' >&2; exit 2; fi",
            "timeout": 5000
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "if echo \"$CLAUDE_FILE_PATH\" | grep -qE '\\.(ts|tsx|js|jsx|json|css|md)$'; then npx prettier --write \"$CLAUDE_FILE_PATH\" 2>/dev/null || true; fi",
            "timeout": 10000
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test -- --bail 2>&1 | tail -5",
            "timeout": 30000
          }
        ]
      }
    ]
  }
}

Ce setup protège les fichiers sensibles, bloque les commandes dangereuses, formate automatiquement et lance les tests en fin de session.