Anleitung

Claude Code Hooks: Regeln des Agenten in Garantien verwandeln

Aktualisiert: · Skillaria.Top

Hooks in Claude Code sind Shell-Befehle, die Claude Code selbst bei bestimmten Ereignissen startet: vor und nach einem Werkzeugaufruf, beim Absenden eines Prompts, beim Sitzungsstart und vor dem Beenden des Agenten. Sie werden in der settings.json eingerichtet, und ein Exit-Code 2 blockiert die Aktion und gibt dem Agenten eine Fehlermeldung zurück. So macht der Hook aus einer Regel aus der CLAUDE.md eine Garantie, die das Modell nicht vergessen kann.

Was Hooks in Claude Code sind

Ein Hook ist ein Befehl, den nicht das Modell startet, sondern das Programm Claude Code selbst in einem festgelegten Moment der Arbeit des Agenten. Der Agent will rm -rf ausführen — der Hook läuft und entscheidet, ob der Befehl durchgelassen wird. Der Agent hat eine Datei geändert — der Hook startet einen Formatierer. Der Agent will die Arbeit beenden — der Hook führt die Tests aus und lässt ihn nicht anhalten, solange sie rot sind.

Der entscheidende Unterschied zu Anweisungen in der CLAUDE.md: Eine Anweisung ist Text, den das Modell mit einer gewissen Wahrscheinlichkeit berücksichtigt. Ein Hook ist deterministischer Code. Er läuft immer, unabhängig davon, wie lang der Kontext ist, was der Agent für richtig hält und ob er die Regel nach hundert Minuten Sitzung vergessen hat. Deshalb sind Hooks der wichtigste Weg, einen Wunsch in eine Garantie zu verwandeln.

Warum Regeln in der CLAUDE.md nicht reichen

Eine Regel wie »Führe keine Befehle mit force-push aus« steht in der CLAUDE.md, und trotzdem kann der Agent sie in der dreißigsten Minute übersehen, besonders wenn der Kontext voll ist. Das ist kein Fehler des Modells, sondern eine Eigenschaft langer Sitzungen: Die Regel steht zwar im Kontext, aber sie konkurriert mit allem anderen darin. Eine Anweisung hilft beim Verstehen, sie schützt aber nicht vor Versehen.

Ein Hook hat dieses Problem nicht. Er prüft jeden Befehl oder jede Änderung, bevor sie ausgeführt wird, unabhängig von der Aufmerksamkeit des Modells. Die Regel muss dann nicht mehr dreimal in der CLAUDE.md wiederholt werden — eine Zeile genügt, die auf den Hook verweist.

Ereignisse: Wann Hooks laufen

Jeder Hook ist an ein Ereignis im Lebenszyklus der Sitzung gebunden. Die wichtigsten Ereignisse:

EreignisWann es auslöstTypische Verwendung
PreToolUsevor dem Aufruf eines Werkzeugs (Bash, Edit, Write u. a.)Sperren gefährlicher Befehle, Schutz von Dateien
PostToolUsenach einem erfolgreichen WerkzeugaufrufFormatierer, Linter, schnelle Tests nach einer Änderung
UserPromptSubmitwenn der Benutzer einen Prompt abgeschickt hat, vor der VerarbeitungKontext ergänzen, Prompt mit einem Geheimnis abweisen
Stopwenn der Agent seine Antwort beenden willVerhindern, dass er anhält, solange Prüfungen nicht bestanden sind
SubagentStopwenn ein Subagent seine Arbeit beendetPrüfung des Ergebnisses des Subagenten
SessionStartbeim Start oder Fortsetzen der SitzungZustand laden: git status, Plan, Fortschrittsjournal
SessionEndbeim Beenden der SitzungAufräumen, Schreiben von Protokollen
Notificationwenn Claude Code eine Benachrichtigung sendet (etwa wenn auf eine Freigabe gewartet wird)Ton oder Nachricht aufs Telefon
PreCompactvor der Komprimierung des KontextsWichtiges vor der Komprimierung sichern

