← Alle Beitrage

Hooks für die Apple-Entwicklung: Muster, die das Projekt retten

From the guide: Claude Code Comprehensive Guide

Eine Claude Code-Sitzung, die auf ein iOS-Projekt gerichtet ist, hat eine Reichweite, die ein generisches Python- oder Web-Projekt nicht hat. Der Agent kann xcodebuild und xcrun über sein Bash-Tool ausführen. Er kann .pbxproj-Dateien lesen und bearbeiten (standardmäßig eine Property List im alten ASCII-Stil, manchmal nach einer Tooling-Konvertierung in XML oder JSON, und in jedem dieser Formate gleichermaßen fatal, wenn beschädigt). Er hält die Signaturidentitäten des Entwicklers, weil er auf dessen Rechner läuft. Er kann einen Simulator löschen. Er kann ein Projekt mit dem falschen Schema neu erstellen. Er kann committen und pushen. Das Protokoll schützt nichts davon: Das Dateisystem des Entwicklers ist das Dateisystem des Agenten, und das Flag --dangerously-skip-permissions von Claude Code ist nur einen Tastendruck von vollständiger Automatisierung entfernt.

Die Abhilfe ist nicht „dem Agenten vertrauen”. Die Abhilfe sind Hooks: deterministische Shell-Skripte, die der Host an Lebenszyklusgrenzen ausführt (PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop).1 Hooks bremsen den Agenten bei gefährlichen Eingaben ab, validieren destruktive Ausgaben und koppeln den Abschluss an einen grünen Build. Sie sind das tragende Sicherheitsprimitiv, das jeder iOS-Entwickler, der einen Agenten betreibt, konfigurieren sollte, bevor der Agent irgendetwas ausführt.

Vier Hook-Muster verdienen ihren Platz in iOS-Projekten. Sie sind framework-bezogen, nicht projektspezifisch; die Apps des Clusters (Return, Get Bananas, Reps, Water, Ace Citizenship) verwenden allesamt Varianten davon. Jedes Muster benennt einen realen Fehlermodus, ein konkretes Skript und das Lebenszyklusereignis, das den Wirkungsradius begrenzt.

