Zum Inhalt springen

Claude Code Hooks, die still nichts tun

Für CTOs und EntwicklerHarness und Prüfungen · Aktualisiert

Filip PapranecGründer von Digitaliko

Jeder Lauf bestanden. Der Hook tat nichts. Illustration: eine Settings-Datei, in der der Hook-Befehl auf 2>/dev/null || true endet, neben einer Reihe grüner Checks, die nichts beweisen.Jeder Lauf bestanden. Der Hook tat nichts. Illustration: eine Settings-Datei, in der der Hook-Befehl auf 2>/dev/null || true endet, neben einer Reihe grüner Checks, die nichts beweisen.

Teams fügen Claude Code Hooks hinzu, damit KI-Agenten keinen Schaden anrichten, und nehmen dann an, dass sie funktionieren. Unsere bestanden jeden Lauf und taten dabei nichts. Ein Hook, der still nichts tut, ist schlimmer als gar keiner, weil niemand mehr hinschaut. Die Lösung sind vier günstige Checks, die jedes Team übernehmen kann.

Die kurze Antwort: Ein Hook verstummt, wenn seine Ausgabe nirgendwo ankommt, wenn sein Pfad nicht existiert oder wenn sein Exit-Code umgeschrieben wird. Prüfen Sie alle drei in der CI. Und legen Sie den wichtigsten Hook auf das Stop-Ereignis, damit der Agent nicht „fertig“ sagen kann, solange Tests rot sind.

Wie blockieren Claude Code Hooks überhaupt etwas?

Ein Hook ist ein Shell-Befehl, den Claude Code bei einem Ereignis ausführt. PreToolUse läuft vor einem Tool-Aufruf, Stop, wenn der Agent fertig werden will. Der Befehl bekommt das Ereignis als JSON-Objekt auf stdin.

Der Exit-Code entscheidet:

  • Exit 0 lässt durch.
  • Exit 2 blockiert. Was der Hook nach stderr schreibt, geht als Begründung an den Agenten zurück.
  • Jeder andere Code ist ein nicht blockierender Fehler. Claude Code macht weiter.

Das ist der ganze Vertrag. Alles, was bei uns kaputtging, ging innerhalb davon kaputt.

Warum taten unsere Hooks nichts?

Zwei Hooks in unserer Firmenkonfiguration waren eine Woche lang stumm, vom ersten Tag an. Drei Ursachen, jede für sich harmlos:

  • Stumm. Beide schrieben ihre einzige Meldung nach stderr. Der Befehl in den Settings endete auf 2>/dev/null. Der Agent hörte kein Wort.
  • Fehlend. Beide nutzten einen relativen Pfad. In einer Session, die in einem verschachtelten Repository startete, existierte der Pfad nicht. Exit 127, Befehl nicht gefunden.
  • Maskiert. Ein || true am Ende machte aus 127 eine 0. Bei einem blockierenden Ereignis macht es aus einer 2 genauso eine 0. Jeder Lauf „bestand“.
Links der Hook, der schwieg: der Befehl bash tools/hooks/guard.sh 2>/dev/null || true und drei Ursachen: ein relativer Pfad (Exit 127), stderr nach /dev/null und || true, das den Exit-Code zu 0 macht. Rechts ein Stop-Hook in vier Schritten: stop_hook_active, Typecheck, Lint und Tests, bei Rot decision: block mit dem Fehlerlog.Links der Hook, der schwieg: der Befehl bash tools/hooks/guard.sh 2>/dev/null || true und drei Ursachen: ein relativer Pfad (Exit 127), stderr nach /dev/null und || true, das den Exit-Code zu 0 macht. Rechts ein Stop-Hook in vier Schritten: stop_hook_active, Typecheck, Lint und Tests, bei Rot decision: block mit dem Fehlerlog.

Niemand bemerkte es, weil ein stummer Hook genauso aussieht wie ein gesunder. Wir fanden es erst bei einer Analyse unserer eigenen Claude-Code-Sessions.

Das kaputte Muster, vereinfacht:

{ "type": "command", "command": "bash tools/hooks/guard.sh 2>/dev/null || true" }

Das korrigierte, aus unserem öffentlichen Starter:

