Praxisprojekt Second Brain · Agenten
Hooks
Lernziele
- Du wählst aus, welche Prüfungen im Vault als HookEin Befehl, den der Harness bei einem festen Ereignis automatisch ausführt, etwa vor oder nach jedem Tool-Aufruf. Ein Hook vor dem Aufruf kann ihn blockieren und dem Modell den Grund melden.Im Glossar laufen und welche nicht.
- Du richtest einen Hook ein, der Frontmatter vor dem Schreiben prüft, und liest an seinem Exit-Code ab, ob er blockiert.
- Du entscheidest für jeden Hook, ob ein Fehler im Hook selbst die Aktion stoppt oder durchlässt.
Ein Hook startet bei einem Ereignis, ein Zeitplan zu einer Uhrzeit. Hooks sind das richtige Werkzeug für alles, was jedes Mal passieren muss. Genau diese Dinge übersieht eine Anweisung im Prompt irgendwann.
Wie ein Hook in Claude Code aufgebaut ist, zeigt die Lektion Die vier Ringe am Beispiel eines Hooks, der git push auf main blockiert. Hier geht es darum, welche Hooks ein Vault braucht.
Was sich lohnt
Prüfen vor dem Schreiben. Der Hook prüft, ob das Frontmatter lesbar ist und die Pflichtfelder enthält. Eine kaputte Seite, die beim Schreiben auffällt, kostet dich nichts. Fällt sie erst einen Monat später auf, war sie seitdem für jede Abfrage unsichtbar.
Lint nach einem Lauf. Kaputte Links entstehen in dem Moment, in dem genug Kontext da ist, um sie richtig zu reparieren. Ein Hook auf das Ereignis Stop läuft, wenn Claude mit seiner Antwort fertig ist.
Commit nach einem geplanten Lauf. Damit gibt es keinen Lauf mehr, der vierzig Dateien geändert und keinen Commit hinterlassen hat. Stop feuert auch nach jeder Antwort in einer normalen Session. Gib diesen Hook deshalb nur dem geplanten Lauf mit, etwa über eine eigene Einstellungsdatei und den Schalter --settings.
Melden beim Löschen. Jede Löschung, jederzeit, wird sichtbar. Ein gepflegter Vault löscht wenig. Eine Meldung kostet also nichts und zählt in dem Moment, in dem sie kommt.
So sieht der Prüf-Hook aus. In .claude/settings.json im Vault meldest du ihn für das Ereignis PreToolUse an. Er läuft vor jedem Aufruf des Werkzeugs Write:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "python3 \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check_frontmatter.py",
"timeout": 5
}
]
}
]
}
}
Das Skript .claude/hooks/check_frontmatter.py bekommt den geplanten Aufruf als JSON. Darin stehen der Pfad und der vollständige Inhalt der Datei. Die Pflichtfelder title, type, created und updated stammen aus dem Seitenvertrag der CLAUDE.md. Skript und Eintrag für die settings.json liegen im Werkzeugkasten des Kurses im Ordner hooks/:
import json, sys
data = json.load(sys.stdin)
path = data["tool_input"].get("file_path", "")
text = data["tool_input"].get("content", "")
if "/wiki/" not in path or not path.endswith(".md") or path.endswith(("/index.md", "/log.md")):
sys.exit(0)
lines = text.split("\n")
if lines[0] != "---" or "---" not in lines[1:]:
print(f"Blocked: {path} has no frontmatter block.", file=sys.stderr)
sys.exit(2)
head = lines[1:lines.index("---", 1)]
keys = {l.split(":", 1)[0] for l in head if ":" in l and not l.startswith(" ")}
missing = [k for k in ("title", "type", "created", "updated") if k not in keys]
if missing:
print(f"Blocked: {path} is missing {', '.join(missing)} in the frontmatter.", file=sys.stderr)
sys.exit(2)
Seiten außerhalb von wiki/, Index und Log lässt das Skript durch. Fehlt der Frontmatter-Block oder ein Pflichtfeld, endet es mit Code 2. Das blockiert den Aufruf, und die Meldung auf stderr geht an Claude. Der Hook prüft nur Write, denn bei Edit steht im Aufruf nur das geänderte Stück. Fehler aus Änderungen findet der Lint.
Wir haben den Hook in einem Test-Vault mit Claude Code 2.1.322 und dem Modell Haiku ausprobiert. Der Auftrag verlangte ausdrücklich eine Seite ohne Frontmatter:
- DuCreate the file wiki/concepts/Testing effect.md containing only the line '# Testing effect'. Do not add anything else. Report what happened.
Was sich nicht lohnt
Alles, was langsam ist. Ein Hook, der bei jedem Schreiben läuft und zwei Sekunden braucht, macht aus einem Import von vierzig Seiten eine Pause von achtzig Sekunden. Du schaltest ihn dann ab.
Alles, was Urteil braucht. Claude Code kennt inzwischen auch Hooks vom Typ prompt und agent, die ein Modell entscheiden lassen. Nach unserer Einschätzung gehört die Frage, ob eine Seite gut genug ist, trotzdem nicht in einen Hook. Ein Hook, der darüber entscheidet, liegt in beide Richtungen daneben, und er blockiert mitten im Lauf. Qualität prüft ein zweiter Agent oder du im Rückblick.
Welcher Hook passt?
Entscheide für jede Prüfung, ob sie als Hook blockieren darf, als Hook nie blockieren soll oder gar kein Hook wird.
Noch offen (6)
- Frontmatter prüfen, bevor eine Wiki-Seite geschrieben wird
- Schreiben in raw/ verhindern
- Eine Meldung schicken, wenn eine Seite gelöscht wird
- Nach dem geplanten Lauf committen
- Beurteilen, ob eine neue Konzeptseite gut genug geschrieben ist
- Bei jedem Schreiben den ganzen Vault linten, zwei Sekunden pro Lauf
Hook, der blockiert (0)
Hook, der nie blockiert (0)
Kein Hook (0)
Schnell und still
Ein Hook sollte deutlich unter einer Sekunde fertig sein und nichts ausgeben, wenn die Prüfung besteht. Hooks, die auch bei Erfolg etwas melden, gewöhnen dich daran, ihre Ausgabe zu übergehen. Am Tag, an dem einer anschlägt, ist das so, als hättest du keine Hooks.
Das Skript oben braucht rund 30 Millisekunden und schweigt bei Erfolg. Bei Code 0 landet die Standardausgabe für die meisten Ereignisse ohnehin nur im Debug-Log. Setz trotzdem ein timeout. Ohne diese Angabe wartet Claude Code bei einem Hook vom Typ command bis zu 600 Sekunden (Stand: Nov. 2026).
Verhalten bei Fehlern
Leg für jeden Hook ausdrücklich fest, ob ein Fehlschlag die Aktion blockiert.
Prüf-Hooks sollen blockieren: Eine Seite, die die Prüfung nicht besteht, soll gar nicht erst geschrieben werden.
Melde-Hooks lassen die Aktion immer durch. Scheitert eine Benachrichtigung, bleibt die Arbeit trotzdem erhalten.
Claude Code unterscheidet dabei zwei Fälle. Blockiert wird nur, wenn der Hook mit Code 2 endet. Stürzt der Hook ab, endet er mit Code 1 oder läuft er in das Zeitlimit, geht die Aktion standardmäßig weiter. Ein Prüf-Hook mit falschem Pfad lässt dann still alles durch. Ab Claude Code 2.1.295 drehst du das mit "onFailure": "block" im Eintrag des Hooks um.
| Hook endet mit | Ohne onFailure |
Mit "onFailure": "block" |
|---|---|---|
| Code 0 | Aktion läuft | Aktion läuft |
| Code 2 | blockiert, stderr geht an Claude | blockiert, stderr geht an Claude |
| Absturz, Code 1, Zeitlimit | Aktion läuft trotzdem | blockiert |
Der schlimme Fall ist ein blockierender Hook, der aus einem fremden Grund scheitert und deinen geplanten Import eine Woche lang still anhält. Setzt du onFailure auf block, reicht dafür ein fehlendes python3 im Suchpfad des Jobs. Was blockiert, muss deshalb so einfach sein, dass es nur aus dem Grund scheitert, für den es da ist. Starte das Skript nach jeder Änderung einmal von Hand mit einer Beispiel-Eingabe.
Merksätze
- Hooks laufen bei Ereignissen und eignen sich für alles, was jedes Mal passieren muss.
- Lohnend sind Prüfung vor dem Schreiben, Lint und Commit nach dem Lauf und eine Meldung beim Löschen.
- Langsame Prüfungen und Urteile über Qualität gehören nicht in einen Hook.
- Ein Hook ist schnell, schweigt bei Erfolg und hat ein
timeout. - Nur Code 2 blockiert. Abstürze lassen die Aktion durch, außer du setzt
"onFailure": "block", und dann muss der Hook sehr einfach sein.
Prüf dich selbst
Denkfrage
Dein Prüf-Hook verweist nach einem Umzug des Vaults auf einen Pfad, den es nicht mehr gibt. Was passiert bei den nächsten Läufen?
Ohne onFailure ist der fehlende Hook ein Fehler, der nicht blockiert. Jede Seite wird geschrieben, auch ohne Frontmatter, und die Prüfung fällt still weg. Mit "onFailure": "block" schlägt dagegen jedes Schreiben im Wiki fehl, bis du den Pfad korrigierst. Im Transkript erscheint in beiden Fällen ein Hinweis auf den Hook-Fehler.
Denkfrage
Warum prüft der Hook nur Write und nicht Edit?
Beim Aufruf von Write steht der ganze neue Inhalt der Datei im JSON, beim Aufruf von Edit nur der ersetzte Text und sein Ersatz. Das Frontmatter einer Seite lässt sich aus einem Edit-Aufruf allein nicht prüfen. Diese Fehler fängt der Lint nach dem Lauf.
Prüf dich selbst
Quellen
- Hooks reference
Ereignisse, Exit-Codes, Standard-Zeitlimit 600 s, onFailure ab 2.1.295, Hook-Typen prompt und agent.
- Run Claude Code programmatically
Zusätzliche Einstellungen per --settings.