
Le rituel ruff + pytest repose sur votre vigilance : vous le lancez à la main, et un oubli casse la boucle. Les hooks Claude Code déclenchent des commandes shell autour des événements de la session (PreToolUse, PostToolUse, Stop, etc.). Vous pouvez exécuter ruff automatiquement après chaque Edit, bloquer rm -rf avant qu’il ne parte, ou notifier qu’une édition a laissé des erreurs de lint — sans dépendre de votre attention.
Ce que vous allez apprendre
Section intitulée « Ce que vous allez apprendre »- Configurer un hook
PostToolUsequi lanceruffaprès chaqueEdit/Write - Configurer un hook
PreToolUsequi bloque une commande destructive - Choisir le bon événement et le bon matcher
- Lire les codes de sortie pour informer, bloquer ou ignorer
Dans quel contexte utiliser cette fonctionnalité ?
Section intitulée « Dans quel contexte utiliser cette fonctionnalité ? »- Vous voulez un rituel déterministe, pas seulement conseillé
- Vous partagez un projet avec une équipe et voulez garantir que le lint tourne
- Vous voulez interdire systématiquement une commande (pas juste refuser en live)
- Vous voulez notifier un événement (ex : un test cassé) sans bloquer
Prérequis
Section intitulée « Prérequis ».claude/settings.jsondéjà en place (voir settings.json avancé)ruffetpytestinstallés et fonctionnels surlab-claude- À l’aise avec la syntaxe des permissions (voir settings.json avancé)
Les événements utiles au quotidien
Section intitulée « Les événements utiles au quotidien »La doc officielle liste beaucoup d’événements. Pour un démarrage pragmatique, concentrez-vous sur ceux-ci :
| Événement | Se déclenche | Exemple d’usage |
|---|---|---|
PreToolUse | Avant l’exécution d’un outil, peut bloquer | Interdire rm -rf, bloquer les curl non whitelistés |
PostToolUse | Après exécution réussie d’un outil | Lancer ruff check après Edit ou Write |
PostToolUseFailure | Après échec d’un outil | Loguer ou notifier un échec |
Stop | Quand Claude termine son tour | Lancer pytest -q en fin de session |
SessionStart | Au démarrage d’une session | Afficher l’état git courant |
Structure minimale dans settings.json
Section intitulée « Structure minimale dans settings.json »Les hooks vivent sous la clé hooks du settings.json. Chaque événement contient une liste d’entrées {matcher, hooks} :
{ "hooks": { "PostToolUse": [ { "matcher": "Edit|Write", "hooks": [ { "type": "command", "command": "uv run ruff check .", "timeout": 30 } ] } ] }}Explication rapide :
matcher: filtre sur le nom de l’outil (Edit,Write,Bash, etc.).Edit|Write= l’un ou l’autretype: "command": exécuter une commande shellcommand: la commande à lancertimeout: optionnel, en secondes
Application sur lab-claude
Section intitulée « Application sur lab-claude »Objectif : deux hooks utiles immédiatement — lint automatique après édition et blocage systématique des commandes destructives.
Hook 1 : ruff après chaque Edit ou Write
Section intitulée « Hook 1 : ruff après chaque Edit ou Write »-
Éditez
.claude/settings.jsonet ajoutez la sectionhooks{"$schema": "https://json.schemastore.org/claude-code-settings.json","permissions": {"allow": ["Bash(uv run ruff check *)", "Bash(uv run pytest *)"],"deny": ["Bash(rm -rf *)", "Read(./.env)"]},"hooks": {"PostToolUse": [{"matcher": "Edit|Write","hooks": [{"type": "command","command": "uv run ruff check .","timeout": 30}]}]}} -
Validez la syntaxe JSON
Fenêtre de terminal python3 -c "import json; json.load(open('.claude/settings.json'))" && echo "JSON valide" -
Testez dans une session
Faites-lui modifier un fichier Python. Après l’
Edit,ruff check .tourne automatiquement, et le résultat revient dans la conversation.
Hook 2 : bloquer rm -rf avant exécution
Section intitulée « Hook 2 : bloquer rm -rf avant exécution »Un deny dans permissions couvre déjà rm -rf, mais un hook PreToolUse permet d’ajouter un message explicite et de centraliser la politique :
{ "hooks": { "PreToolUse": [ { "matcher": "Bash", "hooks": [ { "type": "command", "command": "bash -c 'input=$(cat); echo \"$input\" | grep -qE \"rm -rf|rm -fr\" && { echo \"rm -rf bloque par hook\" >&2; exit 2; } || exit 0'" } ] } ] }}Ce qui se passe :
- Le hook reçoit l’entrée de l’outil sur
stdin(JSON) - S’il détecte
rm -rf, il sort avecexit 2et un message surstderr - Exit code 2 = Claude voit ça comme un blocage et ne lance pas la commande
Les codes de sortie à connaître
Section intitulée « Les codes de sortie à connaître »| Exit code | Effet |
|---|---|
0 | Succès, le tour continue normalement |
2 | Blocage explicite, stderr est renvoyé à Claude comme feedback |
| autre | Erreur non bloquante, signalée dans la session |
Règle simple :
- Vous voulez informer sans bloquer →
exit 0et écrire surstdout - Vous voulez bloquer avec un message →
exit 2et écrire surstderr
Matchers : filtrer finement
Section intitulée « Matchers : filtrer finement »| Matcher | Déclenche sur |
|---|---|
"*" ou omis | Tous les outils |
"Edit" | Uniquement l’outil Edit |
"Edit|Write" | Edit ou Write |
"Bash" | Toutes les commandes shell |
"^Notebook" | Regex : tous les outils commençant par Notebook |
Combinaison avec settings.json et skills
Section intitulée « Combinaison avec settings.json et skills »| Besoin | Bon endroit |
|---|---|
| Interdire complètement une commande | permissions.deny dans settings.json |
| Déclencher automatiquement autour d’un événement | Hook |
| Enchaîner une procédure invoquée à la main | Skill |
| Conseiller une convention de code | Rule ou CLAUDE.md |
Les hooks sont le seul mécanisme déterministe : CLAUDE.md, rules et skills sont du contexte, les hooks sont du code exécuté.
Erreurs fréquentes
Section intitulée « Erreurs fréquentes »| Symptôme | Cause probable | Correction |
|---|---|---|
| Le hook ne se déclenche pas | Mauvais event ou matcher trop strict | Tester avec "matcher": "*" puis resserrer |
timeout atteint | Commande trop lente (ex : pytest complet après chaque Edit) | Déclencher pytest sur Stop plutôt que sur chaque édition |
| Blocage non compris par Claude | Exit code autre que 2 | Utiliser exit 2 pour bloquer explicitement |
| Hook exécuté trop souvent | Matcher trop large | Préciser "Edit|Write" plutôt que "*" |
rm -rf passe quand même | Hook shell mal quoté | Vérifier que la commande utilise bien stdin et un grep correct |
Checklist de fin de guide
Section intitulée « Checklist de fin de guide »- J’ai au moins un hook
PostToolUsefonctionnel surEdit|Write - J’ai validé la syntaxe JSON de
settings.json - J’ai testé un hook bloquant avec
exit 2et un message - Je sais où mettre un hook (automatisme) vs un skill (procédure manuelle)
- Les hooks de validation lourde sont sur
Stop, pas sur chaqueEdit
À retenir
Section intitulée « À retenir »- Les hooks sont le seul garde-fou déterministe de Claude Code
PostToolUse+ matcherEdit|Write= rituel lint automatiquePreToolUse+exit 2= blocage avec message- Gardez les hooks rapides, sinon vous fatiguez la session
CLAUDE.mdconseille,settings.jsonautorise, les hooks exécutent