{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/push-gate.sh" }

Welche vier Checks finden einen stummen Hook?

Wir haben unserem Validator einen Check hinzugefügt, der in der CI unserer Repositories läuft. Vier Greps über .claude/settings.json, nichts wird ausgeführt:

CheckWas er findetFix
Das Skript existiertEinen Pfad, der nicht existiert"$CLAUDE_PROJECT_DIR"/... oder ein absoluter Pfad
Das Skript ist ausführbarEinen Hook, der nur zufällig läuftchmod +x
stderr wird nicht verworfen, wenn das Skript dorthin schreibtEinen stummen Hook2>/dev/null entfernen
Ein PreToolUse-Hook endet nicht auf || trueEinen Block, der zum Durchlass wird|| true entfernen

Wir haben den Check gegen den echten Fehler getestet. Die alte Konfiguration meldete kritische Fehler, die korrigierte bestand.

Nicht jedes || true ist ein Fehler. Unsere Begrüßung läuft auf SessionStart mit 2>/dev/null || true, mit Absicht. Eine Begrüßung darf nie eine Session kaputt machen. Der Check meldet es nur dort, wo ein Block erwartet wird.

Die Lektion in einem Satz, wie sie in unserem Hook gegen KI-Signaturen steht: „A rule is a request. This is the lock.“ Eine Regel ist eine Bitte, ein Hook ist ein Schloss. Und ein Schloss braucht einen eigenen Test.

Vor Kurzem hatte der Agent seine Regeln nur in Regeldateien, und manchmal hielt er sich nicht daran. Also haben wir daraus Checks gemacht, die er nicht umgehen kann.

Filip Papranec
Filip Papranec
Gründer von Digitaliko

Welcher Hook sperrt wirklich?

Der Stop-Hook. Ein Agent, der „fertig“ sagt, während Tests rot sind, kostet einen Senior eine Review-Runde. Ein Stop-Hook zwingt den Agenten, das zuerst zu beheben.

Unsere Version aus dem öffentlichen Starter, leicht gekürzt:

#!/bin/bash
# Stop hook: the agent can't finish while the checks are red.
in=$(cat)
[ "$(printf '%s' "$in" | jq -r '.stop_hook_active // false')" = true ] && exit 0
cd "${CLAUDE_PROJECT_DIR:-.}" 2>/dev/null || exit 0
out=$(GATE_SKIP_INSTALL=1 GATE_SCRIPTS="typecheck lint test" bash scripts/gate.sh checks 2>&1) && exit 0
jq -n --arg r "Checks are red. Fix them before you say the task is done:
$out" '{decision: "block", reason: $r}'

Schritt für Schritt macht er Folgendes:

  1. Liest stop_hook_active

    Ist es true, macht der Agent bereits wegen dieses Hooks weiter. Der Hook lässt ihn durch, damit er sich nicht endlos dreht.

  2. Führt die Checks aus

    Typecheck, Lint und Tests, dieselben wie in der CI.

  3. Blockiert mit Begründung

    Sind die Checks rot, gibt er decision: block mit dem Fehlerlog als Begründung zurück.

  4. Der Agent behebt die Ursache

    Er liest das Log, behebt den Fehler und versucht erneut, fertig zu werden.

Er blockiert mit JSON, nicht mit einem Exit-Code. Der Agent arbeitet weiter, und die Begründung, die er bekommt, ist genau das, was fehlschlug.

Er ist eine Frühwarnung, nicht das Gate. In unserem Agenten-Loop läuft der maßgebliche Check nach der Session noch einmal, in Code, den der Agent nicht anfassen kann. Der Hook spart nur eine Runde.

Was ging in unserem eigenen Stop-Hook kaputt?

Der Stop-Hook unseres Loops hatte einen eigenen stillen Fehler. Er setzte einen festen, minimalen PATH ohne die Werkzeugverzeichnisse des Benutzers, sodass in Repositories mit Bun die Werkzeuge fehlten, die die Checks brauchen. Außerdem suchte er das Gate an einem festen Ort, den es nur auf der Maschine des Agenten gab. Ein PR behob beides: der volle PATH, und das Gate wird relativ zum Hook selbst gefunden.

Claude Code Hooks im Team: wo anfangen?

Gehen Sie durch, was Ihre Repositories davon schon haben. Jeder Punkt sind ein paar Zeilen in den Settings oder in der CI:

Am leichtesten übersieht man den dritten Punkt. Unser Hook gegen KI-Signaturen liest seine Eingabe mit jq, weicht dann auf python3 und danach auf sed aus. Ein fehlendes jq darf das Schloss nie in ein stilles Erlauben verwandeln. Relative Pfade wiederum brechen in verschachtelten Repositories und Worktrees.

Starten können Sie mit dem Claude Code Production Starter: ein Push-Gate, ein Secret-Scan und ein Stop-Hook, MIT-Lizenz.

Wo Hooks neben Regeldateien stehen, erklärt Regeln für KI-Code im Team. Wie man das im ganzen Team einführt, steht in Wie führt man KI im Entwicklungsteam ein?. Was der Agent ohne Rückfrage darf und wann Skip Permissions sicher ist, steht in Claude Code permissions.

Möchten Sie Hooks, die in Ihren Repositories halten?

Wir richten Hooks, Gates und CI direkt im Repository eines Teams ein im Rahmen von AI-native SDLC für Entwicklerteams. Fehlen Ihrem Team eher Hände als Regeln? Sehen Sie sich einen Senior-Entwickler für Ihr Team an. KI schreibt heute unseren gesamten Code, und ein Senior steht für das Ergebnis gerade. Seit 2023 haben wir 15+ Produktionsprojekte geliefert.

Wir melden uns innerhalb von zwei Arbeitstagen und wählen gemeinsam, was wir zuerst lösen.

Wenn Sie lieber zuerst sprechen möchten, buchen Sie ein 15-Minuten-Meeting mit Filip.

Wann haben Sie zuletzt geprüft, ob Ihre Hooks noch blockieren?

Kurz gesagt

  • Ein Hook verstummt, wenn sein stderr verworfen wird, sein Pfad nicht existiert oder || true seinen Exit-Code umschreibt.
  • Vier Greps über .claude/settings.json in der CI finden alle drei.
  • Ein Stop-Hook blockiert „fertig“, solange Tests rot sind, und blockiert nur einmal.
  • Der maßgebliche Check läuft noch einmal in der CI, wo der Agent ihn nicht ändern kann.

Häufige Fragen

Was sind Claude Code Hooks?

Shell-Befehle, die Claude Code bei Ereignissen ausführt, etwa PreToolUse vor einem Tool-Aufruf oder Stop, wenn der Agent fertig werden will. Ein Hook bekommt das Ereignis als JSON auf stdin und entscheidet mit seinem Exit-Code oder einer JSON-Antwort.

Wie blockiert ein Claude Code Hook einen Befehl?

Mit Exit-Code 2. Claude Code blockiert dann die Aktion und gibt das, was der Hook nach stderr geschrieben hat, als Begründung an den Agenten zurück. Exit 0 lässt sie durch. Jeder andere Code ist ein nicht blockierender Fehler, und der Agent macht weiter.

Warum läuft mein Claude Code Hook nicht?

Meist wegen eines relativen Pfads, der im Session-Verzeichnis nicht existiert, wegen stderr, das mit 2>/dev/null verworfen wird, oder wegen eines || true am Ende, das den Exit-Code umschreibt. Beginnen Sie den Pfad mit "$CLAUDE_PROJECT_DIR" und prüfen Sie die Hook-Konfiguration in der CI.

Was bedeutet stop_hook_active?

Es ist true, wenn der Agent bereits weitermacht, weil ein Stop-Hook ihn blockiert hat. Ist es true, beenden Sie sofort. Sonst blockiert ihn ein Check, den er nicht beheben kann, endlos.

Reicht ein Stop-Hook als Quality Gate?

Nein. Er ist eine Frühwarnung innerhalb der Session. Führen Sie dieselben Checks noch einmal in der CI und im Branch-Schutz aus, wo der Agent sie nicht ändern kann.

Fragen Sie eine KI zu diesem Artikel

Die KI nutzt dabei auch unsere anderen Artikel und Erfahrung aus echten Projekten.

ChatGPTClaudePerplexity
Engineering

Weitere Artikel

Harness und Prüfungen

Playwright MCP: Der KI-Agent beweist es im Browser

Playwright MCP gibt einem KI-Agenten einen echten Browser. Wie wir ihn nutzen, damit der Agent seine eigene Änderung in der laufenden App prüft und PASS oder FAIL mit Screenshots zurückgibt, was das findet, was Unit-Tests übersehen, und wann einfache Playwright-Skripte besser sind.

Harness und Prüfungen

LLM as a Judge: KI-Reviewer, der die Arbeit widerlegen soll

LLM as a Judge heißt: Ein Sprachmodell bewertet die Arbeit eines anderen Modells anhand schriftlicher Kriterien. Wie wir das bei jedem Pull Request unseres KI-Agenten einsetzen: zuerst maschinelle Checks, dann eine frische Session nur mit Lesezugriff, die widerlegen soll, ein typisiertes Urteil und die Regeln, die nach seinen Fehlern dazukamen.

Harness und Prüfungen

KI-Code-Review mit Claude Code in CI: was es übersieht

Wie wir Claude Code Review auf jedem Pull Request mit einer wiederverwendbaren GitHub Action betreiben: was der KI-Reviewer suchen soll, wie er grün blieb und schwieg, was er übersieht und warum nur ein Mensch freigibt.

Zeit, mit dem Planen aufzuhören und das Problem zu lösen.

Kontakt

  • Digitaliko s.r.o.
    Šípková 377/17
    958 03 Malé Uherce, Slowakei
  • Melden Sie sich bei uns
    info@digitaliko.com
    +421 950 595 506
    WhatsApp