Retour aux articles
11 MIN READ

Claude Code Hooks : Automatisez votre workflow de développement

By Learnia Team

Claude Code Hooks : Automatisez votre workflow de développement

Les hooks interceptent les actions de Claude Code avant ou après leur exécution. Ils vous permettent d'injecter une logique personnalisée — validation, journalisation, notifications, transformations — dans le workflow de Claude. Considérez les hooks comme un middleware pour votre assistant IA.


Que sont les hooks ?

Les hooks sont des scripts qui s'exécutent à des points spécifiques du cycle de vie de Claude Code :

Flux d'exécution des hooks :

  1. Requête utilisateur → Vous demandez à Claude de faire quelque chose
  2. Hook PreToolUse → Votre script s'exécute en premier (peut bloquer, modifier ou continuer)
  3. Exécution de l'outil → Claude exécute l'action
  4. Hook PostToolUse → Votre script s'exécute après (peut journaliser, notifier ou transformer)
  5. Réponse à l'utilisateur → Vous voyez le résultat

Types de hooks

HookMoment d'exécutionObjectif
PreToolUseAvant l'exécution de tout outilValider, bloquer ou modifier
PostToolUseAprès la fin d'un outilJournaliser, notifier, transformer la sortie
NotificationLors d'événements spécifiquesAlerter sur les erreurs, les achèvements
SessionStartAu début d'une sessionInitialiser l'environnement
SessionStopÀ la fin d'une sessionNettoyer, résumer

Learn AI — From Prompts to Agents

10 Free Interactive Guides120+ Hands-On Exercises100% Free

Pourquoi utiliser les hooks ?

Sans hooks

Vous vérifiez manuellement chaque action :

Claude wants to run: rm -rf ./temp

[y] Accept  [n] Reject

À. Chaque. Fois.

Avec les hooks

L'automatisation gère les vérifications de routine :

Claude wants to run: rm -rf ./temp
[PreToolUse] ✓ Validated: temp directory only
Proceeding automatically...

Claude wants to run: rm -rf /
[PreToolUse] ✗ Blocked: root directory access denied

Les commandes sûres passent ; les dangereuses sont bloquées automatiquement.


Configuration des hooks

Les hooks sont configurés dans ~/.claude/settings.json ou .claude/settings.json :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "command": "python .claude/hooks/validate-bash.py"
      }
    ],
    "PostToolUse": [
      {
        "matcher": "*",
        "command": "python .claude/hooks/log-actions.py"
      }
    ]
  }
}

Propriétés de l'objet hook

PropriétéDescription
matcherPattern d'outil à matcher (Bash, Write, * pour tous)
commandScript à exécuter
timeoutTemps d'exécution maximum (ms)
onErrorAction en cas d'échec : proceed ou block

Hooks PreToolUse

Les hooks PreToolUse s'exécutent avant que Claude exécute un outil. Ils peuvent :

  • Continuer : Autoriser l'action
  • Bloquer : Refuser l'action
  • Modifier : Changer les paramètres

Format d'entrée

Votre hook reçoit du JSON sur stdin :

{
  "hook_type": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm install lodash"
  },
  "session_id": "abc123",
  "timestamp": "2026-01-12T10:30:00Z"
}

Format de sortie

Retournez du JSON sur stdout :

{
  "decision": "proceed"
}

Options de décision :

DécisionComportement
proceedAutoriser l'exécution de l'outil
blockRefuser l'exécution
modifyModifier l'entrée de l'outil

Exemple de blocage

# .claude/hooks/validate-bash.py
import json
import sys

input_data = json.loads(sys.stdin.read())
command = input_data.get("tool_input", {}).get("command", "")

# Block dangerous commands
dangerous_patterns = [
    "rm -rf /",
    "rm -rf ~",
    "sudo rm",
    "> /dev/sda",
    "mkfs",
    "dd if="
]

for pattern in dangerous_patterns:
    if pattern in command:
        result = {
            "decision": "block",
            "reason": f"Blocked dangerous command containing: {pattern}"
        }
        print(json.dumps(result))
        sys.exit(0)

# Allow everything else
print(json.dumps({"decision": "proceed"}))

Exemple de modification

# .claude/hooks/add-dry-run.py
import json
import sys

input_data = json.loads(sys.stdin.read())
command = input_data.get("tool_input", {}).get("command", "")

# Add --dry-run to destructive commands
if command.startswith("kubectl delete"):
    result = {
        "decision": "modify",
        "tool_input": {
            "command": command + " --dry-run=client"
        },
        "reason": "Added --dry-run for safety"
    }
else:
    result = {"decision": "proceed"}

print(json.dumps(result))

