IAMaîtriser l'IA générative Plan du corpus
Accueil/Claude Code : les bases/Hooks : automatiser ce qui doit toujours arriver

05 - Hooks : automatiser ce qui doit toujours arriver

Un hook, c'est une commande shell que Claude Code lance automatiquement a un moment precis. Deterministe, pas negociable.

Temps de lecture : 14 min | Niveau : Avance

Ce que tu sauras faire apres

  • Distinguer un hook d'une instruction CLAUDE.md et savoir quand utiliser lequel.
  • Choisir le bon evenement parmi ceux disponibles.
  • Ecrire une configuration de hook correcte, avec matcher et timeout.
  • Formater automatiquement ton code, bloquer une commande dangereuse, etre notifie.
  • Comprendre les risques de securite avant de deployer un hook.

1. Pourquoi les hooks existent

Une instruction dans CLAUDE.md est un conseil. Claude la suit la plupart du temps. Parfois il l'oublie, surtout quand le contexte se remplit.

Un hook est une garantie. Claude Code lance la commande, point.

Tu veuxUtilise
Orienter le comportement de ClaudeCLAUDE.md
Fournir une procedure a la demandeUne skill
Qu'une action arrive a coup sur, sans exceptionUn hook
Bloquer un outil quoi qu'il arriveUne regle deny ou un hook PreToolUse

Exemple concret : "formate le code apres chaque edition". Dans CLAUDE.md, Claude le fera huit fois sur dix. En hook PostToolUse, c'est dix fois sur dix.


2. Les evenements disponibles

Il y a une trentaine d'evenements. Voici ceux que tu utiliseras vraiment, groupes par moment.

Autour d'un appel d'outil

EvenementQuand il se declenche
PreToolUseAvant l'execution d'un appel d'outil
PostToolUseApres qu'un appel d'outil a reussi
PostToolUseFailureApres l'echec d'un appel d'outil
PostToolBatchApres la resolution d'appels d'outils paralleles
PermissionRequestUn outil a besoin d'une decision de permission
PermissionDeniedLe mode auto a refuse un appel d'outil

Autour d'un tour de conversation

EvenementQuand il se declenche
UserPromptSubmitAvant que Claude ne traite ton prompt
StopQuand Claude a fini de repondre
StopFailureLe tour se termine sur une erreur d'API
NotificationClaude Code envoie une notification

Autour de la session

EvenementQuand il se declenche
SessionStartAu debut ou a la reprise d'une session
SessionEndA la fin de la session, utile pour archiver le transcript
SetupPreparation unique : au demarrage avec --init-only, ou avec --init / --maintenance en mode -p
PreCompact / PostCompactAvant / apres la compaction du contexte
InstructionsLoadedUn CLAUDE.md ou des regles viennent d'etre charges
ConfigChangeUn fichier de configuration a change
FileChangedUn fichier surveille a change sur le disque
CwdChangedLe dossier de travail a change

Autour des sous-agents et des worktrees

EvenementQuand il se declenche
SubagentStart / SubagentStopUn sous-agent demarre / se termine
TaskCreated / TaskCompletedUne tache est creee / marquee terminee
WorktreeCreate / WorktreeRemoveUn worktree est cree / supprime

3. Ou mettre la configuration

Un hook vit dans un fichier de reglages, sous la cle hooks.

EmplacementPorteePartageable
~/.claude/settings.jsonTous tes projetsNon, local a ta machine
.claude/settings.jsonUn projetOui, commitable
.claude/settings.local.jsonUn projetNon, gitignore
Reglages geres (managed)Toute l'organisationOui, controle par l'admin
hooks/hooks.json d'un pluginQuand le plugin est actifOui, dans le plugin
Frontmatter d'une skillLe reste de la session une fois la skill invoqueeOui
Frontmatter d'un sous-agentPendant l'execution du sous-agentOui

Pour voir ce qui est configure :

/hooks

Ce menu est en lecture seule. Pour ajouter ou modifier, edite le JSON directement ou demande a Claude de le faire.


4. La structure de configuration

{
  "hooks": {
    "NomDeLEvenement": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "ma-commande",
            "args": ["arg1"],
            "timeout": 30,
            "if": "Bash(rm *)"
          }
        ]
      }
    ]
  }
}

