Automatyczna weryfikacja kodu z Hooks: jakość gwarantowana podczas pisania

PostToolUse Hooks automatycznie uruchamiają lint po każdym zapisie pliku. Kod wyjścia 2 odsyła błędy do Claude do automatycznej poprawki.


Claude Code pisze kod szybko, ale nie zawsze pamięta o uruchomieniu linta. Prosisz go "sprawdź przy okazji" — raz robi, raz pomija. Hooks rozwiązują ten problem: przenoszą odpowiedzialność za weryfikację kodu z Claude na system.

Ten artykuł skupia się na jednej rzeczy: używaniu PostToolUse Hooks do automatycznego uruchamiania weryfikacji po napisaniu kodu przez Claude, aby każda zmiana w pliku spełniała Twoje standardy.


Podejście: PostToolUse + filtrowanie według typu pliku

Logika Hook weryfikacji kodu jest prosta:

  1. Claude wywołuje Write lub Edit, aby zapisać plik
  2. Hook przechwytuje to zdarzenie i pobiera ścieżkę pliku
  3. Na podstawie rozszerzenia decyduje, które narzędzie uruchomić
  4. Weryfikacja przeszła: kod wyjścia 0, Claude kontynuuje
  5. Weryfikacja nie przeszła: kod wyjścia 2, błędy są zwracane do Claude do poprawki

Kod wyjścia 2 jest kluczowy: Claude otrzymuje błąd i automatycznie go poprawia, tworząc cykl "pisz→sprawdzaj→poprawiaj".


Podstawowa konfiguracja: projekty jednojęzykowe

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

Zaawansowana konfiguracja: projekty wielojęzyczne

Utwórz .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

Odwołaj się do skryptu w settings.json:

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

Automatyczne poprawianie przez Claude

Kod wyjścia 2 + stderr uruchamia automatyczne poprawianie:

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
}

Wykluczanie plików, które nie wymagają weryfikacji

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

W praktyce

Po konfiguracji przepływ pracy wygląda tak:

  1. Prosisz Claude o napisanie pliku Ruby
  2. Claude zapisuje go narzędziem Write
  3. Hook automatycznie uruchamia rubocop -A — znajduje 3 problemy
  4. RuboCop automatycznie poprawia 2; trzeci wymaga ludzkiej oceny
  5. Trzeci błąd jest odsyłany do Claude przez stderr
  6. Claude poprawia kod, Hook uruchamia się ponownie
  7. Druga weryfikacja przeszła — gotowe

Nigdy nie musisz ręcznie uruchamiać żadnego polecenia weryfikacji.


Globalny vs. poziom projektu

Globalny (~/.claude/settings.json): Gdy używasz tych samych reguł we wszystkich projektach.

Poziom projektu (.claude/settings.json w git): Zalecany dla zespołów. Każdy, kto otworzy repozytorium, automatycznie otrzyma właściwą konfigurację.

Oba poziomy mogą współistnieć; hooks projektu łączą się z globalnymi.


Podsumowanie

Automatyczna weryfikacja kodu z Hooks sprowadza się do trzech kroków:

  1. PostToolUse + matcher "Write|Edit" do przechwytywania zapisów plików
  2. Pobranie ścieżki pliku i skierowanie do właściwego lintera wg rozszerzenia
  3. Przy błędzie wyjście z kodem 2, żeby Claude zobaczył błąd i poprawił

Zacznij od jednego języka. Gdy zadziała, dodaj kolejne. Zatwierdź skrypty w .claude/hooks/ i udostępnij je zespołowi.