Hooks PostToolUse

Les hooks PostToolUse s'exécutent après la fin d'un outil. Ils reçoivent la sortie de l'outil et peuvent :

  • Journaliser les actions
  • Envoyer des notifications
  • Transformer la sortie
  • Déclencher des actions de suivi

Format d'entrée

{
  "hook_type": "PostToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  },
  "tool_output": {
    "stdout": "All tests passed",
    "stderr": "",
    "exit_code": 0
  },
  "session_id": "abc123",
  "timestamp": "2026-01-12T10:30:00Z"
}

Exemple de journalisation

# .claude/hooks/log-actions.py
import json
import sys
from datetime import datetime

input_data = json.loads(sys.stdin.read())

log_entry = {
    "timestamp": datetime.now().isoformat(),
    "tool": input_data.get("tool_name"),
    "input": input_data.get("tool_input"),
    "output_preview": str(input_data.get("tool_output", {}))[:200]
}

with open(".claude/logs/actions.jsonl", "a") as f:
    f.write(json.dumps(log_entry) + "\n")

# PostToolUse hooks should return empty or minimal response
print(json.dumps({"status": "logged"}))

Exemple de notification

# .claude/hooks/notify-errors.py
import json
import sys
import requests

input_data = json.loads(sys.stdin.read())
tool_output = input_data.get("tool_output", {})

# Check for errors
exit_code = tool_output.get("exit_code", 0)
stderr = tool_output.get("stderr", "")

if exit_code != 0 or stderr:
    # Send to Slack
    webhook_url = "https://hooks.slack.com/services/YOUR/WEBHOOK/URL"
    requests.post(webhook_url, json={
        "text": f"⚠️ Claude Code error:\n```{stderr}```"
    })

print(json.dumps({"status": "processed"}))

Hooks Notification

Les hooks Notification répondent aux événements système plutôt qu'à l'utilisation des outils :

{
  "hooks": {
    "Notification": [
      {
        "event": "task_complete",
        "command": "python .claude/hooks/notify-complete.py"
      },
      {
        "event": "error",
        "command": "python .claude/hooks/notify-error.py"
      }
    ]
  }
}

Types d'événements

ÉvénementDescription
task_completeClaude a terminé une tâche
errorUne erreur s'est produite
context_limitApproche de la limite de contexte
cost_thresholdLe coût a dépassé le seuil

Notification de fin de tâche

# .claude/hooks/notify-complete.py
import json
import sys
import os

input_data = json.loads(sys.stdin.read())

# macOS notification
os.system(f"""osascript -e 'display notification "Task complete" with title "Claude Code"'""")

# Or desktop-notifier for cross-platform
# from desktop_notifier import DesktopNotifier
# notifier = DesktopNotifier()
# await notifier.send(title="Claude Code", message="Task complete")

print(json.dumps({"status": "notified"}))

Hooks de session

SessionStart

S'exécute au démarrage de Claude Code :

{
  "hooks": {
    "SessionStart": [
      {
        "command": "python .claude/hooks/session-start.py"
      }
    ]
  }
}

Cas d'utilisation :

  • Définir des variables d'environnement
  • Vérifier les prérequis
  • Charger le contexte de session
# .claude/hooks/session-start.py
import json
import sys
import os

# Check required tools
required = ["node", "npm", "git"]
missing = [cmd for cmd in required if os.system(f"which {cmd} > /dev/null") != 0]

if missing:
    result = {
        "warning": f"Missing tools: {', '.join(missing)}"
    }
else:
    result = {"status": "ready"}

print(json.dumps(result))

SessionStop

S'exécute à la fermeture de Claude Code :

# .claude/hooks/session-stop.py
import json
import sys

input_data = json.loads(sys.stdin.read())

# Generate session summary
summary = {
    "session_id": input_data.get("session_id"),
    "duration": input_data.get("duration_seconds"),
    "tools_used": input_data.get("tool_count"),
    "tokens_used": input_data.get("token_count")
}

with open(".claude/logs/sessions.jsonl", "a") as f:
    f.write(json.dumps(summary) + "\n")

print(json.dumps({"status": "logged"}))

Patterns de matcher

Les matchers déterminent quels outils déclenchent un hook :

