Pourquoi ce module
Jour 2 du tronc commun. Une fois Claude Code configuré (J1), on l'industrialise : des hooks qui imposent vos règles de sécurité/qualité à chaque appel d'outil, un workflow Git/GitHub (pre-commit, PR automatiques, code review IA via GitHub Actions), et une status line custom.
Les hooks sont le levier le plus sous-estimé : ce sont des scripts déterministes que Claude Code exécute autour de chaque action — donc votre garde-fou, pas une consigne que le modèle peut ignorer.
Objectifs pédagogiques
À l'issue de ce module, vous serez capable de :
- Comprendre le cycle de vie des hooks et les événements disponibles
- Écrire des hooks de sécurité (
PreToolUse) et de qualité (PostToolUse) - Bloquer une action via code de sortie ou sortie JSON structurée
- Industrialiser un workflow Git : pre-commit, conventional commits
- Configurer GitHub Actions avec
anthropics/claude-code-action(issue → PR auto, code review@claude) - Construire une status line custom
Plan détaillé
- Hooks : principe et cycle de vie
- Les événements de hooks
- Structure de configuration (
matcher,if,command/http) - Bloquer : code de sortie
2vs JSONhookSpecificOutput - Patterns de hooks (sécurité, qualité, contexte, injection)
- Git workflows : pre-commit, conventional commits
- GitHub Actions :
claude-code-action - Status line custom
- Atelier : 2 hooks + 1 GitHub Action sur votre repo
Hooks : principe et cycle de vie
Un hook est un script (ou endpoint HTTP) déclenché par Claude Code à un point précis du cycle. Contrairement à une instruction dans CLAUDE.md (que le modèle peut contourner), un hook s'exécute toujours et peut autoriser, bloquer ou enrichir une action.
Chargement du schéma…
Les événements de hooks
Principaux événements (source : docs officielles hooks) :
| Événement | Rôle |
|---|---|
| SessionStart | Charger du contexte au démarrage/reprise |
| UserPromptSubmit | Valider ou enrichir le prompt avant traitement |
| PreToolUse | Bloquer un appel d'outil avant exécution (sécurité) |
| PostToolUse | Réagir à un outil réussi (lint, test, format) |
| PostToolUseFailure | Gérer un échec d'outil |
| PermissionRequest | Auto-approuver/refuser une demande de permission |
| Stop / SubagentStop | Empêcher Claude (ou un sous-agent) de conclure |
| PreCompact / PostCompact | Encadrer la compaction du contexte |
| FileChanged / SessionEnd | Réagir à un changement de fichier / nettoyer en fin de session |
📚 Événements essentiels pour J2. Ce tableau couvre les cas les plus courants. Pour la liste complète des 25 événements, voir https://code.claude.com/docs/en/hooks