Für die Zuverlässigkeit am nützlichsten sind PreToolUse, PostToolUse und Stop: Sie decken die drei Schwachstellen des Agenten ab — gefährliche Aktionen, ungeprüfte Änderungen und ein vorzeitiges »fertig«.

Wo Hooks eingerichtet werden: settings.json

Hooks werden in den Einstellungsdateien von Claude Code beschrieben:

  • ~/.claude/settings.json — Ihre persönlichen Einstellungen für alle Projekte;
  • .claude/settings.json im Repository — gemeinsam für das Team, wird in git committet;
  • .claude/settings.local.json — lokal für das Projekt, landet nicht in git.

Die Struktur ist: Ereignis → Liste von Gruppen mit matcher → Liste von Befehlen.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint.sh",
            "timeout": 60
          }
        ]
      }
    ]
  }
}

matcher ist der Name eines Werkzeugs oder ein regulärer Ausdruck darauf: Bash, Edit|Write, .*. Er wird für Ereignisse benötigt, die mit Werkzeugen zusammenhängen; bei Stop oder UserPromptSubmit kann er entfallen. Die Variable CLAUDE_PROJECT_DIR zeigt auf das Projektverzeichnis, deshalb legen Sie Skripte am besten in .claude/hooks/ ab und rufen sie darüber auf. Die aktuellen Hooks zeigen Sie mit dem Befehl /hooks innerhalb von Claude Code an.

Wie ein Hook Daten bekommt und antwortet

Eingabe: JSON auf stdin

Claude Code übergibt dem Hook ein JSON mit den Angaben zum Ereignis: Sitzungs-ID, Arbeitsverzeichnis, Name des Ereignisses und bei Werkzeugen tool_name und tool_input. Bei Bash steht der Befehl in tool_input.command, bei Edit und Write der Dateipfad in tool_input.file_path. Bequem lesen lässt sich das mit jq.

{
  "session_id": "abc123",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": { "command": "rm -rf build/" },
  "cwd": "/home/dev/project"
}

Ausgabe: Rückgabecode

CodeWas passiert
0Erfolg, die Arbeit geht weiter; bei UserPromptSubmit und SessionStart wird die stdout-Ausgabe in den Kontext des Agenten übernommen
2blockierender Fehler: stderr geht an den Agenten, und die Aktion wird angehalten (bei PreToolUse wird der Werkzeugaufruf abgebrochen, bei Stop arbeitet der Agent weiter)
anderernicht blockierender Fehler: stderr wird dem Benutzer angezeigt, die Arbeit läuft weiter

Der Exit-Code 2 ist der Schlüssel. Der Agent erhält den Text aus stderr als Rückmeldung und kann sich korrigieren: den Befehl umschreiben, den Test reparieren, die Datei unangetastet lassen. Schreiben Sie deshalb in stderr keinen bloßen Fehler, sondern den verständlichen Grund und was stattdessen zu tun ist.

Zusätzlich kann ein Hook in stdout ein JSON mit einer Entscheidung ausgeben, zum Beispiel "decision": "block" und "reason". Das dient der Feinsteuerung; für die meisten Aufgaben reichen Code 2 und eine Meldung in stderr.

Beispiel 1: Linter und Tests nach jeder Änderung

Ein Hook bei PostToolUse mit dem Matcher Edit|Write läuft nach einer Änderung an einer Datei. Das Werkzeug ist schon ausgeführt, die Änderung lässt sich nicht mehr zurücknehmen, aber Code 2 gibt dem Agenten die Ausgabe des Linters zurück, und er behebt den Fehler sofort und nicht eine halbe Stunde später.

#!/usr/bin/env bash
# .claude/hooks/lint.sh
file=$(jq -r '.tool_input.file_path // empty')
case "$file" in
  *.py)
    if ! out=$(ruff check "$file" 2>&1); then
      echo "Der Linter hat Fehler in $file gefunden:" >&2
      echo "$out" >&2
      exit 2
    fi ;;
  *.php)
    if ! out=$(php -l "$file" 2>&1); then
      echo "$out" >&2
      exit 2
    fi ;;