Le champ if est un filtre supplementaire, disponible sur les evenements d'outil. Il prend une regle de permission, par exemple Bash(rm *) ou Edit(*.sql). Le hook ne tourne que si la regle correspond a l'appel. C'est plus precis que le matcher, qui ne regarde que le nom de l'outil.

Le matcher

Valeur du matcherInterprete comme
"*", "", ou absentTout
Seulement lettres, chiffres, _, -, espaces, ,, |Chaine exacte ou liste separee par | ou ,
Autre caractere presentExpression reguliere JavaScript, non ancree

Sur quoi il matche depend de l'evenement :

EvenementMatche surExemples
Evenements d'outilLe nom de l'outilBash, Edit|Write, mcp__.*
SessionStartLe mode de demarragestartup, resume, clear, compact, fork
NotificationLe type de notificationpermission_prompt, idle_prompt, agent_completed
PreCompact / PostCompactLe declencheurmanual, auto

Certains evenements n'acceptent pas de matcher, dont UserPromptSubmit, Stop, PostToolBatch et CwdChanged.

Les types de hook

typeCe qu'il fait
commandLance un executable ou une ligne de shell
httpEnvoie le JSON du hook en POST vers une URL
mcp_toolAppelle un outil sur un serveur MCP connecte
promptEnvoie le prompt et les donnees du hook a un modele Claude (Haiku par defaut)
agentConfie l'evaluation a un agent qui peut lire des fichiers

Forme "exec" contre forme "shell"

Si tu mets args, il n'y a pas de shell : command est l'executable, et chaque element de args est un argument exact. Les caracteres speciaux passent tels quels.

{
  "type": "command",
  "command": "node",
  "args": ["${CLAUDE_PROJECT_DIR}/scripts/format.js", "--fix"]
}

Si tu omets args, la commande passe par un shell : sh -c sur macOS et Linux, Git Bash sur Windows, ou PowerShell. Tu peux forcer avec "shell": "bash" ou "shell": "powershell".

Les variables d'environnement disponibles

VariableContenu
CLAUDE_PROJECT_DIRLa racine du projet
CLAUDE_PLUGIN_ROOTLe dossier d'installation du plugin
CLAUDE_EFFORTLe niveau d'effort courant
CLAUDE_CODE_REMOTEtrue en session web, non defini en local

Les timeouts par defaut

  • command, http, mcp_tool : 600 secondes
  • prompt : 30 secondes
  • agent : 60 secondes

Ces valeurs sont reduites sur certains evenements : 30 s sur UserPromptSubmit, 10 s sur MessageDisplay. Un hook avec "async": true tourne en arriere-plan sans bloquer, et son timeout n'est pas applique.

Les codes de sortie

CodeSignification
0Succes. La sortie JSON est analysee pour des champs de decision
2Erreur bloquante. Sur PreToolUse, l'appel d'outil est bloque
AutreNon bloquant par defaut : l'action continue, une note d'erreur apparait

Sur Stop, un code 2 empeche l'arret et la conversation continue. Sur PostToolUse, l'outil a deja tourne, donc il n'y a rien a bloquer.


5. Trois cas d'usage reels

Cas 1 : formater apres chaque edition

Le plus utile au quotidien. Ici avec ruff pour Python. Dans .claude/settings.json :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs -r ruff format",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Comment ca marche :

  • matcher: "Edit|Write" : ne se declenche qu'apres les outils qui modifient des fichiers.
  • Claude Code envoie le JSON du hook sur l'entree standard de la commande.
  • jq -r '.tool_input.file_path' extrait le chemin du fichier edite.
  • xargs -r ruff format le passe au formateur.

jq n'est pas installe par defaut. C'est l'outil qui lit du JSON en ligne de commande. Deux options sur Windows :

  1. L'installer : winget install jqlang.jq, la commande donnee par la page de telechargement de jq.
  2. Ne pas l'installer, et ecrire le hook en PowerShell avec ConvertFrom-Json, comme au cas 2 ci-dessous.

La doc Claude Code cite brew install jq sur macOS et apt-get install jq sur Debian et Ubuntu, puis renvoie vers la meme page jq.