/hooks : la vue (lecture seule) des hooks configurés, groupés par événement (PreToolUse, PostToolUse, PostToolBatch, PermissionDenied…). On les édite dans settings.json.Structure de configuration
Dans settings.json, sous hooks : événement → groupes filtrés par matcher, chacun avec des handlers command ou http. Le champ if affine encore (ex. Bash(rm *)).
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash", // filtre par outil (regex possible)
"hooks": [
{
"type": "command",
"if": "Bash(rm *)", // filtre fin sur la commande
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/block-rm.sh",
"timeout": 30
}
]
}
]
}
}
Les matchers acceptent "Bash", "Edit|Write", une regex, ou les outils MCP : mcp__<serveur>__<outil> (ex. mcp__memory__.*).
⚠️ Breaking change v2.1.195 : les matchers sont désormais en correspondance exacte. Un identifiant avec tiret comme
code-revieweroumcp__brave-searchne matche plus par sous-chaîne. Pour cibler tous les outils d'un serveur MCP au nom composé, écrivez la regex explicite :mcp__brave-search__.*. Si un hook a « cessé de marcher » après mise à jour, vérifiez d'abord ses matchers.
⚠️ Breaking change v2.1.214 : les conditions
if:à segment unique ne matchent plus à toute profondeur. Une condition commeif: "src/**"ne vise désormais que<cwd>/src, et non plus tout dossiersrc/de l'arbre. Pour retrouver l'ancien comportement, écrivez explicitement**/src/**.C'est le pendant, côté hooks, du correctif de permissions vu en J1 — même changement, même version, mais avec une asymétrie à mémoriser : les règles de permission
denyetaskconservent le match à toute profondeur (une interdiction doit rester large), tandis que les conditionsif:de hooks suivent la règle stricte. Un hook de sécurité écrit avant juillet 2026 avec un pattern à segment unique couvre donc aujourd'hui moins de terrain qu'à sa rédaction — et sans rien signaler. C'est exactement le genre de régression silencieuse qu'un audit de configuration doit chercher.
🆕 Nouvel événement
DirectoryAdded(v2.1.219). Se déclenche quand un répertoire de travail est ajouté en cours de session — via/add-dirou la requête de contrôle SDKregister_repo_root. Utile pour rejouer une initialisation dépendante du périmètre : recharger un contexte, relancer un indexeur, ou refuser un répertoire hors politique. Jusque-là, seulSessionStartpermettait ce genre de réaction — et il ne voyait que le périmètre d'origine.
Bloquer : code de sortie 2 vs JSON
Deux mécanismes de décision dans un hook :
1. Code de sortie — simple :
#!/bin/bash
# .claude/hooks/block-rm.sh — reçoit le contexte JSON sur stdin
COMMAND=$(jq -r '.tool_input.command' < /dev/stdin)
if echo "$COMMAND" | grep -q 'rm -rf'; then
echo "Bloqué : commande destructrice" >&2
exit 2 # PreToolUse : BLOQUE l'appel d'outil
fi
exit 0 # flux de permission normal
| Événement | exit 0 | exit 2 |
|---|---|---|
| PreToolUse | flux normal | bloque l'outil |
| UserPromptSubmit | traite le prompt | bloque le prompt |
| Stop | continue | empêche l'arrêt |
2. Sortie JSON — contrôle fin via hookSpecificOutput :
jq -n '{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "deny",
"permissionDecisionReason": "Écriture en base bloquée par la policy"
}
}'
permissionDecision : "allow" | "deny" | "ask" | "defer". Un PostToolUse peut renvoyer additionalContext pour re-injecter un retour (ex. erreurs de lint) au modèle.
Patterns de hooks
- Sécurité (
PreToolUse) : bloquerrm -rf,git push --force, écritures sur serveurs MCP sensibles. - Qualité (
PostToolUsesurEdit|Write) : auto-format + lint, et renvoyer les erreurs enadditionalContextpour que Claude corrige. - Contexte (
SessionStart) : charger la branche, les issues ouvertes, l'état du déploiement → stdout = contexte. - Garde-fou (
Stop) : empêcher de conclure tant que les tests ne passent pas.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/lint.sh" }
]
}
]
}
}
Étude de cas : security-guidance, un vrai plugin de hooks signé Anthropic
Plutôt que d'inventer un exemple, disséquons un plugin officiel installable en une commande. C'est le meilleur support pour comprendre comment les hooks s'assemblent en produit :
/plugin install security-guidance@claude-plugins-official
Ce qu'il fait — une revue de sécurité du code écrit par Claude, en trois couches de coût et de profondeur croissants :
| Couche | Événement | Mécanique | Coût |
|---|---|---|---|
| 1. Alertes par motif | PostToolUse sur Edit|Write|MultiEdit|NotebookEdit | ~25 motifs regex dangereux (yaml.load, pickle.load, innerHTML brut, secrets en dur…) | Nul — pas d'appel LLM |
| 2. Revue du diff | Stop | Envoie le diff à un appel LLM rapide ; les findings graves reviennent à Claude avant que vous voyiez la réponse | 1 appel / tour |
| 3. Revue agentique | PostToolUse avec if: "Bash(git commit:*)" | Un reviewer SDK lit les fichiers liés (Read/Grep/Glob) et trace le flux de données entre fichiers — attrape IDOR, bypass d'auth, SSRF multi-fichiers | 1 session agent / commit |
Les trois leçons d'architecture, et c'est pour elles qu'on l'étudie :
- Le bon événement pour la bonne latence. Le regex est gratuit → on le met sur chaque édition. L'appel LLM coûte → on le met sur
Stop, une fois par tour. La revue agentique coûte cher → surgit commit, quelques fois par jour. Un hook mal placé, c'est soit une facture, soit un angle mort. asyncRewake: le hook qui réveille Claude. Les couches 2 et 3 déclarentasyncRewake: trueavec unrewakeMessage. Le hook ne bloque pas la réponse : il tourne en tâche de fond et relance Claude avec ses findings quand ils arrivent. C'est le motif à retenir pour tout contrôle lent qu'on refuse de mettre sur le chemin critique.- La politique maison est un fichier, pas du code. Un
claude-security-guidance.mddéposé dans~/.claude/,<projet>/.claude/(versionné) ou.local.md(gitignoré) est concaténé au prompt de revue, dans cet ordre. Les règles built-in couvrent les classes génériques (injection, XSS, SSRF, path traversal…) ; ce fichier sert à ce que le modèle ne peut pas déduire — « les SELECT surcustomerspassent pardb.replica», « les jobs de fond n'utilisent pas le token utilisateur ».
Réglages — tout par variables d'environnement, aucune obligatoire : SECURITY_GUIDANCE_DISABLE=1 (kill switch), ENABLE_PATTERN_RULES=0, ENABLE_STOP_REVIEW=0 (à couper en worktree partagé, où un autre agent peut déplacer HEAD entre deux tours), ENABLE_COMMIT_REVIEW=0, SECURITY_REVIEW_MODEL (modèle de la couche 2). Prérequis : CLI ≥ v2.1.144 et Python 3.8+ sur le PATH.
⚠️ Deux points à énoncer en formation, sans les enjoliver. D'abord, la revue envoie du code à un modèle : diff, chemins, contenu des fichiers concernés, et — pour la couche 3 — tout ce que le reviewer ouvre en traçant le flux. Selon votre configuration (API Anthropic, gateway, Bedrock/Vertex/Foundry), ça part chez un destinataire différent, avec des conditions contractuelles différentes. À arbitrer avant l'installation, pas après (cf. M1). Ensuite, la documentation le dit elle-même : « best-effort assistive tool, not a guarantee », sans garantie. Ça ne remplace ni la revue humaine, ni un SAST/DAST, ni le scan de dépendances. Un outil qui trouve 60 % des failles et rassure à 100 % est un risque net.
💡 À ne pas confondre avec la commande intégrée
/security-review, qui lance une revue de sécurité des changements de la branche courante à la demande. Le plugin, lui, est passif et continu : il s'exécute sans qu'on le convoque. Les deux se complètent — l'un pour l'audit ponctuel, l'autre pour le filet de sécurité.
Git workflows : pre-commit, conventional commits
- Pre-commit (côté Git, complémentaire des hooks Claude) : lint, types, scan de secrets — bloque un commit non conforme.
- Conventional commits : Claude rédige des messages
type(scope): …cohérents ; à cadrer dansCLAUDE.md. - Astuce : un hook
PostToolUsesurBash(git commit *)peut valider le format du message avant qu'il parte.
🔄 Claude ne déclenche plus les revues tout seul (v2.1.215 et v2.1.218). Trois skills sont passées en invocation manuelle exclusive :
/verify,/code-review(v2.1.215) et/deep-research(v2.1.218). Auparavant, Claude pouvait décider de les lancer de lui-même au milieu d'une tâche.Le changement paraît anodin ; il ne l'est pas pour une équipe. Une revue auto-déclenchée consomme un budget que personne n'a demandé et injecte ses findings dans une conversation qui parlait d'autre chose. Depuis la v2.1.218,
/code-reviewtourne d'ailleurs en sous-agent d'arrière-plan — son travail ne pollue plus le fil principal. Si votre process reposait sur le déclenchement automatique, il faut désormais l'expliciter : un hook, une étape de CI, ou une consigne dansCLAUDE.md.À ne pas confondre non plus :
/review <pr>est revenu à une revue simple, une passe (v2.1.202) ; la revue multi-agents se demande via/code-review <niveau> <pr#>. Deux commandes, deux coûts, deux profondeurs.
🔒 Injection shell dans les plugins — correctif v2.1.207, à connaître si vous en écrivez. Les valeurs de configuration
${user_config.*}en forme shell sont désormais rejetées dans les hooks, monitors etheadersHelperde plugins : une valeur fournie par l'utilisateur pouvait s'échapper dans la commande shell. Les remplacements :
- Hooks : passer en forme exec (tableau
args), ou lire$CLAUDE_PLUGIN_OPTION_<CLÉ>depuis l'environnement.- Monitors et
headersHelper: lire la valeur à l'intérieur du script (fichier de config, ou blocenvdu serveur).Dans la même version, les valeurs d'options de plugin (
pluginConfigs) ne sont plus lues depuis le.claude/settings.jsondu projet — seulement depuis les settings user,--settingset managed. Même doctrine que pourautoMode(cf. J5) et l'approbation.mcp.json(cf. J4) : un dépôt cloné ne configure pas ce qui s'exécute sur votre machine. Trois correctifs distincts, une seule règle — c'est ce fil directeur qu'il faut enseigner, pas la liste.

/diff : revoir les changements non commités (git diff HEAD) directement dans la session — fichiers, statut, et +/- lignes — avant de rédiger le commit.GitHub Actions : claude-code-action
L'action officielle anthropics/claude-code-action fait tourner Claude Code sur vos PR et issues : répondre aux mentions @claude, traiter une issue assignée, ou exécuter une tâche d'automatisation.
Mise en place (la plus simple) : dans le terminal, claude puis /install-github-app — la commande configure la GitHub App et les secrets.
Authentification (secrets du repo, jamais en clair) :
ANTHROPIC_API_KEY(clé API), ouCLAUDE_CODE_OAUTH_TOKEN(Pro/Max/Team/Enterprise : générer viaclaude setup-token).
# .github/workflows/claude.yml (extrait)
name: Claude
on:
issue_comment:
types: [created]
jobs:
claude:
runs-on: ubuntu-latest
steps:
- uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
Cas d'usage : issue → investigate → PR auto, code review automatique sur PR, génération de release notes. L'action détecte le contexte (mention, assignation, prompt explicite) pour décider quand s'activer.
🔐 Sécurité CI : toujours via GitHub Actions secrets, jamais de clé commitée. Restreindre les déclencheurs (qui peut invoquer
@claude).
Attribution et métadonnées des commits
Le réglage attribution.sessionUrl (v2.1.183) permet d'omettre le lien de session claude.ai des commits et PR automatiques. Pertinent pour qui veut des commits sans trace de session externe.
{
"attribution": {
"sessionUrl": false // N'ajoute pas le lien claude.ai|session/<id> au pied des commits/PR
}
}
Par défaut, Claude Code ajoute le lien de session en fin de message de commit ou description PR (utile pour audit/debugging). Passer sessionUrl: false supprime cette métadonnée — les commits apparaissent plus épurés.
💡 Cas d'usage : équipes refusant toute référence à des sessions externes, ou préférant une hygiène de commit minimaliste.
Status line custom
La status line affiche l'état de session (modèle, coût, branche…). Elle reçoit un JSON sur stdin et écrit la ligne (ANSI) sur stdout.

Ctx %, durée, quotas, dossier). Une status line custom remplace cette ligne via stdin JSON → stdout.{ "statusLine": { "type": "command", "command": "~/.claude/statusline.sh" } }
#!/bin/bash
input=$(cat) # JSON de session sur stdin
model=$(echo "$input" | jq -r '.model.display_name')
branch=$(git branch --show-current 2>/dev/null)
printf "⚡ %s · %s" "$model" "${branch:-no-git}"
Atelier (180 min, 2 temps) : hooks + GitHub Action
Atelier 2a (matin) — écrire 2 hooks sur le repo réel du stagiaire :
PreToolUse(sécurité) qui bloquerm -rf/git push --force(exit 2 ou JSONdeny).PostToolUsesurEdit|Write(qualité) qui formate + lint et renvoie les erreurs enadditionalContext.
Atelier 2b (après-midi) — déployer status line + 1 GitHub Action :
3. Une status line affichant modèle + branche.
4. /install-github-app + un workflow @claude qui répond sur une issue de test (auth via CLAUDE_CODE_OAUTH_TOKEN).
Livrable : repo avec .claude/hooks/, status line active, et un workflow GitHub Actions fonctionnel.
Pour aller plus loin
- 📖 Claude Code — Hooks
- 📖 Claude Code — GitHub Actions
- 🔗 anthropics/claude-code-action
- 📖 Claude Code — Status line
- 🎯 Tronc commun : J1 (settings/permissions) ← → J3 (skills, SDK, vision)