Automatische Code-Prüfung mit Hooks: Qualität beim Schreiben garantiert

PostToolUse Hooks lösen den Linter nach jedem Schreibvorgang von Claude automatisch aus. Exit-Code 2 meldet Fehler zurück für automatische Korrektur.


Claude Code schreibt Code schnell, aber er vergisst nicht selten, den Linter auszuführen. Sie bitten ihn "dabei gleich zu prüfen" — manchmal macht er's, manchmal nicht. Hooks lösen dieses Problem: Sie verlagern die Verantwortung für die Code-Prüfung von Claude auf das System.

Dieser Artikel konzentriert sich auf eine Sache: PostToolUse Hooks einsetzen, um nach jedem Schreibvorgang automatisch Prüfungen auszulösen, sodass jede Dateiänderung Ihren Standards entspricht.


Der Ansatz: PostToolUse + Dateityp-Filterung

Die Logik eines Code-Prüf-Hooks ist einfach:

  1. Claude ruft Write oder Edit auf, um eine Datei zu schreiben
  2. Der Hook fängt dieses Ereignis ab und holt den Dateipfad
  3. Anhand der Erweiterung wird das passende Prüfwerkzeug bestimmt
  4. Prüfung erfolgreich: Exit-Code 0, Claude macht weiter
  5. Prüfung fehlgeschlagen: Exit-Code 2, Fehler werden an Claude zurückgemeldet

Exit-Code 2 ist entscheidend: Claude empfängt den Fehler und korrigiert ihn automatisch, womit ein Zyklus "schreiben→prüfen→korrigieren" entsteht.


Basiskonfiguration: Einsprachige Projekte

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

Erweiterte Konfiguration: Mehrsprachige Projekte

Die Prüflogik in ein eigenständiges Skript auszulagern ist wartbarer als Befehle im JSON zu stapeln.

.claude/hooks/lint.sh erstellen:

#!/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

Das Skript in settings.json referenzieren:

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

Claude zur automatischen Korrektur bringen

Exit-Code 2 + stderr löst die automatische Korrektur durch Claude aus:

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
}

Dateien ausschließen, die nicht geprüft werden müssen

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

In der Praxis

Nach der Konfiguration sieht der Ablauf so aus:

  1. Sie bitten Claude, eine Ruby-Datei zu schreiben
  2. Claude schreibt sie mit dem Write-Werkzeug
  3. Der Hook führt rubocop -A automatisch aus — findet 3 Probleme
  4. RuboCop behebt 2 automatisch; das 3. erfordert menschliches Urteil
  5. Das 3. wird über stderr an Claude zurückgemeldet
  6. Claude korrigiert den Code, der Hook wird erneut ausgelöst
  7. Zweite Prüfung bestanden — fertig

Sie müssen niemals manuell einen Prüfbefehl ausführen.


Global vs. Projektebene

Global (~/.claude/settings.json): Wenn Sie dieselben Regeln in allen Projekten verwenden.

Projektebene (.claude/settings.json in git): Empfohlen für Teams. Wer das Repository öffnet, erhält automatisch die richtige Konfiguration.

Beide können koexistieren; Projekt-Hooks werden mit globalen zusammengeführt.


Zusammenfassung

Automatische Code-Prüfung mit Hooks läuft auf drei Schritte hinaus:

  1. PostToolUse + Matcher "Write|Edit", um Datei-Schreibvorgänge abzufangen
  2. Den Dateipfad ermitteln und anhand der Erweiterung an den richtigen Linter weiterleiten
  3. Bei Fehler mit Exit-Code 2 beenden, damit Claude den Fehler sieht und korrigiert

Beginnen Sie mit einer Sprache. Sobald es läuft, fügen Sie weitere hinzu. Committen Sie die Skripte in .claude/hooks/ und teilen Sie sie mit dem Team.