Mardi dernier, j'ai failli pousser une API key dans un dépôt public. Un Claude Code hook l'a interceptée une demi-seconde avant le commit. C'est à ce moment précis que je suis passé de « les hooks sont un plus » à « je ne ferai jamais tourner un agent sans eux ».
Les Claude Code hooks sont la fonctionnalité la plus sous-estimée de tout l'outil, et presque personne dans mon fil d'actualité ne les utilise. Je vais y remédier. Je vais vous montrer les hooks exacts que j'utilise en solo, la config qui les alimente, et celui qui a véritablement sauvé mon dépôt.
Ce qu'est vraiment un hook
Un hook est une commande shell que Claude Code exécute automatiquement quand quelque chose se produit. Avant qu'un outil tourne. Après qu'un fichier soit modifié. Quand l'agent a terminé. Vous ne demandez pas à Claude de se souvenir de faire quelque chose — le harness le fait, de manière déterministe, à chaque fois. Cette distinction est importante. Un agent qui « se souvient généralement » de lancer le formateur est un agent qui oubliera au pire moment possible.
Les hooks vivent dans votre settings.json. Voici la structure :
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "prettier --write \"$CLAUDE_FILE_PATHS\"" }
]
}
]
}
}
Celui-là lance Prettier sur n'importe quel fichier que Claude modifie, à l'instant même où il le modifie. Je ne pense plus jamais au formatage. Le diff est toujours propre. Fini les « oups, 400 changements d'espaces blancs » qui polluent mes commits.
Le hook qui a sauvé mon dépôt
C'est celui de l'API key. Un hook PreToolUse sur Bash qui détecte un git commit ou git push et fait un grep du diff stagé à la recherche de tout ce qui ressemble à un secret.
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "~/.claude/scripts/secret-scan.sh" }
]
}
]
}
Le script fait un grep des changements stagés à la recherche de patterns comme sk-, AKIA, et quelques-uns de mes propres préfixes de fournisseurs. S'il trouve quelque chose, le script sort avec un code non nul, et une sortie non nulle d'un hook PreToolUse bloque l'action. Claude ne peut littéralement pas exécuter la commande. Il est informé de la raison et contourne le problème.
Mardi dernier, ça a tapé. J'avais laissé un agent refactoriser une config et il a inséré en dur une clé depuis un fichier env qu'il n'aurait pas dû lire. Sans le hook, cette clé se retrouve sur GitHub, je fais tourner la rotation de credentials à minuit, et peut-être que quelqu'un mine de la crypto à mes frais. Avec le hook, j'ai eu un message de blocage d'une ligne et j'ai continué. Douze lignes de bash. L'assurance la moins chère que je possède.
Mes hooks du quotidien
Au-delà des deux précédents, voici ce qui gagne vraiment sa place.
Un hook PostToolUse qui lance mon vérificateur de types après les modifications de fichiers .ts et renvoie les erreurs directement à l'agent. Claude voit ses propres erreurs de types sans que j'aie à les coller. Il se corrige dans le même tour. Cette boucle de rétroaction seule vaut la mise en place. L'agent corrige l'erreur avant même que je la lise.
Un hook Stop qui déclenche une notification bureau quand Claude termine une longue tâche. Je lance des agents sur de grosses refactorisations et je vais faire la cuisine. La notification signifie que je ne surveille pas un terminal, y jetant un œil toutes les quatre-vingt-dix secondes comme un tamagotchi.
{
"Stop": [
{ "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude is done\" with title \"CC\"'" }] }
]
}
Minuscule. Change entièrement ma façon de travailler. Je lance une tâche et je m'en vais avec une vraie confiance d'être prévenu.
Celui que j'ai supprimé
J'ai essayé un hook qui lançait toute ma suite de tests après chaque modification. Ça semblait responsable. C'était pénible. La suite prend 40 secondes, donc chaque petite modification bloquait pendant 40 secondes, et l'agent restait là à l'arrêt à brûler mon après-midi. Leçon : les hooks doivent être rapides ou asynchrones. Un hook synchrone lent transforme votre agent réactif en mélasse.
Maintenant les tests ne tournent que sur un hook Stop, une fois, à la fin. Les vérifications rapides (lint, types, scan de secrets) tournent en ligne. Les vérifications lentes tournent à la frontière. Adaptez le coût du hook à sa fréquence de déclenchement. S'il se déclenche à chaque modification, il a intérêt à retourner en moins d'une seconde.
Où les mettre
Les hooks spécifiques à un projet vont dans .claude/settings.json dans le dépôt pour qu'ils soient livrés avec le code et que mes collaborateurs en bénéficient aussi. Les personnels, comme la notification bureau, vont dans ~/.claude/settings.json pour me suivre sur tous les projets. Gardez le scanner de secrets global. Vous le voulez partout, sans exception, sans « je l'ajouterai plus tard ».
Un piège qui m'a coûté une heure : les commandes de hooks tournent dans un shell non interactif. Si votre commande s'appuie sur quelque chose de votre .zshrc, ça ne sera pas là. Je l'ai découvert quand prettier fonctionnait dans mon terminal et ne faisait silencieusement rien dans le hook parce que le PATH était différent. Utilisez des chemins absolus ou npx.
Commencez par un seul
Ne construisez pas tout ça aujourd'hui. Ajoutez le scanner de secrets. Juste celui-là. C'est douze lignes et c'est la différence entre un mardi calme et une urgence de rotation de credentials.
Ensuite, une fois que vous lui faites confiance, ajoutez le formateur. Puis la notification. En une semaine vous aurez un agent qui formate propre, bloque les secrets, corrige ses propres erreurs de types, et vous tape sur l'épaule quand il a terminé — et vous aurez arrêté de faire ces quatre choses à la main.
C'est ça, l'heure par jour. Ce n'a jamais été une grande tâche. C'était quarante petites, et maintenant un script shell les fait pendant que je me prépare un café.
