# Hooks Claude Code : configuration et sept exemples

> 7 hooks prêts à l’emploi pour .claude/settings.json, à copier-coller : protection de .env, prettier, force push vers main, tirets cadratins et serveur de développement. 4 types de gestionnaires et 31 événements.

Source: https://ailoot.ai/blog/fr/claude-code-hooks/
Canonical: https://ailoot.ai/blog/fr/claude-code-hooks/
Language: fr
Publisher: Ailoot (https://ailoot.ai/)
Translation: guide-121
Revision: sha256-e484389e5eddc27612e930d60c001a0d5c55cab54e911c7952cd524716399651


Dans Claude Code, les hooks sont des commandes shell, des appels HTTP ou de courtes vérifications par LLM qui s’exécutent automatiquement avant ou après une action de Claude. Ils ajoutent des contrôles à certains événements d’outils, formatent après modification et injectent du contexte en début de session. Un PreToolUse correspondant peut rejeter cet appel ; ces regex sont des garde-fous pratiques, pas une limite de sécurité couvrant tous les chemins de fichiers ou Git. Voici une présentation des 4 types de gestionnaires, des 31 événements du cycle de vie, un test pour déterminer « quand une règle doit être un hook », 7 hooks prêts à copier dans `.claude/settings.json` et 3 pièges que j’ai moi-même rencontrés.



[Documentation Ailoot : API, clés et configuration](https://ailoot.ai/docs/).

## Que sont les hooks dans Claude Code ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Un hook est une commande shell, un appel HTTP, un court prompt LLM ou un sous-agent que Claude Code exécute en réponse à un événement précis du cycle de vie. Contrairement aux instructions de `CLAUDE.md`, que le modèle peut ignorer ou oublier, un hook s’exécute toujours de la même manière : c’est du code, pas une suggestion.

</div>

Pendant six mois, j’ai gardé la règle « ne jamais modifier `.env` directement » dans `CLAUDE.md`. Claude l’enfreignait une fois par mois environ. Pas intentionnellement ; il l’oubliait simplement à la fin d’une longue session. Une fois, un jeton supplémentaire s’est retrouvé dans `.env` ; une autre fois, une variable portant un nom en double a écrasé celle qui était utilisée.

J’ai déplacé cette règle dans un hook, ce qui la rend impossible à oublier. Quand un outil de modification directe correspondant fournit un chemin .env, le hook rejette cet appel. Redirections Bash, scripts, MCP et autres accès exigent des contrôles distincts ; cet exemple ne rend pas le fichier immuable.

<figure>
<blockquote>
<p>Les hooks sont des commandes shell, des points de terminaison HTTP ou des prompts LLM que vous définissez et qui s’exécutent automatiquement à des moments précis du cycle de vie de Claude Code.</p>
</blockquote>
<figcaption>— Anthropic, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Autrement dit, un hook est une commande que Claude Code exécute lors d’un événement précis. Chaque modification de fichier, lancement de Bash, début de session ou fin d’exécution d’un sous-agent est un événement auquel vous pouvez associer un hook.

Pourquoi c’est utile pour les vibe codeurs :

- Protéger les fichiers contenant des secrets contre les modifications accidentelles.
- Exécuter automatiquement prettier/eslint après chaque génération de code.
- Bloquer `git push --force` vers main sans autorisation explicite.
- Injecter du contexte récent au début de chaque session.
- Consigner les actions de Claude à des fins d’audit.

CLAUDE.md gère le contexte. Les hooks gèrent les **actions**. Ce sont deux choses différentes, et les confondre est la principale erreur des débutants.

## Quelle est la différence entre les hooks, CLAUDE.md et les skills ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

`CLAUDE.md` est un texte que le modèle lit au début d’une session et qu’il essaie de suivre. Un hook est une commande shell que le processus Claude Code exécute en réponse à un événement, indépendamment du modèle. Un skill est un ensemble nommé d’instructions que Claude peut appeler avec l’outil Skill, comme une fonction. Ces trois éléments répondent à des types de tâches différents.

</div>

La documentation d’Anthropic explique explicitement la différence entre `CLAUDE.md` et les hooks :

<figure>
<blockquote>
<p>Le contenu de CLAUDE.md est transmis sous forme de message utilisateur après le prompt système, et non dans le prompt système lui-même. Claude le lit et essaie de le suivre, mais son respect strict n’est pas garanti.</p>
</blockquote>
<figcaption>— Anthropic, documentation sur la mémoire, https://code.claude.com/docs/en/memory</figcaption>
</figure>

Autrement dit, le contenu de `CLAUDE.md` parvient à Claude sous la forme du premier message de l’utilisateur, et non d’une instruction système. Le modèle le lit et essaie de le suivre. Mais rien ne le garantit, surtout vers la fin d’une longue session, quand le contexte s’allonge et que les instructions plus anciennes sont écartées.

Un hook fonctionne autrement. Il se trouve dans `settings.json`, ne dépend pas du modèle et est exécuté par le processus Claude Code lors d’un événement précis : `PreToolUse`, `PostToolUse`, `SessionStart` et d’autres. La commande s’exécute avant que Claude reçoive le résultat et peut bloquer concrètement une action (exit 2) ou modifier un fichier après sa modification (exit 0).

Comparaison :

| Fonctionnalité | CLAUDE.md | Hook | Skill |
|----|----|----|----|
| Emplacement | `CLAUDE.md` à la racine du projet | `.claude/settings.json` | Dossier `.claude/skills/` |
| Qui l’exécute ? | Le modèle le lit dans un message utilisateur | Le processus Claude Code lors d’un événement | Le modèle via l’outil Skill |
| Exécution garantie ? | Non (au mieux) | Le hook configuré correspondant exécute du code ; échecs et sorties dépendent de l’événement | Via un appel au modèle (avec une autorisation, mais pas au niveau du shell) |
| Usage | Contexte du projet, ton, style, routage | Restrictions, formatage, vérifications portant sur certains fichiers | Procédures complexes en plusieurs étapes avec paramètres |
| Quand s’exécute-t-il ? | À chaque message du modèle dans une session | Lors d’un événement du cycle de vie | Quand le modèle l’appelle |
| Peut-il bloquer une action ? | Non | Oui (exit 2) | Non |

Le modèle complet de `CLAUDE.md` et 6 règles pour garder ce fichier vivant figurent dans le guide [Comment configurer CLAUDE.md en 2026 : un modèle prêt à l’emploi et 6 règles](https://ailoot.ai/blog/fr/claude-md-template/). Découvrez les skills comme capacités nommées dans [Claude Code Skills : comment créer votre propre bibliothèque](https://ailoot.ai/blog/fr/claude-code-skills-library/).

Un hook se situe au niveau le plus bas : pas de modèle, pas d’interprétation, juste le shell.

Les hooks sont l’un des outils d’[ingénierie du contexte](https://ailoot.ai/blog/fr/context-engineering/), aux côtés de Plan Mode et de /compact.



## 4 types de hooks : Command, HTTP, Prompt, Agent — lequel choisir ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Claude Code propose 4 types de gestionnaires. Command est un script shell, le type le plus courant. HTTP envoie une requête POST à votre URL, pour les équipes et les intégrations. Prompt effectue un court appel à Haiku pour une vérification sémantique. Agent crée un sous-agent ayant accès aux outils pour une validation approfondie. Les débutants ont besoin de Command et Prompt ; ils couvrent 90% des tâches.

</div>

| Type | Fonction | Quand le choisir | Complexité |
|----|----|----|----|
| **Command** | Exécute une commande shell, reçoit du JSON sur stdin et répond avec un code de sortie et stdout | 90% des cas : vérifier des chemins, formater, rechercher dans le contenu avec grep, lsof, opérations Git | faible |
| **HTTP** | Envoie une requête POST avec un corps JSON à votre URL et reçoit du JSON en réponse | Quand une règle concerne une équipe ou une CI : validation centralisée, tableau de bord d’état partagé, audit à distance | moyenne (nécessite un serveur) |
| **Prompt** | Envoie un court prompt à Haiku (par défaut) et obtient une réponse oui/non pour une évaluation sémantique | Quand grep ne suffit pas : « évalue si ce texte a un ton d’entreprise » | faible |
| **Agent** | Crée un sous-agent ayant accès à Read/Grep/Glob pour des vérifications en plusieurs étapes | Validation approfondie : « trouve tous les fichiers modifiés et vérifie que les tests correspondants existent » | élevée |

L’erreur la plus courante consiste à utiliser Command pour une tâche qui relève de Prompt. Par exemple, « ne pas utiliser de tirets cadratins dans les fichiers `.md` » peut se vérifier avec grep, donc Command est idéal. Mais « ne pas écrire sur un ton d’entreprise » relève de la sémantique et nécessite Prompt.

Si vous débutez, utilisez Command. Les 7 hooks prêts à l’emploi ci-dessous sont tous de type Command. Vous pourrez passer à HTTP/Prompt/Agent ensuite, quand vous serez prêt.

## Où se trouve settings.json et quels sont les 4 niveaux de configuration ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Claude Code recherche la configuration à 4 endroits : entreprise (déployée via MDM), utilisateur (`~/.claude/settings.json`), projet (`.claude/settings.json` à la racine du dépôt) et local (`.claude/settings.local.json`, absent de Git). Les 4 fichiers sont fusionnés au début de la session, le niveau inférieur étant prioritaire.

</div>

| Niveau | Chemin | Portée | Dans Git ? |
|----|----|----|----|
| Entreprise | `/Library/Application Support/ClaudeCode/settings.json` (Mac), `/etc/claude-code/settings.json` (Linux) | À l’échelle de l’équipe via MDM | non |
| Utilisateur | `~/.claude/settings.json` | Tous les projets d’un utilisateur | non |
| Projet | `.claude/settings.json` à la racine du dépôt | Ce projet, pour toute l’équipe | **oui** |
| Local | `.claude/settings.local.json` | Ce projet uniquement, pour cet utilisateur uniquement | **non** (dans `.gitignore`) |

Je garde les 7 hooks décrits ci-dessous dans le fichier `.claude/settings.json` du **projet**, afin qu’ils soient inclus automatiquement lors du clonage du dépôt et que Claude suive les mêmes règles dans une nouvelle fenêtre. Les hooks expérimentaux personnels vont dans `.claude/settings.local.json` pour ne pas gêner l’équipe.

Les 4 fichiers sont fusionnés au début de la session. En cas de conflit entre les règles, le niveau inférieur est prioritaire (local sur projet, projet sur utilisateur).

**Le moyen le plus rapide de créer votre premier hook** consiste à utiliser le menu interactif `/hooks` directement dans Claude Code. Choisissez un événement dans la liste, sélectionnez un matcher (par exemple, `Bash` ou `Edit`), puis collez une commande. Claude Code écrit un JSON valide dans `settings.json`.

## 31 événements du cycle de vie : lesquels sont utiles aux débutants ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Claude Code compte actuellement 31 événements du cycle de vie, répartis en 3 fréquences : une fois par session (`SessionStart`/`SessionEnd`), une fois par tour (`UserPromptSubmit`/`Stop`) et à chaque appel d’outil (`PreToolUse`/`PostToolUse`). Les débutants ont besoin de 4 des 31 événements : `PreToolUse` pour les restrictions, `PostToolUse` pour les modifications automatiques, `SessionStart` pour le contexte et `Stop` pour les vérifications finales.

</div>

<figure>
<blockquote>
<p>Les événements se produisent selon trois fréquences : une fois par session (SessionStart, SessionEnd), une fois par tour (UserPromptSubmit, Stop, StopFailure) et à chaque appel d’outil dans la boucle agentique (PreToolUse, PostToolUse).</p>
</blockquote>
<figcaption>— Anthropic, référence des hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Il est important de comprendre ces trois fréquences pour éviter toute confusion :

1.  **Une fois par session** - `SessionStart`, `SessionEnd`, `Setup`. Ces événements se déclenchent une fois à l’ouverture et à la fermeture de Claude Code. Utilisez-les pour initialiser le contexte et nettoyer les fichiers temporaires.
2.  **Une fois par tour** - `UserPromptSubmit`, `UserPromptExpansion`, `Stop`, `StopFailure`. Ils se déclenchent à chacun de vos messages et chaque fois que le modèle s’arrête. Utilisez-les pour consigner la conversation et effectuer les vérifications finales du résultat.
3.  **À chaque appel d’outil** - `PreToolUse`, `PostToolUse`, `PostToolUseFailure`, `PermissionRequest`, `PermissionDenied`, `PostToolBatch`, `SubagentStart`, `SubagentStop`. Ils se déclenchent à chaque modification de fichier, lancement de Bash, lecture ou recherche. C’est la couche la plus sollicitée, où se trouvent les restrictions, le formatage et les validations.

La liste complète des 31 événements comprend des événements asynchrones/réactifs : `TeammateIdle`, `PreCompact`, `PostCompact`, `Elicitation`, `ElicitationResult`, `InstructionsLoaded`, `ConfigChange`, `CwdChanged`, `FileChanged`, `WorktreeCreate`, `WorktreeRemove`, `Notification`. Ils sont réservés aux scénarios avancés ; laissez-les de côté pour le moment.

## Test en cinq questions : quand une règle doit-elle devenir un hook et quand doit-elle rester dans CLAUDE.md ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Si une règle porte sur une action (et non sur le contexte), concerne la sécurité/l’argent/les données, peut être vérifiée avec grep/regex, serait difficile à annuler en cas d’infraction et que Claude l’a déjà enfreinte au moins une fois, elle doit devenir un hook. Si moins de trois de ces cinq réponses sont « oui », elle reste dans `CLAUDE.md`. J’ai appliqué ce test à mes 80 lignes et déplacé 5 règles dans des hooks.

</div>

Pour moi, c’est une loi, pas une suggestion. J’ai résumé le choix entre `CLAUDE.md` et un hook en un simple test de cinq questions. Si au moins trois réponses sont « oui », la règle devient un hook. Avec moins de trois réponses positives, elle reste dans le texte.

**Le test « quand une règle doit-elle devenir un hook ? » :**

1.  Cette règle porte-t-elle sur une action plutôt que sur le contexte ? (« ne pas exécuter `rm -rf` » concerne une action ; « j’ai un projet TypeScript » relève du contexte.)
2.  Porte-t-elle sur la sécurité, l’argent ou les données ? (« ne pas toucher à `.env` », « ne pas publier de clés ».)
3.  Peut-on la vérifier avec grep, un regex ou le code de sortie d’une commande shell ? (« après la modification de `.md`, vérifier qu’il n’y a pas de tiret cadratin. »)
4.  Si la règle est enfreinte, les conséquences seraient-elles difficiles à annuler ? (« forcer un push vers main » est difficile à annuler ; « oublier un point final » est facile.)
5.  Avez-vous déjà constaté que Claude oubliait cette règle au moins une fois ?

**Exemple : « Ne pas utiliser de tirets cadratins ; utiliser uniquement des traits d’union. »** Action ? Oui. Sécurité ? Non. Vérifiable avec grep ? Oui. Difficile à annuler ? Non. Déjà enfreinte ? Oui. **3 sur 5 : mettez-la dans un hook.**

**Contre-exemple : « J’ai des vibe codeurs, pas des développeurs ; écris sans argot. »** Action ? Plutôt du contexte. Sécurité ? Non. Vérifiable avec un regex ? Trop difficile. Difficile à annuler ? Non. Déjà enfreinte ? Oui. **1 sur 5 : gardez-la dans `CLAUDE.md`.**

Cinq de mes règles ont réussi le test. Elles sont passées dans des hooks. Les 75% restants sont restés dans le fichier texte.

## 7 hooks prêts à copier

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Voici 7 hooks prêts à l’emploi pour `.claude/settings.json` : bloquer les modifications de `.env`, lancer prettier automatiquement, empêcher les force push vers main, détecter les tirets cadratins, libérer un port avant `npm run dev`, effectuer un commit automatique après des tests réussis et injecter du contexte récent au début d’une session. Copiez chaque hook dans le fichier, redémarrez Claude Code et c’est opérationnel.

</div>

Les 7 hooks sont réunis dans un seul fichier `.claude/settings.json` à la racine du projet. Chaque entrée contient un type d’événement, un filtre d’outil (matcher) et une commande shell. Le code 2 rejette un appel PreToolUse correspondant avant exécution. Pour PostToolUse, l’action a déjà eu lieu : le hook donne un retour sans pouvoir l’annuler.

<figure>
<blockquote>
<p>Pour la plupart des événements de hook, seul le code de sortie 2 bloque une action. Claude Code traite le code de sortie 1 comme une erreur non bloquante et poursuit l’action, même si 1 signifie généralement un échec sous Unix.</p>
</blockquote>
<figcaption>— Anthropic, référence des hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

`exit 0` signifie réussite. `exit 1` signifie erreur, mais Claude continue (c’est le premier anti-pattern ci-dessous). `exit 2` dépend de l’événement : il peut bloquer PreToolUse, tandis que PostToolUse rapporte seulement une action déjà réalisée.

### Hook 1. Contrôler les modifications directes de `.env` (PreToolUse, exit 2)

Bloque les tentatives de modification directe de tout fichier correspondant à `.env`, `.env.local`, `.env.production`, etc.

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '(^|/)\\.env(\\.|$)' && { echo 'Do not edit `.env` directly. Tell me which variable to add, and I’ll open the file myself.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** Avant chaque `Edit`/`Write`/`MultiEdit`, il récupère le chemin du fichier, vérifie avec grep s’il correspond au motif `.env` et, si c’est le cas, renvoie le code de sortie 2 accompagné d’un message. Claude voit stderr et me dit : « J’ai essayé de modifier `.env`, mais le hook m’a bloqué ; dites-moi quelle variable ajouter. »

### Hook 2. Lancer prettier automatiquement après chaque modification de code (PostToolUse, exit 0)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(ts|tsx|js|jsx|json)$' && npx prettier --write \"$FILE\" 2>/dev/null & exit 0"
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** Après chaque `Edit`/`Write`, il récupère le chemin et, si l’extension est `.ts`/`.tsx`/`.js`/`.jsx`/`.json`, lance prettier en arrière-plan avec `&`. Il renvoie toujours le code de sortie 0, car il s’agit de formatage, pas d’une restriction. Si prettier échoue, ce n’est pas grave ; le fichier reste tel quel.

La différence avec le hook 1 est fondamentale : PreToolUse + exit 2 constitue un blocage strict **avant** l’action. PostToolUse + exit 0 apporte une modification légère **après** l’action. Ce sont deux modes différents, adaptés à des tâches différentes.

### Hook 3. Bloquer `git push --force` vers la branche main (PreToolUse, exit 2)

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE 'git[[:space:]]+push' && echo \"$CMD\" | grep -qE '(\\-\\-force|\\-\\-force-with-lease|[[:space:]]-f([[:space:]]|$))' && echo \"$CMD\" | grep -qE '([[:space:]/:])main([[:space:]/:]|$)' && { echo 'Force push to main is forbidden. Create a new branch or ask me to do it manually.' >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** À chaque lancement de Bash, il vérifie si la commande contient `push`, `--force`/`--force-with-lease`/`-f` et mentionne `main`. Si les trois conditions sont réunies, il bloque l’action avec un message clair.

Avant, j’avais cette règle en majuscules dans `CLAUDE.md`. Claude l’enfreignait encore environ toutes les deux semaines quand je lui demandais de « forcer le push pour annuler mon mauvais commit ». Le hook reconnaît les commandes littérales montrées, mais alias, scripts, branche implicite ou autre client Git peuvent contourner ce contrôle de chaîne. Protégez main sur le distant et limitez les identifiants de l’agent.

### Hook 4. Bloquer les tirets cadratins dans les fichiers texte (PostToolUse, exit 2)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write|MultiEdit",
        "hooks": [
          {
            "type": "command",
            "command": "FILE=$(jq -r '.tool_input.file_path // empty'); [ -z \"$FILE\" ] && exit 0; echo \"$FILE\" | grep -qE '\\.(md|mdx|tsx?)$' && grep -q $'\\xe2\\x80\\x94' \"$FILE\" && { echo \"File $FILE contains an em dash (U+2014). Replace it with a regular hyphen (U+002D).\" >&2; exit 2; } || exit 0"
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** PostToolUse avec exit 2 signale à Claude un échec après modification, sans annuler ni empêcher l’écriture. Si Claude modifie un fichier et y laisse un tiret cadratin, le hook échoue, Claude reçoit un retour et réessaie. Après 2–3 sessions, Claude cesse complètement d’insérer ce caractère.

Notez que grep recherche spécifiquement le caractère du tiret cadratin (U+2014) à l’aide de `$'\xe2\x80\x94'`, la notation Bash qui représente ses octets UTF-8. Si vous écrivez simplement `grep -q '-'`, la commande détectera aussi les traits d’union ordinaires et s’exécutera sur chaque fichier. Un caractère invisible peut faire échouer toute la logique.

### Hook 5. Libérer le port 3000 avant `npm run dev` (PreToolUse, exit 0)

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "CMD=$(jq -r '.tool_input.command // empty'); [ -z \"$CMD\" ] && exit 0; echo \"$CMD\" | grep -qE '(npm|pnpm|yarn)[[:space:]]+run[[:space:]]+dev' && { lsof -ti:3000 | xargs -r kill -9 2>/dev/null; echo 'Port 3000 freed before launch' >&2; }; exit 0"
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** Avant chaque `npm run dev` (ou `pnpm`/`yarn`), il tue le processus qui occupe le port 3000. Il renvoie toujours le code de sortie 0, car il s’agit de préparation, pas d’une restriction. Cela évite une catégorie de pièges où Claude voit « port in use », pense qu’il y a un problème de code et commence à corriger des choses alors que l’ancien serveur tournait simplement encore.

### Hook 6. Créer automatiquement un commit après des tests réussis (PostToolUse, exit 0)

```json
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "INPUT=$(cat); CMD=$(printf '%s' \"$INPUT\" | jq -r '.tool_input.command // empty') || exit 1; EXIT=$(printf '%s' \"$INPUT\" | jq -r '.tool_response.exit_code // empty') || exit 1; case \"$CMD\" in 'npm test'|'pnpm test'|'yarn test') ;; *) exit 0 ;; esac; [ \"$EXIT\" = \"0\" ] || exit 0; git diff --cached --quiet || { echo 'Existing staged changes: review manually' >&2; exit 0; }; git add -- src/orders.ts tests/orders.test.ts || exit 1; git diff --cached --quiet && exit 0; git commit -m 'tests: checkpoint after successful run'"
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** Il lit stdin JSON une seule fois et accepte seulement les commandes de test simples exactes affichées. Il poursuit si `tool_response.exit_code` vaut explicitement zéro ; si ce champ manque dans votre version, il saute le commit sans supposer la réussite. Remplacez `src/orders.ts` et `tests/orders.test.ts` par les fichiers exacts attribués à la tâche. Des changements déjà indexés arrêtent la recette pour exclure le travail sans rapport. Un ajout raté arrête, un diff vide ne crée rien et les hooks ordinaires restent actifs. Testez hors ligne succès, échec et résultat manquant avant activation.

Anti-pattern : ajouter `git commit --no-verify` à chaque Edit. Gardez les contrôles de commit. Si l’automatisation récursive les déclenche, réduisez le déclencheur ou repensez le contrôle sans contourner la validation par défaut. Examinez diff indexé et propriété des fichiers avant activation.

### Hook 7. Injecter du contexte récent au début d’une session (SessionStart avec additionalContext)

```json
{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "*",
        "hooks": [
          {
            "type": "command",
            "command": "BRANCH=$(git symbolic-ref --short HEAD 2>/dev/null || echo 'no-branch'); STATUS=$(git status --short 2>/dev/null | head -10); echo \"{\\\"additionalContext\\\":\\\"Current branch: $BRANCH. Uncommitted changes:\\n$STATUS\\\"}\""
          }
        ]
      }
    ]
  }
}
```

**Ce qu’il fait :** Au début de la session, il récupère la branche Git actuelle et la liste des modifications non validées, puis renvoie du JSON contenant le champ `additionalContext`. Claude Code injecte ce texte dans le premier message système ; Claude sait ainsi dès le départ où il se trouve et ce qui reste de la session précédente.

<figure>
<blockquote>
<p>Utilisez additionalContext pour les informations que Claude doit connaître sur l’état actuel de l’environnement ou une opération tout juste terminée : état de l’environnement (branche actuelle, cible de déploiement, indicateurs de fonctionnalité actifs), règles de projet conditionnelles (quelle commande de test s’applique au fichier qui vient d’être modifié), données externes (tickets ouverts qui vous sont attribués, résultats récents de CI).</p>
</blockquote>
<figcaption>— Anthropic, référence des hooks, https://code.claude.com/docs/en/hooks</figcaption>
</figure>

Extensions possibles de ce hook : récupérer le dernier commit et sa description, l’état de GitHub Actions, le sprint en cours dans Linear/Jira et les tickets ouverts du dépôt actuel.

## 3 pièges liés aux hooks qui surprennent une personne sur deux

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Premier piège : un hook qui renvoie exit 1 laisse l’action passer sans bruit, et Claude ignore l’erreur. Deuxième piège : les opérations lourdes dans un hook sont interrompues après un délai maximal de 60 secondes. Troisième piège : un hook ne voit que les variables d’environnement de `~/.zshrc`, pas le `.env` du projet. Chaque problème se règle en une ligne, mais si vous ne connaissez pas ces pièges, vous pourriez croire que « les hooks ne fonctionnent pas ».

</div>

### Piège 1. Un hook avec exit 1 laisse passer l’action sans bruit

C’est le plus dangereux. Pendant une semaine, j’ai cru que mon protecteur de `.env` fonctionnait, mais j’avais en fait utilisé `jq -r '.tool_input.file_path'` dans le hook sans vérifier si la valeur était nulle. Quand `tool_input` était vide, jq renvoyait le code 1. Claude interprétait exit 1 comme « erreur du hook, continuer », et l’action était exécutée. Je ne comprenais pas pourquoi `.env` était toujours modifié. Je n’ai découvert la cause qu’en lançant manuellement le hook dans un terminal avec une entrée vide.

**La solution tient en une règle :** commencez chaque hook par vérifier si la valeur est nulle et terminez-le par `|| exit 0`. Toute sortie inattendue entraîne alors une sortie « sûre », au lieu de laisser l’action passer sans bruit.

```bash
# Safe start and end template:
FILE=$(jq -r '.tool_input.file_path // empty')
[ -z "$FILE" ] && exit 0
# ... your logic ...
exit 0   # or || exit 0 on one line
```

### Piège 2. Les hooks lents sont interrompus par un délai maximal

J’avais un hook qui exécutait `eslint --fix` sur tout le fichier après Edit. Le traitement des fichiers volumineux (1000+ lignes) prenait 8–10 secondes. Le délai maximal par défaut des hooks de Claude Code est de 60 secondes, donc je pensais rester dans la limite. Mais quand plusieurs Edit se produisent en une minute, les hooks s’accumulent et font la queue, et certains sont tout simplement ignorés. Je ne m’en suis rendu compte que lorsque le journal a affiché « hook timed out ».

**Solution :** si un hook nécessite un traitement lourd, exécutez-le en arrière-plan. Lancez `eslint --fix "$FILE" &` sans attendre le résultat. Le hook renvoie alors exit 0 en quelques millisecondes, tandis que le travail réel s’effectue en arrière-plan.

```bash
# Heavy operation in background:
npx eslint --fix "$FILE" &
exit 0
```

### Piège 3. Un hook ne peut pas accéder aux variables d’environnement du projet

Je pensais que `process.env` serait disponible dans un hook comme dans une application Node.js. Ce n’est pas le cas. Le hook s’exécute dans un shell lancé par le processus parent Claude Code et ne voit que ce qui se trouve dans `~/.zshrc` ou `~/.bashrc`, ainsi que ce que Claude Code transmet explicitement. Si votre hook dépend de `OPENAI_API_KEY` dans `.env`, vous devrez lire `.env` manuellement.

**Solution :** lisez explicitement `.env` dans la commande du hook.

```bash
# Passing project env into hook:
cd "$PWD" && source .env && curl -H "Authorization: Bearer $OPENAI_API_KEY" ...
# Or with dotenv:
dotenv -e .env -- node my-validator.js
```

C’est particulièrement pénible dans un monorepo, où différents paquets ont chacun leur propre fichier `.env`. Pour résoudre le problème, transmettez `PWD` et exécutez explicitement `cd "$PWD"` au début de la commande.

## Comment tester les hooks avant de créer un commit ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Un hook est une commande shell. Vous pouvez l’exécuter manuellement dans un terminal avec une entrée stdin fictive, vérifier son code de sortie et stderr, et faire tout cela avant que Claude Code ne l’exécute. Vous économiserez des heures de débogage et éviterez les hooks qui bloquent leur propre fonctionnement.

</div>

**Scénario de test de base.** Prenez la commande du hook dans le JSON, ouvrez un terminal et transmettez-lui le JSON que Claude Code enverrait :

```bash
# Test hook 1 (protect .env):
echo '{"tool_input":{"file_path":".env"}}' | bash -c '
FILE=$(jq -r ".tool_input.file_path // empty")
[ -z "$FILE" ] && exit 0
echo "$FILE" | grep -qE "(^|/)\.env(\.|$)" && {
  echo "BLOCKED" >&2
  exit 2
} || exit 0
'
echo "Exit code: $?"
```

Vous devriez voir `BLOCKED` dans stderr et `Exit code: 2`. Si vous obtenez exit 0, votre regex ne fonctionne pas ; corrigez grep.

**Scénarios à vérifier :**

1.  **Cas nominal** - le fichier doit être bloqué. `.env` pour le hook 1, `git push --force main` pour le hook 3.
2.  **Cas négatif** - le fichier ne doit pas être bloqué. `README.md` pour le hook 1, `git push origin feature-branch` pour le hook 3.
3.  **Cas limites** - entrée vide, valeurs nulles, caractères inattendus. Le hook doit soit renvoyer exit 0, soit échouer avec un message clair, sans laisser l’action passer sans bruit.

**Où placer les hooks expérimentaux ?** Pas dans `.claude/settings.json`, mais dans `.claude/settings.local.json` (hors de Git). Ajoutez-y les nouveaux hooks sous `PostToolUse` avec `echo` à la place de l’action réelle, vérifiez qu’ils se déclenchent, puis déplacez-les dans le `settings.json` de production.

```json
// Experiment in settings.local.json - writes to /tmp/hook-debug.log:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit",
        "hooks": [
          {
            "type": "command",
            "command": "echo \"[hook fired] $(date) $(jq -r '.tool_input.file_path // \"?\"')\" >> /tmp/hook-debug.log"
          }
        ]
      }
    ]
  }
}
```

Après 5–10 modifications dans Claude Code, ouvrez `/tmp/hook-debug.log` pour voir quels fichiers ont déclenché le hook. Si le fichier est vide, le matcher ne convient pas ; corrigez le regex.

## Et maintenant ?

<div class="aside" data-semantic="key-takeaway" role="note" aria-label="Key takeaways">

À retenir

Commencez par des hooks PostToolUse qui renvoient exit 0 ; ce sont les plus sûrs et ils ne bloquent rien. Après une semaine, ajoutez votre premier hook PreToolUse avec exit 2 pour protéger `.env` ou empêcher les force push. Au bout de deux semaines, `CLAUDE.md` passera d’un contrat de 200 lignes à une courte description du contexte de 50–80 lignes, avec certains contrôles d’événements dans les hooks et des limites d’accès strictes appliquées séparément par permissions, sandbox et protections distantes.

</div>

5 étapes pour la semaine à venir :

1.  **Recherchez dans votre `CLAUDE.md`** des mots comme « jamais », « interdit », « toujours » et « doit ». Chacune de ces règles est candidate à un hook. Si une règle formule une obligation, le fichier texte ne la fera pas respecter.
2.  **Commencez par PostToolUse + exit 0.** C’est le plus sûr : ces hooks apportent une modification sans rien bloquer. prettier (hook 2), arrêter le serveur de développement (hook 5), créer automatiquement un commit après les tests (hook 6).
3.  **N’ajoutez votre premier PreToolUse + exit 2 que lorsque vous êtes sûr de vous.** Par exemple, protégez `.env` (hook 1) ou empêchez les force push (hook 3). C’est un blocage strict, et une erreur dans la commande grep peut vous bloquer vous-même.
4.  **Testez les hooks manuellement avant de créer un commit** (voir la section ci-dessus). Vous économiserez des heures de débogage.
5.  **Gardez `settings.local.json` pour les expérimentations.** Ajoutez-y les nouveaux hooks avec echo à la place de l’action réelle.

Une fois vos hooks configurés, l’étape suivante de cette série consiste à découvrir [comment annuler des modifications dans Claude Code](https://ailoot.ai/blog/fr/claude-code-undo-changes/), au cas où un hook laisserait passer une action dangereuse. Si vous voulez que Claude se souvienne de la structure de votre projet entre les sessions, créez un [Second Brain dans Claude Code](https://ailoot.ai/blog/fr/claude-code-second-brain/) (dossier `business/` + base de connaissances liée). Et pour donner des noms aux skills de votre projet afin de les réutiliser, consultez [Claude Code Skills](https://ailoot.ai/blog/fr/claude-code-skills-library/).

<div class="section" role="doc-bibliography" aria-label="Sources">

### Sources

- [Référence des hooks de Claude Code - documentation officielle d’Anthropic](https://code.claude.com/docs/en/hooks)
- [Documentation sur la mémoire de Claude Code - à propos de CLAUDE.md et du prompt système](https://code.claude.com/docs/en/memory)
- [Automatiser les workflows avec des hooks - guide officiel d’Anthropic avec exemples](https://code.claude.com/docs/en/hooks-guide)

</div>





Si vous avez besoin d’accéder à des réseaux neuronaux via Ailoot, découvrez [Ailoot](https://ailoot.ai/).

## Questions fréquentes : Hooks Claude Code : configuration et sept exemples

### Pourquoi un hook doit-il lire son entrée JSON une seule fois ?

Stdin est un flux : le premier jq le consomme et les suivants lisent EOF. Conservez l’entrée une fois puis extrayez commande et résultat de cette copie. Un résultat absent ou inconnu reste non vérifié et doit empêcher le commit automatique.

### Les hooks regex d’exemple créent-ils une barrière de sécurité ?

Non. Ils contrôlent certains événements et chaînes reconnues. Redirection shell, alias, scripts, MCP ou autre écriture peuvent les contourner ; utilisez identifiants restreints, permissions de fichiers, sandbox et branches protégées.


## Ailoot · L’entreprise et son portefeuille de projets

Ailoot s’appuie sur une expérience opérationnelle éprouvée, une large clientèle, un service stable et un suivi continu des KPI.

L’entreprise et son portefeuille de projets : des millions de clients ; des ventes qui se chiffrent en milliards chaque mois.

Ailoot met cette expérience opérationnelle au service de l’accès API aux fonctions d’IA dans les produits des clients, leurs agents, applications et automatisations.