TL;DR

  • Vier Hook-Muster, die in iOS zählen: .pbxproj-Validierung (PostToolUse, gibt Fehler an den Agenten zurück), Gating gefährlicher Bash-Befehle (PreToolUse, blockiert vor der Ausführung), Stop-Gate für grünen Build, Hygiene des Simulator-Zustands (Stop).
  • Hook-Exit-Codes sind wichtig, und sie verhalten sich je Ereignis unterschiedlich. exit 2 blockiert die vorgeschlagene Aktion bei PreToolUse (das Tool wird nie ausgeführt); bei PostToolUse kann nicht blockiert werden (das Tool wurde bereits ausgeführt), aber es gibt stderr an den Agenten zurück, sodass dieser reparieren oder zurücksetzen kann; bei Stop verhindert es, dass der Agent die Sitzung beendet. exit 0 lässt zu. exit 1 protokolliert in der Regel, blockiert aber nicht.1
  • Hook-Skripte liegen in .claude/hooks/*.sh im Repository und werden über einen relativen Pfad in .claude/settings.json referenziert. Code-Review gilt.
  • Die Befugnis des Agenten ist die Befugnis des Entwicklers. Hooks sind die Methode, mit der der Entwickler diese Befugnis in eine bewusste Menge genehmigter Aktionen zurückschneidet.

Muster Eins: .pbxproj-Validierung bei jeder Bearbeitung

Die Xcode-Projektdatei ist die eine Datei, die ein Agent regelmäßig verändert und die das höchste Verhältnis von Wirkungsradius pro Zeile aufweist. Eine falsche Klammer in project.pbxproj bricht den Build für jeden Entwickler im Team stillschweigend. Der Build-Fehler erscheint beim nächsten xcodebuild-Aufruf, nicht im Moment der Bearbeitung, sodass der Agent in der Regel behauptet, die Änderung habe funktioniert, bevor der Schaden zutage tritt.

Der Hook führt plutil -lint gegen jeden .pbxproj-Schreibvorgang aus. PostToolUse kann den Schreibvorgang selbst nicht blockieren (die Datei liegt zum Zeitpunkt der Hook-Ausführung bereits auf der Festplatte), aber exit 2 gibt den Validierungsfehler sofort als Tool-Aufruf-Fehlschlag an den Agenten zurück: Der Agent liest den Fehlschlag, weiß, dass die Datei beschädigt ist, und kann zurücksetzen oder reparieren, bevor die Sitzung weiterläuft:

#!/bin/bash
# .claude/hooks/post-write-pbxproj.sh
# Runs after every Edit or Write tool call. Exits 2 to surface the
# validation failure to the agent so it can revert/repair the broken
# .pbxproj before the session moves on. (PostToolUse cannot prevent
# the write itself; it can only feed the error back.)

INPUT=$(cat)
FILE=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

if [[ "$FILE" != *.pbxproj ]]; then
    exit 0
fi

if ! plutil -lint "$FILE" >/dev/null 2>&1; then
    echo "ERROR: $FILE failed plutil -lint after write" >&2
    echo "The Xcode project file is structurally broken. Revert and try again." >&2
    exit 2
fi

exit 0

plutil -lint erkennt strukturelle Brüche: unausgewogene geschweifte oder runde Klammern, fehlende Semikolons, ungültige Plist-Tokens, fehlerhafte XML-Verschachtelung.2 Es erkennt keine Xcode-semantischen Fehler wie eine fehlerhafte UUID, die zufällig syntaktisch gültiger Plist-Text ist, oder eine Build-Phase, die auf eine fehlende Datei verweist. Diese erzeugen gewöhnliche Build-Fehler, die der Agent normal debuggen kann. Das plutil-Gate fängt die Klasse der katastrophalen Parser-Fehler ab; semantische Fehler fallen durch zum Build selbst.

Die Hook-Konfiguration in .claude/settings.json (beachten Sie das in Anführungszeichen gesetzte $CLAUDE_PROJECT_DIR für Pfade mit Leerzeichen):

{
  "hooks": {
    "PostToolUse": [{
      "matcher": "Write|Edit",
      "hooks": [{
        "type": "command",
        "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/post-write-pbxproj.sh"
      }]
    }]
  }
}

Der Matcher feuert den Hook nur bei Write- und Edit-Tool-Aufrufen; die erste Aktion des Skripts ist ein Kurzschluss bei Nicht-.pbxproj-Pfaden. Die Kosten der Ausführung bei jeder Bearbeitung sind vernachlässigbar, da der Pfadfilter die erste Prüfung ist.

Muster Zwei: Gating destruktiver Bash-Befehle vor der Ausführung

xcrun simctl erase löscht die Daten eines Simulators. xcodebuild archive ruft die Signierung auf und kann signierte Artefakte erzeugen, die der Entwickler nicht beabsichtigt hat. git push --force schreibt die Historie um. Der Agent hat über sein Bash-Tool Zugriff auf alle diese Befehle. Ein PreToolUse-Hook auf Bash gleicht das Muster des vorgeschlagenen Befehls ab und entscheidet, ob die Ausführung fortgesetzt wird.

Die Form:

#!/bin/bash
# .claude/hooks/pre-bash-xcode.sh
# Runs before every Bash tool call. Exits 2 (blocking) on irreversible
# Xcode/signing/git operations the developer hasn't explicitly approved.
# PreToolUse hooks CAN block: exit 2 prevents the tool from running.

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

case "$COMMAND" in
    *"simctl erase"*)
        echo "ERROR: simulator erase requires explicit human approval" >&2
        echo "Tell the developer what you wanted to erase and why; let them run it." >&2
        exit 2
        ;;
    *"xcodebuild archive"*|*"xcodebuild -exportArchive"*)
        echo "ERROR: xcodebuild archive/export invokes signing; requires human approval" >&2
        exit 2
        ;;
    *"git push --force"*|*"git push -f"*)
        echo "ERROR: force-push rewrites history; requires human approval" >&2
        exit 2
        ;;
    *"rm -rf"*)
        echo "ERROR: rm -rf requires explicit approval" >&2
        exit 2
        ;;
esac

exit 0

Der Hook ist ein Switch über Substrings von Befehlsmustern. Die blockierende Klasse ist die der irreversiblen Aktionen: Löschen, Signieren, Force-Push, rekursives Löschen. Reversible Operationen (reguläre Builds, Tests, Git-Commits ohne Force) gehen durch.

Eine gängige Verfeinerung besteht darin, einige Befehle zuzulassen, wenn der Entwickler über eine Umgebungsvariable oder ein Flag im Gespräch ausdrücklich zustimmt. Beispielsweise könnte xcodebuild archive zugelassen werden, wenn CLAUDE_ALLOW_ARCHIVE=1 in der Umgebung gesetzt ist, vom Entwickler vor der Sitzung für eine bestimmte Archivierungsaufgabe gesetzt. Der Hook liest die Umgebungsvariable und umgeht die Blockade:

*"xcodebuild archive"*)
    if [[ "${CLAUDE_ALLOW_ARCHIVE:-0}" == "1" ]]; then
        exit 0
    fi
    echo "ERROR: xcodebuild archive requires CLAUDE_ALLOW_ARCHIVE=1" >&2
    exit 2
    ;;

Das Muster: Default-Deny für die irreversible Klasse, Opt-in-Notausgang für die Fälle, in denen der Entwickler den Agenten handeln lassen möchte.

Muster Drei: Stop-Hook, der den Abschluss an einen grünen Build koppelt

Der Agent neigt dazu, eine Aufgabe für erledigt zu erklären, wenn das Gespräch gelöst aussieht. Ohne Gate kann „erledigt” bedeuten „Ich habe die Dateien bearbeitet und der Chat befindet sich in einem kohärenten Zustand”, anstatt „der Build kompiliert noch”. Der Stop-Hook ist der Ort, an dem die richtige Bedeutung durchgesetzt wird.

#!/bin/bash
# .claude/hooks/stop-build-check.sh
# Runs when the agent tries to stop. Exits 2 if the build is broken,
# which prevents the session from concluding until the agent fixes it.

cd "$CLAUDE_PROJECT_DIR" || exit 0

# Hard-code project, scheme, and destination per repo. Do not rely on
# auto-discovery: workspaces, multiple projects, or shared-vs-user
# schemes all break naive heuristics.
PROJECT="MyApp.xcodeproj"
SCHEME="MyApp"
DESTINATION="platform=iOS Simulator,name=iPhone 17 Pro"

LOG=/tmp/claude-stop-build.log
if ! xcodebuild -project "$PROJECT" -scheme "$SCHEME" \
        -configuration Debug \
        -destination "$DESTINATION" \
        build > "$LOG" 2>&1; then
    echo "ERROR: build failed; cannot stop with a broken build" >&2
    echo "See $LOG for the full xcodebuild output." >&2
    tail -50 "$LOG" >&2
    exit 2
fi

exit 0

Das Hartkodieren von PROJECT, SCHEME und DESTINATION ist die richtige Form für ins Repository eingecheckte Hooks: Die Werte driften nie, Workspaces und Multi-Projekt-Repos funktionieren ohne maschinenspezifische Anpassungen, und ein CI-Build-System, das denselben Hook verwendet, kann das Ziel über eine Umgebungsvariable austauschen. Auto-Discovery (ls *.xcodeproj, xcodebuild -list | awk) funktioniert für den einfachsten Solo-Fall, scheitert aber bei .xcworkspace-basierten Projekten, bei Repositories mit mehreren .xcodeproj-Dateien und bei der Unterscheidung zwischen Shared- und User-Schemas. Die Destination-Zeichenkette folgt der dokumentierten Syntax platform=...,name=... von xcodebuild;3 sie muss ein Simulator sein, den der Rechner des Entwicklers tatsächlich hat, sonst schlägt der Hook aus Umgebungs- und nicht aus Code-Gründen fehl.

Zwei produktbezogene Entscheidungen, die der Hook trifft:

Stop blockiert das „Ich bin fertig”-Signal des Agenten, nicht das des Menschen. Der Entwickler kann immer Strg+C drücken, das Terminal schließen oder überschreiben. Der Hook ist eine Leitplanke gegen den Optimismus des Agenten, kein Lock-in für den Menschen.

Der Hook führt einen echten Build aus, keine Syntaxprüfung. swift build gegen ein iOS-spezifisches Projekt überspringt die iOS-spezifischen Kompilierungsschritte; nur xcodebuild beweist, dass das iOS-Ziel kompiliert. Die Kosten sind die Build-Zeit selbst (10-60 Sekunden bei den meisten Projekten); der Wert besteht darin, den Fall „Build kaputt, aber als erledigt markiert” jedes Mal abzufangen.

Muster Vier: Hygiene des Simulator-Zustands

Nach einer langen Agenten-Sitzung können sich Simulatoren ansammeln: gebootete Simulatoren, die der Agent vergessen hat herunterzufahren, alte App-Installationen, die veralteten Zustand zwischenspeichern, Laufzeitdaten, die über Sitzungen hinweg überleben und nicht reproduzierbare Bugs erzeugen. Ein Stop-Hook kann aufräumen.

#!/bin/bash
# .claude/hooks/stop-simulator-cleanup.sh
# Soft cleanup: shuts down booted simulators we don't need anymore.
# Does NOT erase data; does NOT block. Logs only.

BOOTED=$(xcrun simctl list devices booted 2>/dev/null | grep -E "Booted" | wc -l | xargs)
if (( BOOTED > 0 )); then
    echo "[hook] $BOOTED booted simulators at session end; consider shutdown" >&2
    # Uncomment to auto-shutdown:
    # xcrun simctl shutdown all 2>/dev/null
fi

exit 0

Die Form ist absichtlich nicht blockierend: Der Hook meldet den Zustand, handelt aber nicht, es sei denn, der Entwickler kommentiert die Shutdown-Zeile aus. Der Grund ist, dass die nächste Sitzung des Agenten den gebooteten Simulator möglicherweise erhalten lassen möchte (schnellerer Kaltstart, wenn der Simulator bereits läuft). Die Entscheidung ist je Entwickler: Wenn sich Simulatoren über mehrere Sitzungen hinweg ansammeln und die Kosten real sind, kommentieren Sie den Shutdown aus; ansonsten lassen Sie es als Log-Signal stehen.

Eine aggressivere Variante löscht Simulatoren zwischen Sitzungen, aber das überschreitet die Grenze zum Bereich der destruktiven Operation aus Muster Zwei. Löschen gehört in PreToolUse-Blockierung, nicht in Stop-Automatisierung.

Was Hooks nicht lösen

Die vier obigen Muster sind das Arbeitsset, nicht das gesamte Bild. Drei Klassen von Fehlern, die Hooks nicht abfangen können:

Logikfehler im Code des Agenten. Ein Hook validiert die Struktur, nicht die Semantik. Der Agent kann eine @Model-Klasse schreiben, die kompiliert, den Projektdatei-Lint besteht, grün baut und trotzdem semantisch falsch ist (eine fehlende Migration, eine kaputte Unique-Constraint, eine SwiftData-Beziehung ohne Inverse). Logikkorrektheit lebt in Tests, Code-Reviews und den Augen des Entwicklers; Hooks sind für strukturelle und Lebenszyklus-Belange.

Langsame Drift in der Qualität des Agenten. Jeder einzelne Hook stoppt eine Klasse von Fehlern bei der ersten Begegnung, aber die kumulative Drift über viele Sitzungen (allmählich unsaubererer Code, allmählich schwächere Tests, allmählich veraltete CLAUDE.md-Anweisungen) ist nicht das, was Hooks messen. Das ist ein Sitzungs-Review-Problem, kein Hook-Problem.

Verletzungen der Vertrauensgrenze außerhalb der Tool-Oberfläche des Agenten. Ein Hook auf Bash und Edit deckt den gemeinsamen Pfad ab. Ein Hook auf jedes MCP-Tool, das der Agent aufrufen könnte, erfordert Matcher pro Tool; einige MCP-Server stellen Dutzende oder Hunderte von Tools bereit (XcodeBuildMCP wirbt mit etwa 80) und einen Hook pro Tool zu schreiben ist unpraktikabel. Das richtige Muster dort ist, den MCP-Server-Zugriff einzuschränken (projektweite .mcp.json, Genehmigungsablauf bei der ersten Nutzung), anstatt jedes einzelne Tool zu hooken, und zu akzeptieren, dass der Agent, der seine MCP-Server bedient, Teil seiner sanktionierten Befugnis ist.

Die Beziehung zwischen Hooks und der breiteren Vertrauenshaltung wird in The Repo Shouldn’t Get to Vote on Its Own Trust behandelt: Vertrauen ist eine Lade-Reihenfolge-Invariante, keine nachgelagerte Prüfung. Hooks sind nachgelagerte Wachen für Aktionen, die ein bereits vertrauter Agent ausführt; sie ersetzen nicht die vorgelagerte Entscheidung darüber, ob dem Agenten überhaupt vertraut werden sollte.

Was ich anders bauen würde

Drei Muster, die die Apps des Clusters entweder einsetzen oder einsetzen wollten.

Hook-Skripte unter Versionskontrolle mit dem Rest des Projekts. Die Hook-Skripte liegen in .claude/hooks/*.sh im Repository. Die .claude/settings.json referenziert sie über einen relativen Pfad. Das Team erhält dieselben Sicherheitsnetze über alle Maschinen hinweg, Code-Review gilt für Hook-Änderungen, und das Onboarding eines neuen Entwicklers ist ein git clone statt einer Copy-Paste-Übung. Benutzergebundene Hooks in ~/.claude/settings.json sind die falsche Granularität für projektspezifisches Gating.

Ein SessionStart-Hook, der die aktive Hook-Konfiguration ausgibt. Hooks sind stumm, bis sie auslösen. Ein SessionStart-Hook, der zu Beginn jeder Claude Code-Sitzung läuft und „Aktive Hooks: pbxproj-validation, dangerous-bash-gate, build-check-on-stop” ausgibt, erinnert den Entwickler (und den Agenten), welche Wachen laufen. Die Kosten betragen eine Zeile stderr pro Sitzung; der Wert besteht darin, dass niemand entwickelt, ohne zu wissen, dass das Sicherheitsnetz da ist.

Ein Audit-Log auf Repo-Ebene über die Tool-Aufrufe des Agenten. Ein PostToolUse-Hook, der jeden Tool-Aufruf (mit Zeitstempel, Tool-Name, Argumenten) an eine JSONL-Datei in .claude/logs/ anhängt (gitignored). Das Log beantwortet die Frage „Was hat der Agent in dieser Sitzung getan?” mit einer jq-Abfrage anstatt mit einem Scrollen durch den Chat-Verlauf. Der Hook fügt einige Millisekunden pro Tool-Aufruf hinzu und produziert dauerhafte Audit-Daten, die der Entwickler durchsuchen kann, wenn etwas schief läuft.

Wann Hooks die falsche Antwort sind

Zwei Fälle, in denen die Hook-Schicht der falsche Ort ist, um das Problem zu lösen.

Die MCP-Server des Agenten selbst. Ein schlechter MCP-Server, der das Falsche tut, ist kein Hook-Problem; es ist ein MCP-Server-Problem. Die Lösung besteht darin, einzuschränken, welchen MCP-Servern das Projekt vertraut (.mcp.json-Review, projektgebundene Erst-Nutzungs-Genehmigung) und den Quellcode des Servers zu lesen, falls er offen ist. Ein Hook auf jeden MCP-Tool-Aufruf fügt Overhead hinzu, ohne die Vertrauensfrage zu adressieren.

Agenten, die unbeaufsichtigt laufen. Die vollständige Hook-Haltung setzt voraus, dass ein Entwickler in der Nähe der Sitzung ist und einen fehlgeschlagenen Hook interpretieren kann. Ein Agent, der in CI ohne Mensch in der Schleife läuft, benötigt eine andere Haltung: strengeres MCP-Scoping, engere Tool-Sets, ein anderes Vertrauensmodell. Hooks allein überbrücken nicht den Übergang von beaufsichtigter Entwicklung zu unbeaufsichtigter Automatisierung; diese Lücke ist beabsichtigt.

Was das Muster für iOS-Apps bedeutet, die auf iOS 26+ ausgeliefert werden

Drei Erkenntnisse.

  1. Default-Deny bei irreversiblen Operationen, die strukturellen Wirkungsradius-Dateien validieren, den Abschluss an grüne Builds koppeln. Drei Hook-Lebenszyklusereignisse (PreToolUse, PostToolUse, Stop), vier Muster, die die häufigen iOS-Fehlermodi abdecken. Das kombinierte Set ist klein genug, um an einem Nachmittag geschrieben zu werden, und dauerhaft genug, um jeden bestimmten Agenten oder jedes Modell zu überleben.

  2. Der Exit-Code ist wichtig und unterscheidet sich je Ereignis. exit 2 blockiert die Aktion bei PreToolUse (das Tool wird nie ausgeführt); bei PostToolUse kann es nicht blockieren (das Tool wurde bereits ausgeführt), aber es gibt stderr an den Agenten zurück, sodass der Agent reparieren oder zurücksetzen kann; bei Stop verhindert es, dass der Agent die Sitzung abschließt. exit 1 blockiert bei den meisten Ereignissen nicht. Testen Sie jeden Hook mit einem absichtlich fehlschlagenden Fall, bevor Sie sich darauf verlassen.

  3. Hooks begrenzen Befugnisse. Sie gewähren sie nicht. Die Reichweite des Agenten in den Rechner des Entwicklers ist das, was das Betriebssystem der Terminal-Sitzung des Entwicklers erlaubt. Hooks erlauben es dem Entwickler, bestimmte Aktionen aus dieser Befugnis auszuschneiden und eine ausdrückliche Genehmigung zu verlangen. Der Standard ist, was das Betriebssystem gewährt; das Ziel von Hooks ist es, den Standard kleiner zu machen, nicht größer.

Der vollständige Apple-Ecosystem-Cluster: typisierte App Intents für die Apple-Intelligence-Oberfläche; MCP-Server für die Agenten-Oberfläche; die Routing-Frage zwischen ihnen; Foundation Models für In-App-On-Device-LLM-Funktionen; die Laufzeit-vs.-Tooling-LLM-Unterscheidung; die Synthese der drei Oberflächen; das Single-Source-of-Truth-Muster; Zwei MCP-Server für die Xcode-Integration, die mit diesen Hooks zusammenpasst; Live Activities für die Zustandsmaschine des iOS-Sperrbildschirms; der watchOS-Laufzeit-Vertrag auf der Apple Watch; SwiftUI-Interna für das Framework-Substrat; RealityKits räumliches mentales Modell für visionOS-Szenen; SwiftData-Schema-Disziplin für Persistenz; Liquid-Glass-Muster für die visuelle Schicht; Multi-Plattform-Auslieferung für gerätübergreifende Reichweite. Der Hub befindet sich unter Apple Ecosystem Series. Für einen breiteren Kontext zu iOS mit AI-Agenten siehe den iOS Agent Development guide.

FAQ

Was sind Claude Code-Hooks und warum sind sie für die iOS-Entwicklung wichtig?

Claude Code-Hooks sind deterministische Shell-Skripte, die bei Lebenszyklusereignissen laufen (PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop). Für die iOS-Entwicklung begrenzen sie die Befugnis des Agenten über destruktive Operationen: Simulator-Löschung, Code-Signierung, Mutationen von Projektdateien, Force-Pushes. Ohne Hooks hat der Agent die volle Maschinenbefugnis des Entwicklers; mit Hooks erfordern bestimmte gefährliche Aktionen eine ausdrückliche Genehmigung.

Welche Hook-Ereignisse sollte ein iOS-Entwickler priorisieren?

PreToolUse auf Bash zum Blockieren destruktiver Befehle (simctl erase, xcodebuild archive, git push --force). PostToolUse auf Edit/Write zur Validierung der .pbxproj-Integrität. Stop zum Gating bei einem grünen Build. SessionStart zum Protokollieren der aktiven Hook-Konfiguration. Die vier zusammen fangen die häufigsten iOS-spezifischen Agenten-Fehler ab.

Was ist der Unterschied zwischen den Exit-Codes 0, 1 und 2?

Exit 0 erlaubt die Aktion und fährt fort. Exit 2 verhält sich je Ereignis unterschiedlich: bei PreToolUse blockiert es die vorgeschlagene Aktion (das Tool wird nie ausgeführt); bei PostToolUse kann es nicht blockieren, weil das Tool bereits ausgeführt wurde, aber es gibt stderr an den Agenten zurück, sodass der Agent reparieren oder zurücksetzen kann; bei Stop verhindert es, dass der Agent die Sitzung beendet. Exit 1 protokolliert einen Fehler, blockiert aber bei den meisten Hook-Ereignissen nicht. Für Sicherheitsmuster, die die Aktion tatsächlich vor der Ausführung verhindern müssen, verwenden Sie exit 2 bei PreToolUse. Für die Validierung nach einem destruktiven Schreibvorgang verwenden Sie exit 2 bei PostToolUse, um den Fehlschlag an den Agenten zurückzugeben. Testen Sie jeden Hook mit einer absichtlich fehlschlagenden Eingabe, um zu bestätigen, dass er sich für das spezifische Ereignis wie erwartet verhält.

Wo sollten Hook-Skripte abgelegt werden?

In .claude/hooks/*.sh im Projekt-Root, wobei .claude/settings.json sie über einen relativen Pfad referenziert. Versionskontrolliert und code-reviewt zusammen mit dem Rest des Projekts. Benutzergebundene Hooks in ~/.claude/settings.json funktionieren ebenfalls, sind aber die falsche Granularität für projektspezifisches iOS-Gating.

Ersetzen Hooks die Notwendigkeit eines Code-Reviews?

Nein. Hooks fangen strukturelle Fehler ab (kaputte Projektdateien, gefährliche Bash-Befehle, kaputte Builds), bevor sie ausgeliefert werden. Code-Reviews fangen semantische Fehler ab (Logikfehler, fehlende Migrationen, schwache Tests). Die beiden Schichten ergänzen sich: Hooks machen den Agenten sicherer für den Einsatz im Inner Loop, Code-Reviews halten die Ausgabe des Agenten an der Grenze ehrlich.

Referenzen


  1. Anthropic, “Claude Code reference: Hooks”. Lebenszyklusereignisse (PreToolUse, PostToolUse, UserPromptSubmit, SessionStart, Stop), Matcher-Syntax, Befehlsform und die Rolle der Exit-Codes. Exit 2 verhält sich je Ereignis unterschiedlich: bei PreToolUse und Stop blockiert es die Aktion; bei PostToolUse kann es nicht blockieren (das Tool wurde bereits ausgeführt), aber es gibt stderr an den Agenten zurück. Exit 0 lässt zu; Exit 1 protokolliert in der Regel, blockiert aber nicht. Die Analyse des Autors in When the LLM Lives in Your App vs in Your Tooling deckt die Vertrauenshaltung zwischen Laufzeit und Tooling LLM ab, die Hooks operationalisieren. 

  2. Apple, plutil(1) man page. Das Flag -lint validiert die Property-List-Syntax über das alte ASCII-, XML- und Binärformat hinweg. Es erkennt Brüche auf Parser-Ebene, prüft aber keine Xcode-spezifische Semantik wie Build-Phasen-Referenzen oder UUID-Gültigkeit innerhalb des Projektgraphen. 

  3. Apple Developer, “xcodebuild Destination Specifier” und die xcodebuild-Manpage. Die Syntax -destination 'platform=...,name=...' ist der kanonische Weg, ein Build-Ziel festzunageln; CI-Umgebungen überschreiben den Simulator-Namen über Umgebungsvariablen oder skriptgesteuerte Geräteverfügbarkeitserkennung. 

Verwandte Beiträge

Single Source Of Truth: SwiftData, MCP, iCloud

Three callers can write to the same shopping list: a human, Apple Intelligence, and an external agent. Truth has to live…

18 Min. Lesezeit

Claude Code as Infrastructure

Claude Code is not an IDE feature. It is infrastructure. 84 hooks, 48 skills, 19 agents, and 15,000 lines of orchestrati…

15 Min. Lesezeit