Vérification automatique du code avec les Hooks : qualité garantie à l'écriture

Utilisez PostToolUse Hooks pour déclencher le lint automatiquement après chaque écriture. Le code de sortie 2 renvoie les erreurs à Claude pour correction automatique.


Claude Code écrit du code rapidement, mais il n'exécute pas toujours le lint. Vous lui demandez de "vérifier pendant qu'il écrit" et parfois il le fait, parfois non. Les Hooks résolvent ce problème en transférant la responsabilité de la vérification du code de Claude au système.

Cet article se concentre sur une seule chose : utiliser les PostToolUse Hooks pour déclencher automatiquement des vérifications après que Claude a écrit du code, afin que chaque modification de fichier respecte vos standards.


L'approche : PostToolUse + filtrage par type de fichier

La logique d'un Hook de vérification de code est simple :

  1. Claude appelle Write ou Edit pour écrire un fichier
  2. Le Hook intercepte cet événement et récupère le chemin du fichier
  3. Selon l'extension, il détermine quel outil de vérification exécuter
  4. Vérification réussie : code de sortie 0, Claude continue
  5. Vérification échouée : code de sortie 2, les erreurs sont renvoyées à Claude pour correction

Le code de sortie 2 est essentiel — il permet à Claude de recevoir l'erreur et de la corriger automatiquement, formant une boucle "écrire→vérifier→corriger".


Configuration de base : projets mono-langage

Ruby (RuboCop)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.rb ]] && rubocop -A \"$file\" --no-color -q || true'"
          }
        ]
      }
    ]
  }
}

JavaScript/TypeScript (ESLint)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" =~ \\.(js|ts|jsx|tsx)$ ]] && npx eslint --fix \"$file\" || true'"
          }
        ]
      }
    ]
  }
}

Python (ruff)

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'file=$(echo \"$CLAUDE_TOOL_INPUT\" | jq -r .file_path 2>/dev/null); [[ \"$file\" == *.py ]] && ruff check --fix \"$file\" && ruff format \"$file\" || true'"
          }
        ]
      }
    ]
  }
}

Configuration avancée : projets multi-langages

Déplacer la logique de vérification dans un script indépendant est bien plus maintenable que d'accumuler des commandes dans le JSON.

Créer .claude/hooks/lint.sh :

#!/bin/bash
set -e

input=$(cat)
file=$(echo "$input" | jq -r '.tool_input.file_path // empty')

[[ -z "$file" ]] && exit 0
[[ ! -f "$file" ]] && exit 0

ext="${file##*.}"

case "$ext" in
  rb)    rubocop -A "$file" --no-color -q ;;
  js|ts|jsx|tsx) npx eslint --fix "$file" --quiet ;;
  py)    ruff check --fix "$file"; ruff format "$file" ;;
  go)    gofmt -w "$file"; golangci-lint run "$file" 2>&1 ;;
  *)     exit 0 ;;
esac

Référencer le script dans settings.json :

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [{ "type": "command", "command": "bash .claude/hooks/lint.sh" }]
      }
    ]
  }
}

Faire corriger Claude automatiquement

Code de sortie 2 + stderr est ce qui déclenche la correction automatique de Claude :

run_lint() {
  local output exit_code
  output=$(rubocop "$file" --no-color 2>&1)
  exit_code=$?
  if [[ $exit_code -ne 0 ]]; then
    echo "$output" >&2
    exit 2
  fi
  rubocop -A "$file" --no-color -q
}

Exclure les fichiers qui n'ont pas besoin d'être vérifiés

skip_patterns=("vendor/" "node_modules/" "db/schema.rb" ".min.js" "_test.go")
for pattern in "${skip_patterns[@]}"; do
  [[ "$file" == *"$pattern"* ]] && exit 0
done

Résultat en pratique

Une fois configuré, le flux de travail devient :

  1. Vous demandez à Claude d'écrire un fichier Ruby
  2. Claude l'écrit avec l'outil Write
  3. Le Hook exécute rubocop -A automatiquement — trouve 3 problèmes
  4. RuboCop en corrige 2 automatiquement ; le 3ème nécessite un jugement humain
  5. Le 3ème est renvoyé à Claude via stderr
  6. Claude corrige le code, le Hook se redéclenche
  7. Deuxième vérification réussie — terminé

Vous n'avez jamais besoin d'exécuter une commande de vérification manuellement.


Global vs. niveau projet

Global (~/.claude/settings.json) : Quand vous utilisez les mêmes règles dans tous vos projets.

Niveau projet (.claude/settings.json dans git) : Recommandé pour les équipes. Quiconque ouvre le dépôt obtient automatiquement la bonne configuration.

Les deux coexistent ; les hooks de projet fusionnent avec les globaux et les deux s'exécutent.


Résumé

La vérification automatique du code avec les Hooks se résume à trois étapes :

  1. PostToolUse + matcher "Write|Edit" pour intercepter les écritures de fichiers
  2. Récupérer le chemin du fichier et le router vers le bon linter selon l'extension
  3. En cas d'échec, sortir avec le code 2 pour que Claude voie l'erreur et la corrige

Commencez avec un langage. Une fois fonctionnel, ajoutez-en d'autres. Committez les scripts dans .claude/hooks/ pour les partager avec l'équipe.