Vous utilisez Claude Code tous les jours. Il écrit du bon code, refactorise, génère des tests. Mais entre deux tâches, qui vérifie que le linter passe ? Que les tests tournent encore ? Qu’aucune clé API ne s’est glissée dans un commit ?
Personne. Ou plutôt : vous, quand vous y pensez. Et vous n’y pensez pas toujours.
Les hooks résolvent ce problème. Ce sont des commandes shell qui s’exécutent automatiquement à des moments précis du cycle de vie de Claude Code. Pas besoin d’y penser : le filet de sécurité est permanent.
Qu’est-ce qu’un hook ?
Un hook est une commande qui se déclenche quand Claude Code effectue une action. Quatre événements sont disponibles :
PreToolUse: avant que Claude exécute un outil (Bash, Edit, Write…)PostToolUse: après l’exécution d’un outilNotification: quand Claude envoie une notificationStop: quand Claude termine un tour de conversation
La configuration se fait dans .claude/settings.json (scope projet, commité dans le repo) ou ~/.claude/settings.json (scope global, personnel). Voici la structure JSON :
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "your-script.sh"
}
]
}
]
}
}
Le hook reçoit le contexte via stdin : un JSON contenant le nom de l’outil, les paramètres d’entrée et les informations de session. Le code de sortie détermine le comportement : exit 0 = continuer, exit 2 = bloquer l’action. Tout ce qui est écrit sur stderr est renvoyé à Claude comme feedback. Il peut alors corriger son action.
5 hooks concrets
Hook 1 : Bloquer les commandes dangereuses
Un rm -rf / ou un git push --force sur main, ça arrive. Surtout quand c’est un agent autonome qui tape les commandes. Ce hook intercepte les commandes Bash avant exécution et bloque les patterns dangereux.
Événement : PreToolUse | Matcher : Bash
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 -c \"\nimport sys, json\ndata = json.load(sys.stdin)\ncmd = data.get('tool_input', {}).get('command', '')\nblocked = ['rm -rf /', 'git push --force', 'git push -f', 'DROP TABLE', 'DROP DATABASE', ':(){:|:&};:']\nfor b in blocked:\n if b in cmd:\n print(f'BLOCKED: commande dangereuse détectée ({b})', file=sys.stderr)\n sys.exit(2)\n\""
}
]
}
]
}
}
Si la commande contient un pattern bloqué, le script retourne exit 2. Claude reçoit le message d’erreur et reformule sa commande.
Hook 2 : Linter automatique après chaque édition
Chaque fois que Claude modifie un fichier, ESLint tourne automatiquement dessus. Si le linter trouve des erreurs, Claude les voit dans stderr et corrige immédiatement.
Événement : PostToolUse | Matcher : Edit
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(echo $CLAUDE_FILE_PATH); if [[ \"$FILE\" == *.js || \"$FILE\" == *.ts || \"$FILE\" == *.tsx ]]; then npx eslint --fix \"$FILE\" 2>&1; fi'"
}
]
}
]
}
}
Adaptez la commande à votre stack : ruff check pour Python, rubocop -a pour Ruby, gofmt pour Go. Le principe reste le même.
Hook 3 : Vérifier les secrets avant commit
Ce hook intercepte toute commande git commit et scanne les fichiers staged à la recherche de patterns suspects : clés API, tokens, mots de passe en dur.
Événement : PreToolUse | Matcher : Bash
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "python3 -c \"\nimport sys, json, subprocess, re\ndata = json.load(sys.stdin)\ncmd = data.get('tool_input', {}).get('command', '')\nif 'git commit' not in cmd:\n sys.exit(0)\nresult = subprocess.run(['git', 'diff', '--cached', '--diff-filter=ACM'], capture_output=True, text=True)\npatterns = [r'AKIA[0-9A-Z]{16}', r'sk-[a-zA-Z0-9]{20,}', r'ghp_[a-zA-Z0-9]{36}', r'password\\s*=\\s*[\\\"\\'][^\\\"\\']+']\nfor p in patterns:\n matches = re.findall(p, result.stdout)\n if matches:\n print(f'BLOCKED: secret potentiel détecté dans les fichiers staged: {matches[0][:20]}...', file=sys.stderr)\n sys.exit(2)\n\""
}
]
}
]
}
}
Ce n’est pas un remplacement de gitleaks ou trufflehog dans votre CI. C’est une première ligne de défense qui attrape les erreurs évidentes avant qu’elles ne quittent votre machine.
Hook 4 : Tests automatiques après modification
Quand Claude modifie un fichier source, ce hook cherche le fichier de test correspondant et le lance. Si les tests échouent, Claude reçoit le feedback et corrige.
Événement : PostToolUse | Matcher : Write
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "bash -c 'FILE=$(echo $CLAUDE_FILE_PATH); TEST_FILE=$(echo \"$FILE\" | sed \"s/\\.ts/.test.ts/\" | sed \"s/src\\//src\\/__tests__\\//\"); if [ -f \"$TEST_FILE\" ]; then npx jest \"$TEST_FILE\" --no-coverage 2>&1 | tail -20; fi'"
}
]
}
]
}
}
Le tail -20 est important : on ne veut pas noyer Claude sous 500 lignes de sortie Jest. Seul le résumé des échecs suffit pour qu’il comprenne et corrige.
Hook 5 : Notification Slack quand l’agent termine
Claude Code peut tourner longtemps en arrière-plan sur une tâche complexe. Ce hook envoie une notification Slack quand il a fini, avec un résumé de ce qui a été fait.
Événement : Stop
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "python3 -c \"\nimport sys, json, urllib.request\ndata = json.load(sys.stdin)\nstop_reason = data.get('stop_reason', 'unknown')\npayload = json.dumps({'text': f'Claude Code a terminé. Raison: {stop_reason}'}).encode()\nreq = urllib.request.Request('YOUR_SLACK_WEBHOOK_URL', data=payload, headers={'Content-Type': 'application/json'})\nurllib.request.urlopen(req)\n\""
}
]
}
]
}
}
Remplacez YOUR_SLACK_WEBHOOK_URL par votre webhook Slack. Vous pouvez enrichir le message avec le nombre de fichiers modifiés, le dernier commit, ou tout autre contexte utile.
Scope projet vs global
Deux emplacements, deux usages :
.claude/settings.json (dans le repo) : hooks spécifiques au projet. Commités dans Git, partagés avec l’équipe. Le linter du projet, les conventions de test, les contraintes d’architecture.
~/.claude/settings.json (home) : hooks personnels, appliqués à tous vos projets. Les garde-fous de sécurité (blocage de commandes dangereuses, détection de secrets), les notifications Slack.
Dans nos projets, la règle est simple : les hooks de sécurité sont globaux, les hooks de qualité sont dans le repo. Un nouveau développeur qui clone le projet hérite automatiquement des conventions de l’équipe. Et ses hooks personnels de sécurité restent actifs quel que soit le projet.
Limites et bonnes pratiques
Les hooks sont synchrones et bloquants. Un hook lent ralentit chaque action de Claude Code. Gardez-les rapides : un linter sur un fichier, pas une suite de tests complète. Réservez les checks lourds au PostToolUse ou au Stop. C’est d’ailleurs l’un des avantages de Claude Code face à ses concurrents. Notre comparatif des agents CLI 2026 détaille les différences de personnalisation entre les outils.
Ne remplacez pas votre CI/CD. Les hooks sont une première ligne de défense locale. Ils attrapent les erreurs évidentes avant qu’elles ne quittent votre machine. Votre pipeline CI reste indispensable pour les tests d’intégration, les analyses de sécurité approfondies et le déploiement.
Testez vos hooks manuellement. Avant de les activer, lancez le script à la main avec un JSON d’entrée simulé. Un hook qui crash silencieusement est pire que pas de hook du tout.
Commencez petit. Deux hooks suffisent pour démarrer : un blocage de commandes dangereuses (global) et un linter automatique (projet). Ajoutez-en au fur et à mesure des besoins réels, pas des besoins imaginés.
Sources
Vous déployez Claude Code dans votre équipe ? Formez vos développeurs aux agents IA →