Quand le hook reussit, Claude Code n'affiche rien dans la conversation. Pour verifier, regarde simplement que le fichier a ete reformate.

Cas 2 : bloquer une commande dangereuse

En data, le risque n'est pas rm -rf mais plutot un bq rm ou un dbt run --target prod.

Etape 1. Cree .claude/hooks/bloque-prod.ps1 :

$callInput = [Console]::In.ReadToEnd() | ConvertFrom-Json
$command = $callInput.tool_input.command

$motifsInterdits = @(
  'bq rm',
  'bq mk --force',
  '--target\s+prod',
  'gcloud .* delete',
  'DROP\s+TABLE'
)

foreach ($motif in $motifsInterdits) {
  if ($command -match $motif) {
    @{
      hookSpecificOutput = @{
        hookEventName = "PreToolUse"
        permissionDecision = "deny"
        permissionDecisionReason = "Commande bloquee par le hook projet : motif '$motif'. Utilise --target dev."
      }
    } | ConvertTo-Json -Depth 5
    exit 0
  }
}

exit 0

Etape 2. Enregistre le hook dans .claude/settings.json :

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": [
              "-NoProfile",
              "-ExecutionPolicy", "Bypass",
              "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/bloque-prod.ps1"
            ],
            "timeout": 15
          }
        ]
      }
    ]
  }
}

La version macOS ou Linux du meme principe, en Bash, sort avec le code 2 et ecrit la raison sur la sortie d'erreur :

#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if echo "$COMMAND" | grep -qE 'bq rm|--target[[:space:]]+prod'; then
  echo "Bloque : commande visant la production. Utilise --target dev." >&2
  exit 2
fi

exit 0

Sur macOS et Linux, n'oublie pas chmod +x .claude/hooks/bloque-prod.sh.

Claude recoit le message de blocage et peut ajuster son approche.

Cas 3 : etre notifie quand Claude t'attend

Tu lances une grosse tache, tu vas faire autre chose, et tu veux savoir quand Claude a besoin de toi. Dans ~/.claude/settings.json, version Windows :

{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe -Command \"[System.Reflection.Assembly]::LoadWithPartialName('System.Windows.Forms'); [System.Windows.Forms.MessageBox]::Show('Claude Code a besoin de ton attention', 'Claude Code')\""
          }
        ]
      }
    ]
  }
}

Cette commande ouvre une boite de dialogue, pas une notification de coin d'ecran. La boite peut s'ouvrir derriere ta fenetre de terminal.

Un matcher vide se declenche sur tous les types de notification. Pour cibler :

MatcherSe declenche quand
permission_promptClaude attend une approbation depuis environ six secondes
idle_promptClaude a fini il y a environ 60 secondes et tu n'as rien tape
auth_successL'authentification vient de reussir
agent_completedUne session d'arriere-plan s'est terminee ou a echoue

6. Exemple de configuration complete et commentee

Voici une configuration .claude/settings.json realiste pour un projet data. Les commentaires sont hors du JSON, parce que Claude Code refuse les commentaires dans un fichier de reglages.

