Verifica automatica del codice con gli Hooks: qualità garantita alla scrittura

Usa PostToolUse Hooks per avviare il lint automaticamente dopo ogni scrittura di Claude. Il codice di uscita 2 restituisce gli errori a Claude per la correzione automatica.


Claude Code scrive codice velocemente, ma non ricorda sempre di eseguire il lint. Gli chiedi di "controllare mentre scrive" e a volte lo fa, a volte no. Gli Hooks risolvono questo problema: spostano la responsabilità della verifica del codice da Claude al sistema.

Questo articolo si concentra su una cosa sola: usare i PostToolUse Hooks per avviare automaticamente le verifiche dopo che Claude ha scritto codice, assicurando che ogni modifica rispetti i tuoi standard.


L'approccio: PostToolUse + filtraggio per tipo di file

La logica di un Hook di verifica del codice è semplice:

  1. Claude chiama Write o Edit per scrivere un file
  2. L'Hook intercetta l'evento e ottiene il percorso del file
  3. In base all'estensione, decide quale strumento di verifica eseguire
  4. Verifica superata: codice di uscita 0, Claude continua
  5. Verifica fallita: codice di uscita 2, gli errori vengono rimandati a Claude per la correzione

Il codice di uscita 2 è fondamentale: fa sì che Claude riceva l'errore e lo corregga automaticamente, formando un ciclo "scrivi→verifica→correggi".


Configurazione di base: progetti monolinguaggio

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'"
          }
        ]
      }
    ]
  }
}

Configurazione avanzata: progetti multilingua

Spostare la logica di verifica in uno script indipendente è più facile da mantenere che accumulare comandi nel JSON.

Crea .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

Riferisci lo script in settings.json:

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

Far correggere Claude automaticamente

Codice di uscita 2 + stderr è ciò che attiva la correzione automatica di 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
}

Escludere i file che non devono essere verificati

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

Risultato in pratica

Con questa configurazione, il flusso di lavoro diventa:

  1. Chiedi a Claude di scrivere un file Ruby
  2. Claude lo scrive con lo strumento Write
  3. L'Hook esegue rubocop -A automaticamente — trova 3 problemi
  4. RuboCop ne corregge 2 automaticamente; il 3° richiede giudizio umano
  5. Il 3° viene rimandato a Claude via stderr
  6. Claude corregge il codice, l'Hook si attiva di nuovo
  7. Seconda verifica superata — completato

Non devi mai eseguire manualmente un comando di verifica.


Globale vs. livello progetto

Globale (~/.claude/settings.json): Quando usi le stesse regole in tutti i progetti.

Livello progetto (.claude/settings.json in git): Consigliato per i team. Chiunque apra il repository ottiene automaticamente la configurazione corretta.

I due coesistono; gli hooks di progetto si uniscono a quelli globali ed entrambi vengono eseguiti.


Riepilogo

La verifica automatica del codice con gli Hooks si riduce a tre passi:

  1. PostToolUse + matcher "Write|Edit" per intercettare le scritture di file
  2. Ottenere il percorso del file e instradarlo al linter giusto per estensione
  3. In caso di fallimento, uscire con codice 2 perché Claude veda l'errore e lo corregga

Inizia con un linguaggio. Una volta funzionante, aggiungine altri. Fai commit degli script in .claude/hooks/ per condividerli con il team.