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 :
- →Requête utilisateur → Vous demandez à Claude de faire quelque chose
- →Hook PreToolUse → Votre script s'exécute en premier (peut bloquer, modifier ou continuer)
- →Exécution de l'outil → Claude exécute l'action
- →Hook PostToolUse → Votre script s'exécute après (peut journaliser, notifier ou transformer)
- →Réponse à l'utilisateur → Vous voyez le résultat
Types de hooks
| Hook | Moment d'exécution | Objectif |
|---|---|---|
PreToolUse | Avant l'exécution de tout outil | Valider, bloquer ou modifier |
PostToolUse | Après la fin d'un outil | Journaliser, notifier, transformer la sortie |
Notification | Lors d'événements spécifiques | Alerter sur les erreurs, les achèvements |
SessionStart | Au début d'une session | Initialiser l'environnement |
SessionStop | À la fin d'une session | Nettoyer, résumer |
Learn AI — From Prompts to Agents
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 |
|---|---|
matcher | Pattern d'outil à matcher (Bash, Write, * pour tous) |
command | Script à exécuter |
timeout | Temps d'exécution maximum (ms) |
onError | Action 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écision | Comportement |
|---|---|
proceed | Autoriser l'exécution de l'outil |
block | Refuser l'exécution |
modify | Modifier 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énement | Description |
|---|---|
task_complete | Claude a terminé une tâche |
error | Une erreur s'est produite |
context_limit | Approche de la limite de contexte |
cost_threshold | Le 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 :
| Pattern | Correspond à |
|---|---|
Bash | Toutes les commandes Bash |
Bash(npm:*) | Commandes npm uniquement |
Bash(git:*) | Commandes git uniquement |
Write | Toutes les écritures de fichiers |
Write(src/**) | Écritures dans le dossier src |
Edit | Toutes les modifications de fichiers |
* | Tous les outils |
Read,Write,Edit | Plusieurs 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
timeoutdans 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
- →Gardez les hooks rapides : Visez moins de 100 ms d'exécution
- →Asynchrone pour les opérations lentes : Ne bloquez pas sur les appels réseau dans PreToolUse
- →Mettez en cache si possible : Stockez les valeurs calculées entre les appels
- →Utilisez des matchers spécifiques :
Bash(git:*)est plus rapide que* - →Journalisation par lots : Écrivez les logs par lots, pas par événement
Points clés à retenir
- →
PreToolUse pour la validation : Bloquez ou modifiez les actions dangereuses avant leur exécution.
- →
PostToolUse pour l'observation : Journalisez, notifiez et analysez sans bloquer.
- →
Matchers pour la précision : Ciblez des outils spécifiques pour minimiser la surcharge.
- →
JSON en entrée, JSON en sortie : Les hooks communiquent via un protocole JSON simple.
- →
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
Module 6 — AI Agents & ReAct
Create autonomous agents that reason and take actions.
Weekly AI Insights
Tools, techniques & news — curated for AI practitioners. Free, no spam.
Free, no spam. Unsubscribe anytime.
→Related Articles
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.