{
  "hooks": {
    "SessionStart": [
      {
        "matcher": "compact",
        "hooks": [
          {
            "type": "command",
            "command": "cat ${CLAUDE_PROJECT_DIR}/.claude/rappel-apres-compaction.md"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell",
        "hooks": [
          {
            "type": "command",
            "command": "powershell.exe",
            "args": ["-NoProfile", "-File", "${CLAUDE_PROJECT_DIR}/.claude/hooks/bloque-prod.ps1"],
            "timeout": 15
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.file_path' | xargs -r ruff format",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

Ligne par ligne :

  • SessionStart avec le matcher compact : apres chaque compaction du contexte, Claude Code reinjecte le contenu d'un fichier de rappel. Le texte ecrit sur la sortie standard est ajoute au contexte de Claude. Tres utile pour que Claude n'oublie pas --target dev apres une compaction.
  • PreToolUse avec le matcher Bash|PowerShell : avant chaque commande shell, le script de garde-fou tourne. Il peut refuser.
  • PostToolUse avec le matcher Edit|Write : apres chaque edition, le formateur passe.

Pour desactiver tous les hooks ponctuellement, mets "disableAllHooks": true dans un fichier de reglages.

Template pret a copier

Colle ce bloc dans .claude/settings.json et remplace uniquement ce qui est entre crochets.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "[TA_COMMANDE_DE_FORMATAGE]",
            "timeout": 30
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash|PowerShell",
        "hooks": [
          {
            "type": "command",
            "command": "[LE_PROGRAMME_QUI_LANCE_TON_SCRIPT]",
            "args": ["[ARGUMENT_1]", "[CHEMIN_DE_TON_SCRIPT_DE_GARDE_FOU]"],
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Exemple rempli, pour un projet Python sous Windows :

  • [TA_COMMANDE_DE_FORMATAGE] devient jq -r '.tool_input.file_path' | xargs -r ruff format
  • [LE_PROGRAMME_QUI_LANCE_TON_SCRIPT] devient powershell.exe
  • [ARGUMENT_1] devient -NoProfile
  • [CHEMIN_DE_TON_SCRIPT_DE_GARDE_FOU] devient ${CLAUDE_PROJECT_DIR}/.claude/hooks/bloque-prod.ps1

Et le template du prompt a donner a Claude pour qu'il ecrive le hook a ta place :

Ecris-moi un hook Claude Code.
EVENEMENT : [PreToolUse / PostToolUse / Notification / SessionStart]
DECLENCHEUR : [QUAND_CA_DOIT_SE_PRODUIRE, par exemple "apres chaque edition d'un .sql"]
ACTION ATTENDUE : [CE_QUE_LA_COMMANDE_DOIT_FAIRE]
SHELL : [powershell / bash]
FICHIER DE REGLAGES : [.claude/settings.json / ~/.claude/settings.json]
Montre-moi le JSON complet, puis explique-moi chaque ligne, puis dis-moi comment le tester
a la main avant de l'activer.

7. Avertissement securite

Un hook execute des commandes shell arbitraires sur ta machine, automatiquement, avec tes droits utilisateur. Tu es entierement responsable de ce que tu configures.

Consequences pratiques :

  1. Ne copie jamais un hook d'Internet sans le lire ligne par ligne. Un hook PostToolUse peut exfiltrer tes fichiers aussi facilement qu'il peut les formater.
  2. Mefie-toi d'un .claude/settings.json dans un depot que tu clones. Les hooks d'un projet ne tournent pas tant que tu n'as pas declare faire confiance a ce dossier. Claude Code te pose la question au premier demarrage. Ne reponds pas oui a l'aveugle : ouvre d'abord le fichier et lis les hooks.
  3. Cite tes variables. Dans un hook shell, $VAR non protege peut casser sur un chemin avec un espace, ou pire, executer autre chose que prevu. Prefere la forme args, qui ne passe pas par un shell.
  4. Ne mets pas de secret en clair dans la commande. Pour un hook http, utilise allowedEnvVars pour n'interpoler que les variables listees.
  5. Un hook PreToolUse qui sort en code 2 bloque avant l'evaluation des permissions. Il est prioritaire meme sur une regle allow. C'est puissant, et c'est dangereux si le script a un bug.
  6. Teste ton hook a part avant de l'activer. Lance le script a la main, avec un JSON d'exemple sur l'entree standard.

Cote administration, une organisation peut mettre "allowManagedHooksOnly": true dans les reglages geres : seuls les hooks geres tournent alors.


A retenir

  • Un hook est deterministe : c'est la difference avec une instruction CLAUDE.md.
  • Les trois evenements les plus utiles : PostToolUse (formater), PreToolUse (bloquer), Notification (etre prevenu).
  • Le matcher filtre par nom d'outil : Edit|Write, Bash, mcp__.*.
  • Le JSON du hook arrive sur l'entree standard ; extrais les champs avec jq ou ConvertFrom-Json.
  • Code de sortie 2 sur PreToolUse = l'appel est bloque, avec la raison renvoyee a Claude.
  • /hooks affiche la configuration, en lecture seule.
  • Un hook lance des commandes arbitraires avec tes droits : lis toujours avant d'activer.
  • Tu n'es pas oblige d'ecrire le JSON toi-meme : donne le template de la section 6 a Claude.

Sources

Liens verifies le 26 septembre 2026.

Corpus personnel de formation · genere le 26/09/2026 · source : 05-hooks.md