esac
exit 0

Hängen Sie hier keinen vollständigen Testlauf von zehn Minuten an: Der Hook läuft bei jeder Änderung. Nach einer Änderung gehören schnelle Prüfungen hinein (Syntax, Linter, Tests eines Moduls), die schweren Prüfungen in den Hook Stop.

Beispiel 2: Sperren gefährlicher Befehle

Ein Hook bei PreToolUse mit dem Matcher Bash sieht den Befehl, bevor er ausgeführt wird. Code 2 bricht den Aufruf ab, und der Agent erhält eine Erklärung.

#!/usr/bin/env bash
# .claude/hooks/guard.sh
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'rm -rf /|git push (-f|--force)|DROP (TABLE|DATABASE)'; then
  echo "Der Befehl wurde durch die Richtlinie des Projekts blockiert: $cmd. Gefährliche Operationen führt ein Mensch aus." >&2
  exit 2
fi
exit 0

Ähnlich schützen Sie Dateien: Ein Hook bei Edit|Write prüft file_path und blockiert das Schreiben in .env, in Produktionskonfigurationen, in generierten Code oder in bereits angewendete Migrationsdateien. Beachten Sie, dass sich eine Suche per regulärem Ausdruck umgehen lässt — der Agent kann einen Befehl anders schreiben. Der Hook ist eine starke Absicherung, aber kein Ersatz für Zugriffsrechte und eine Sandbox.

Beispiel 3: Der Agent kann nicht »fertig« sagen, solange die Tests rot sind

Der Hook Stop läuft, wenn der Agent beenden will. Sind die Tests nicht bestanden, hindert ihn Code 2 am Anhalten, und die Ausgabe der Tests geht als nächste Aufgabe an den Agenten. Das ist die mechanische Form der Definition of Done.

#!/usr/bin/env bash
# .claude/hooks/done.sh
input=$(cat)
# Schutz vor einer Endlosschleife: der Hook hat die Arbeit bereits einmal verlängert
if [ "$(echo "$input" | jq -r '.stop_hook_active')" = "true" ]; then
  exit 0
fi
if ! out=$(npm test --silent 2>&1); then
  echo "Tests sind nicht bestanden, die Arbeit ist nicht abgeschlossen:" >&2
  echo "$out" | tail -n 40 >&2
  exit 2
fi
exit 0

Das Feld stop_hook_active ist true, wenn der Agent die Arbeit bereits auf Grund eines Stop-Hooks fortsetzt. Ohne diese Prüfung kann der Hook den Agenten endlos im Kreis schicken — genau die Situation, in der der Agent »die ganze Nacht in die falsche Richtung arbeitet«. Bei autonomen Schleifen ergänzen Sie zusätzlich eine Grenze für Versuche und eine Bedingung, bei der ein Mensch hinzugerufen wird.

Beispiel 4: Zustand zu Beginn der Sitzung laden

Bei SessionStart gelangt die stdout-Ausgabe des Hooks in den Kontext. Praktisch ist es, den aktuellen Branch, nicht committete Änderungen und die nächste Aufgabe aus dem Plan auszugeben, damit der Agent nicht bei null anfängt.

#!/usr/bin/env bash
echo "Branch: $(git branch --show-current)"
git status --short | head -n 20
[ -f PROGRESS.md ] && tail -n 15 PROGRESS.md
exit 0

Wie Hooks Regeln in Garantien verwandeln

Die Regeln des Harness lassen sich in drei Stufen einteilen. Die erste ist Text in der CLAUDE.md: Er erklärt, wie zu arbeiten ist, wird aber nur mit Wahrscheinlichkeit befolgt. Die zweite sind ausführbare Prüfungen: Tests, End-to-End-Tests, Architekturregeln als Skript, das bei einem Verstoß fehlschlägt. Die dritte sind Hooks, die diese Prüfungen im richtigen Moment automatisch starten, ohne Zutun des Modells.