PatternCorrespond à
BashToutes les commandes Bash
Bash(npm:*)Commandes npm uniquement
Bash(git:*)Commandes git uniquement
WriteToutes les écritures de fichiers
Write(src/**)Écritures dans le dossier src
EditToutes les modifications de fichiers
*Tous les outils
Read,Write,EditPlusieurs outils

Matchers multiples

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash(rm:*),Bash(git:push --force*)",
        "command": "python .claude/hooks/confirm-dangerous.py"
      }
    ]
  }
}

Recettes de hooks réelles

Recette 1 : Liste blanche de commandes

N'autoriser que des commandes spécifiques :

# .claude/hooks/allowlist.py
import json
import sys

input_data = json.loads(sys.stdin.read())
command = input_data.get("tool_input", {}).get("command", "")

ALLOWED_PREFIXES = [
    "npm ",
    "yarn ",
    "git ",
    "node ",
    "python ",
    "pytest ",
    "echo ",
    "cat ",
    "ls ",
    "pwd"
]

allowed = any(command.startswith(prefix) for prefix in ALLOWED_PREFIXES)

if allowed:
    print(json.dumps({"decision": "proceed"}))
else:
    print(json.dumps({
        "decision": "block",
        "reason": f"Command not in allowlist: {command.split()[0]}"
    }))

Recette 2 : Journal d'audit

Créer une piste d'audit détaillée :

# .claude/hooks/audit-log.py
import json
import sys
import os
from datetime import datetime

input_data = json.loads(sys.stdin.read())

audit_entry = {
    "timestamp": datetime.now().isoformat(),
    "user": os.environ.get("USER"),
    "session": input_data.get("session_id"),
    "tool": input_data.get("tool_name"),
    "action": input_data.get("tool_input"),
    "hook_type": input_data.get("hook_type")
}

log_file = os.path.expanduser("~/.claude/logs/audit.jsonl")
os.makedirs(os.path.dirname(log_file), exist_ok=True)

with open(log_file, "a") as f:
    f.write(json.dumps(audit_entry) + "\n")

print(json.dumps({"decision": "proceed"}))

Recette 3 : Notifications Slack

Alerter l'équipe lors d'événements spécifiques :

# .claude/hooks/slack-notify.py
import json
import sys
import os
import requests

input_data = json.loads(sys.stdin.read())
tool_name = input_data.get("tool_name")
tool_input = input_data.get("tool_input", {})

WEBHOOK_URL = os.environ.get("SLACK_WEBHOOK_URL")

# Notify on deployments
if tool_name == "Bash":
    command = tool_input.get("command", "")
    if "deploy" in command or "kubectl apply" in command:
        requests.post(WEBHOOK_URL, json={
            "text": f"🚀 Deployment initiated:\n```{command}```"
        })

print(json.dumps({"decision": "proceed"}))

Recette 4 : Sécurité Git

Empêcher les opérations git dangereuses :

# .claude/hooks/git-safety.py
import json
import sys

input_data = json.loads(sys.stdin.read())
command = input_data.get("tool_input", {}).get("command", "")

if not command.startswith("git "):
    print(json.dumps({"decision": "proceed"}))
    sys.exit(0)

BLOCKED_PATTERNS = [
    "git push --force",
    "git push -f",
    "git reset --hard",
    "git clean -fd",
    "git checkout -- .",  # discard all changes
]

PROTECTED_BRANCHES = ["main", "master", "production"]

for pattern in BLOCKED_PATTERNS:
    if pattern in command:
        print(json.dumps({
            "decision": "block",
            "reason": f"Dangerous git operation: {pattern}"
        }))
        sys.exit(0)

# Check protected branches
for branch in PROTECTED_BRANCHES:
    if f"push origin {branch}" in command or f"push -f origin {branch}" in command:
        print(json.dumps({
            "decision": "block",
            "reason": f"Direct push to protected branch: {branch}"
        }))
        sys.exit(0)

print(json.dumps({"decision": "proceed"}))

Recette 5 : Vérification des tests

Exécuter les tests avant d'autoriser les merges :

# .claude/hooks/require-tests.py
import json
import sys
import subprocess

input_data = json.loads(sys.stdin.read())
command = input_data.get("tool_input", {}).get("command", "")

# Check if this is a merge operation
if "git merge" in command or "git push" in command:
    # Run tests first
    result = subprocess.run(
        ["npm", "test"],
        capture_output=True,
        text=True
    )
    
    if result.returncode != 0:
        print(json.dumps({
            "decision": "block",
            "reason": f"Tests must pass before merge/push:\n{result.stderr}"
        }))
        sys.exit(0)

print(json.dumps({"decision": "proceed"}))

Débogage des hooks

Activer le mode verbeux

claude --verbose

Affiche l'exécution des hooks :

[HOOK] PreToolUse: Running validate-bash.py
[HOOK] Input: {"tool_name": "Bash", "tool_input": {"command": "npm test"}}
[HOOK] Output: {"decision": "proceed"}
[HOOK] Duration: 45ms

Tester les hooks manuellement

echo '{"tool_name": "Bash", "tool_input": {"command": "rm -rf /"}}' | python .claude/hooks/validate-bash.py

Problèmes courants

Le hook ne s'exécute pas :

  • Vérifiez le chemin du fichier dans les paramètres
  • Vérifiez que le pattern matcher correspond à l'outil
  • Assurez-vous que le script est exécutable

Le hook expire :

  • Augmentez le timeout dans la configuration
  • Optimisez les performances du script
  • Utilisez l'asynchrone pour les opérations lentes

Erreurs de parsing JSON :

  • Validez la sortie JSON de votre script
  • Vérifiez qu'il n'y a pas d'instructions print supplémentaires
  • Assurez-vous d'une séparation propre stdout/stderr

Hooks + autres fonctionnalités

Hooks + commandes personnalisées

Les commandes peuvent déclencher des hooks :

<!-- .claude/commands/safe-deploy.md -->
Deploy to production. Hooks will:
- Validate deployment criteria
- Log the deployment
- Notify the team

Les appels Bash de la commande déclenchent automatiquement vos hooks.

Hooks + permissions

Les hooks ajoutent une couche supplémentaire aux permissions :

Requête : git push --force origin main

1. Vérification des permissions : Bash(git:*) est-il autorisé ? → OUI
2. Hook PreToolUse : Est-ce sûr ? → BLOQUÉ (branche protégée)

Voir Claude Code Permissions : modes Deny, Allow et Ask expliqués.

Hooks + MCP

Les appels d'outils MCP déclenchent aussi les hooks :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__github__*",
        "command": "python .claude/hooks/validate-github.py"
      }
    ]
  }
}

Voir Model Context Protocol (MCP) pour Claude Code : guide complet.


Considérations de performance

  1. Gardez les hooks rapides : Visez moins de 100 ms d'exécution
  2. Asynchrone pour les opérations lentes : Ne bloquez pas sur les appels réseau dans PreToolUse
  3. Mettez en cache si possible : Stockez les valeurs calculées entre les appels
  4. Utilisez des matchers spécifiques : Bash(git:*) est plus rapide que *
  5. Journalisation par lots : Écrivez les logs par lots, pas par événement

Points clés à retenir

  1. PreToolUse pour la validation : Bloquez ou modifiez les actions dangereuses avant leur exécution.

  2. PostToolUse pour l'observation : Journalisez, notifiez et analysez sans bloquer.

  3. Matchers pour la précision : Ciblez des outils spécifiques pour minimiser la surcharge.

  4. JSON en entrée, JSON en sortie : Les hooks communiquent via un protocole JSON simple.

  5. Combinez avec les permissions : Les hooks complètent le système de permissions.


Construisez des agents IA autonomes

Les hooks sont un élément de la construction d'agents autonomes fiables. Apprenez les principes plus larges de l'orchestration d'agents.

Dans notre Module 6 — Agents autonomes, vous apprendrez :

  • Les patterns d'architecture d'agents
  • L'orchestration et les boucles de contrôle
  • La gestion des erreurs et la récupération
  • La construction de workflows IA fiables

Explorer le Module 6 : Agents autonomes

GO DEEPER — FREE GUIDE

Module 6 — AI Agents & ReAct

Create autonomous agents that reason and take actions.

Newsletter

Weekly AI Insights

Tools, techniques & news — curated for AI practitioners. Free, no spam.

Free, no spam. Unsubscribe anytime.

FAQ

Que sont les hooks de Claude Code ?+

Les hooks sont des scripts qui s'exécutent à des points spécifiques du workflow de Claude Code — avant les actions (PreToolUse), après les actions (PostToolUse), ou lors d'événements (Notification). Ils ajoutent une logique personnalisée comme la validation ou la journalisation.

Comment créer un hook dans Claude Code ?+

Définissez les hooks dans .claude/settings.json sous la clé 'hooks'. Spécifiez le type de hook, le pattern matcher et la commande à exécuter. Les hooks peuvent être des scripts shell ou tout exécutable.

Les hooks peuvent-ils bloquer les actions de Claude ?+

Oui. Les hooks PreToolUse peuvent retourner un code de sortie non nul pour bloquer une action, ou produire du JSON pour modifier le comportement de Claude. Cela permet des portes de validation avant les opérations destructrices.

Quelle est la différence entre les hooks et les permissions ?+

Les permissions contrôlent ce que Claude PEUT faire (autoriser/refuser). Les hooks exécutent du code personnalisé QUAND Claude fait quelque chose. Utilisez les permissions pour le contrôle d'accès, les hooks pour la logique personnalisée et l'automatisation.