Praktische Faustregel: Wenn der Agent dieselbe Regel zweimal verletzt hat, fügen Sie der CLAUDE.md keine weitere Warnung in Großbuchstaben hinzu. Fragen Sie stattdessen, ob sich der Verstoß per Skript prüfen lässt. Wenn ja, schreiben Sie das Skript und hängen Sie es an einen Hook. Dann wird der Textabschnitt kürzer und das Verhalten stabiler.

Regel im TextGarantie über einen Hook
»Formatiere den Code nach dem Standard«PostToolUse startet den Formatierer für die geänderte Datei
»Pushe nicht mit force«PreToolUse bei Bash blockiert den Befehl, exit 2
»Sag nicht fertig, solange die Tests rot sind«Stop startet die Tests und lässt die Arbeit nicht enden
»Bearbeite nicht die .env«PreToolUse bei Edit|Write prüft den Pfad
»Denk daran, wo du aufgehört hast«SessionStart gibt Plan und git status aus

Typische Fehler

  • Code 1 statt 2. Das Skript bricht mit Code 1 ab, der Agent erfährt nichts, und die Aktion läuft durch. Blockieren kann nur Code 2.
  • Meldung auf stdout. Bei Code 2 geht stderr an den Agenten. Schreiben Sie über >&2.
  • Langsamer Hook bei häufigem Ereignis. Ein vollständiger Testlauf in PostToolUse macht jede Änderung zur Minutenaufgabe. Schwere Prüfungen gehören in Stop.
  • Endloses Stop. Ohne Prüfung von stop_hook_active oder ohne Grenze dreht sich der Agent ohne Ende.
  • Relative Pfade. Der Hook läuft im Arbeitsverzeichnis der Sitzung; verwenden Sie $CLAUDE_PROJECT_DIR.
  • Ungeprüfter fremder Hook. Der Hook läuft mit Ihren Rechten und ohne Bestätigung. Lesen Sie Skripte aus fremden Repositories, bevor Sie ihnen vertrauen.

Fazit

Hooks in Claude Code übertragen die wichtigsten Regeln aus dem Text in den Code: PreToolUse hält gefährliche Aktionen an, PostToolUse prüft jede Änderung, Stop verhindert eine vorzeitige Meldung der Fertigstellung, SessionStart gibt dem Agenten das Gedächtnis über das Projekt zurück. Beginnen Sie mit zwei bis drei Hooks für Regeln, die der Agent schon verletzt hat, und formulieren Sie die Meldungen in stderr verständlich. Dann korrigiert der Agent Fehler selbst, und Sie prüfen das Ergebnis statt des Prozesses. Die Validierung, die Prüfung der Fertigstellung und autonome Schleifen behandeln wir im Kurs praktisch.

Häufige Fragen

Muss ich Claude Code nach einer Änderung an den Hooks neu starten?

Änderungen in der settings.json werden am zuverlässigsten in einer neuen Sitzung übernommen; die aktuelle Hook-Konfiguration zeigen Sie mit dem Befehl /hooks an.

Kann ich Hooks nicht in Bash schreiben?

Doch. Ein Hook ist jeder Befehl: ein Skript in Python, Node.js oder ein Binärprogramm. Wichtig ist nur, dass er das JSON von stdin liest und den richtigen Exit-Code zurückgibt.

Sieht der Agent die Ausgabe eines erfolgreichen Hooks?

In der Regel nicht: Bei Code 0 gelangt die Ausgabe nicht in den Kontext. Ausnahmen sind UserPromptSubmit und SessionStart, bei denen stdout in den Kontext des Agenten übernommen wird.

Gelten Hooks auch für Subagenten?

Ja, Werkzeugaufrufe von Subagenten laufen über dieselben Hooks PreToolUse und PostToolUse, für ihr Ende gibt es das eigene Ereignis SubagentStop.

Ist es sicher, Hooks ins Repository zu committen?

Ja, die .claude/settings.json und die Skripte in .claude/hooks/ sind für ein Team übliche Praxis; persönliche Hooks halten Sie in settings.local.json oder in ~/.claude/settings.json.