Mit einem Mod auf Ereignisse reagieren
Behandeln Sie Claude Code-Ereignisse von einem Mod aus: beobachten, schreiben Sie um oder beantworten Sie Tool-Aufrufe, Eingabeaufforderungen und Turns, filtern Sie, welche Ereignisse ein Hook behandelt, und planen Sie für andere Mods.
Ein Hook ist ein Ereignishandler: eine Funktion, die Claude Code ausführt, wenn ein benanntes Ereignis eintritt. Claude Code löst ein Ereignis an jedem Punkt aus, an dem es handeln wird, z. B. wenn es ein Tool ausführt, eine Eingabeaufforderung einreicht, eine Anfrage an das Modell sendet oder eine Sitzung startet oder beendet. Ihr Hook wird ausgeführt, bevor Claude Code handelt, sodass er das Ereignis beobachten, umschreiben oder an Stelle von Claude Code beantworten kann. Sie registrieren einen Hook mit on(eventName, handler).
Erstellen Sie Ihren ersten Mod, bevor Sie hier beginnen. Für jedes Ereignis und seine genauen Felder siehe die Referenz oder lesen Sie die Typen für Ihren Build.
Wie ein Hook ein Ereignis behandelt
Ein Hook sitzt zwischen einem Ereignis und dem, was Claude Code daran tun würde, sodass er das Ereignis beobachten, umschreiben oder selbst beantworten kann. Er empfängt drei Argumente: die Mods-API als $, das Ereignis als e und den nächsten Handler als next. Die Handler für ein Ereignis bilden eine Middleware-Kette. next(e) ruft den nächsten Handler auf, der ein Hook eines anderen Mods oder am Ende der Kette Claude Codes eigenes Verhalten ist, und wird zum Ergebnis aufgelöst. Was Ihr Hook mit next tut, entscheidet, welches der drei er tut.
Ein Ereignis beobachten
Um ein Ereignis zu beobachten, ohne es zu ändern, führen Sie Ihre Arbeit aus und geben Sie next(e) zurück. Dieser Hook protokolliert jedes Tool, das Claude verwenden wird:
on('tool.call', async ($, e, next) => {
// Wird ausgeführt, bevor das Tool ausgeführt wird
$.ui.log('Claude is about to use ' + e.tool)
// Geben Sie das Ereignis unverändert weiter
return next(e)
})
Bevor jedes Tool ausgeführt wird, erscheint eine schwache Zeile wie ● my-mod: Claude is about to use Bash im Transkript, wobei my-mod der Name Ihres Plugins ist. Das Tool wird so ausgeführt, wie es ohne den Mod wäre.
Um nach dem Ereignis zu handeln, await next(e), führen Sie Ihre Arbeit aus und geben Sie das Ergebnis zurück. Dieser Hook protokolliert jedes Tool, nachdem es ausgeführt wurde:
on('tool.call', async ($, e, next) => {
// Lassen Sie das Tool ausführen und warten Sie auf sein Ergebnis
const result = await next(e)
// Wird ausgeführt, nachdem das Tool ausgeführt wurde
$.ui.log(e.tool + ' finished')
// Geben Sie das Ergebnis unverändert zurück
return result
})
Die Zeile erscheint jetzt, nachdem jedes Tool beendet ist. Claude liest das gleiche Ergebnis in beiden Fällen, da der Hook das zurückgibt, zu dem next(e) aufgelöst wurde.
Ein Ereignis umschreiben
Um zu ändern, worauf Claude Code handelt, z. B. den Text einer Eingabeaufforderung, rufen Sie next mit einer geänderten Kopie des Ereignisses auf. Das Ereignis selbst ist unveränderlich: es ist in jeder Tiefe eingefroren, und das Zuweisen zu einem Feld wirft einen Fehler. Dieser Hook schneidet jede Eingabeaufforderung ab, bevor sie gesendet wird:
on('prompt.submit', async ($, e, next) => {
// Geben Sie eine Kopie des Ereignisses mit geändertem Text weiter
return next({ ...e, text: e.text.trim() })
})
Spätere Handler und Claude Code erhalten die gekürzte Eingabeaufforderung und sehen niemals das Original. Sie können auch das Ergebnis ändern: await next(e), dann geben Sie eine Kopie des Ergebnisses mit einem ersetzten Feld zurück.
Ein Ereignis beantworten
Um ein Ereignis selbst zu behandeln, geben Sie ein Ergebnis zurück, ohne next aufzurufen. Das unterbricht die Kette, sodass spätere Mods und Claude Codes eigenes Verhalten nicht ausgeführt werden. Dieser Hook lehnt jeden Bash-Befehl ab:
on('tool.call', { tool: 'Bash' }, async () => {
// Kein Aufruf von next, daher wird der Befehl nie ausgeführt
return { deny: 'Bash is turned off in this project. Use the file tools.' }
})
Wenn Claude einen Bash-Befehl versucht, wird der Befehl nicht ausgeführt, und Claude liest den deny-Text als Ergebnis des Tools. Jedes Ereignis hat seine eigene Ergebnisform, die die Ereignisreferenz auflistet.
Filtern Sie, welche Ereignisse ein Hook behandelt
Um einen Hook nur für einige Ereignisse auszuführen, übergeben Sie einen Filter als zweites Argument an on. Claude Code nennt den Filter einen Matcher. Es ist ein Objekt, dessen Felder mit denen des Ereignisses verglichen werden, und der Hook wird nur ausgeführt, wenn jedes Feld übereinstimmt. Ein Feld kann ein Wert, ein Array zulässiger Werte oder ein regulärer Ausdruck sein.
Jede Zeile in diesem Beispiel registriert die gleiche Funktion, hook, für einen engeren Satz von Tool-Aufrufen:
// Ein String passt zu einem Wert: nur Bash-Aufrufe
on('tool.call', { tool: 'Bash' }, hook)
// Ein Array passt zu jedem Wert darin: Edit-Aufrufe und Write-Aufrufe
on('tool.call', { tool: ['Edit', 'Write'] }, hook)
// Ein regulärer Ausdruck passt nach Muster: jedes Tool eines MCP-Servers
on('tool.call', { tool: /^mcp__github__/ }, hook)
hook wird einmal für einen Bash-, Edit- oder Write-Aufruf ausgeführt und einmal für einen Aufruf zu einem Tool, dessen Name mit mcp__github__ beginnt. Ein Aufruf zu jedem anderen Tool, z. B. Read, passt zu keinem der drei, daher wird hook nicht dafür ausgeführt.
Der Ereignisname kann ein Platzhalter sein. 'classic.*' passt zu jedem Settings-Hook-Ereignis. '*' passt zu jedem Ereignis außer den Telemetrie-Ereignissen, die Sie nach Name oder als 'telemetry.*' hooken.
Registrieren Sie jedes Ereignis einmal pro Matcher. Wenn Sie on zweimal für session.start ohne Matcher aufrufen, schlägt das Modul mit on("session.start") is registered twice without a matcher fehl. Fügen Sie alles, was Ihr Mod beim Sitzungsstart tut, in einen Hook ein.
Hook, was Claude tut
Hooken Sie diese Ereignisse, um einen Tool-Aufruf, eine Eingabeaufforderung oder einen Turn zu sehen oder zu ändern, während er stattfindet. Für jedes Ereignis und was ein Hook zurückgeben kann, siehe die Ereignisreferenz.
Schützen oder ändern Sie einen Tool-Aufruf
Ein tool.call-Hook sieht jedes Tool, das Claude verwenden wird, sodass er den Aufruf ablehnen, seine Argumente ändern oder ihn durchlassen kann. tool.call wird ausgelöst, wenn Claude Code ein Tool ausführen wird, einschließlich Aufrufe, die ein Subagent tätigt, und Aufrufe zu MCP-Tools. e.tool ist der Name des Tools und die Argumente des Tools sind Felder von e, z. B. e.command für Bash. Wenn Sie next(e) aufrufen, führt Claude Code die Berechtigungsprüfung und dann das Tool aus.
Dieser Hook lehnt einen Bash-Befehl ab, der Force-Push durchführt, und teilt Claude mit, warum:
// Der Matcher begrenzt den Hook auf Bash-Aufrufe, daher ist e.command der Shell-Befehl
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
if (/git push .*--force/.test(e.command)) {
// Rückgabe ohne Aufruf von next beantwortet das Ereignis, daher wird der Befehl nie ausgeführt
return { deny: 'Force pushes are not allowed in this repository. Push to a new branch instead.' }
}
// Jeder andere Befehl geht zur Berechtigungsprüfung und dann zu Bash
return next(e)
})
Wenn Claude git push --force versucht, wird der Befehl nicht ausgeführt und es erscheint keine Berechtigungsaufforderung, da der Hook next nie aufruft. Claude liest den deny-Text als Ergebnis des Tools, daher schreiben Sie ihn als Anweisung, auf die Claude reagieren kann. Jeder andere Bash-Befehl wird so ausgeführt, wie er ohne den Mod wäre.
Um nach Ausführung eines Tools zu handeln, await next(e), führen Sie Ihre Arbeit aus und geben Sie das zurück, was next Ihnen gab. Dieser Hook protokolliert jede .mdx-Datei, die Claude ändert, mit $.ui.log, das eine schwache Zeile zum Transkript hinzufügt, die Claude nicht liest:
on('tool.call', { tool: ['Edit', 'Write'] }, async ($, e, next) => {
// Warten Sie auf die Berechtigungsprüfung und das Tool und behalten Sie, was sie produziert haben
const result = await next(e)
// Ein abgelehnter Aufruf kommt als { deny } zurück, und ein fehlgeschlagener hat isError gesetzt
const changed = !result.deny && !result.isError
if (changed && e.file_path.endsWith('.mdx')) $.ui.log('Claude changed ' + e.file_path)
// Geben Sie das Ergebnis so zurück, wie es kam, damit Claude liest, was das Tool zurückgegeben hat
return result
})
Nachdem Claude eine .mdx-Datei bearbeitet oder geschrieben hat, benennt eine schwache Zeile im Transkript die Datei. Nichts wird für eine andere Art von Datei protokolliert oder für einen Aufruf, der abgelehnt oder fehlgeschlagen ist. Claude's Ansicht des Aufrufs ändert sich nicht, da der Hook das Ergebnis zurückgibt, das er erhalten hat.
Um einen Aufruf zu ändern, übergeben Sie geänderte Argumente an next. Um einen Aufruf zu wiederholen, rufen Sie next(e) erneut auf: Ein Hook, der isError beim ersten Ergebnis sieht, kann das Tool ein zweites Mal ausführen und dieses Ergebnis zurückgeben. Um einen Aufruf selbst zu beantworten, geben Sie ein Objekt mit einem result-Feld zurück, z. B. { result: 'Skipped by my-mod' }, ohne next aufzurufen. Wenn Sie das tun, erscheint keine Berechtigungsaufforderung und das Tool wird nicht ausgeführt, daher ist das Ergebnis, das Sie zurückgeben, alles, was Claude über das Geschehene erfährt.
Hooks in den verwalteten Einstellungen Ihrer Organisation werden vor jedem tool.call-Hook eines Mods ausgeführt, und ein Block von einem von ihnen ist endgültig.
Halten Sie einen Tool-Aufruf an, bis der Benutzer entscheidet
Ein Hook kann einen Tool-Aufruf anhalten und den Benutzer fragen, was zu tun ist, bevor er fortfährt. Ein tool.call-Hook kann await durchführen, bevor er next aufruft oder zurückgibt, und der Tool-Aufruf bleibt ausstehend, bis dann. Um die Frage dem Benutzer zu stellen, rufen Sie $.ui.ask auf. Es zeigt Ihre Frage über einer nummerierten Liste Ihrer Optionen in dem Dialog an, den Claude verwendet, um Sie etwas zu fragen, und wird zur Bezeichnung aufgelöst, die der Benutzer auswählt. Nach Ihren Optionen fügt der Dialog eine Zeile zum Eingeben einer anderen Antwort und eine Zeile Chat about this hinzu.
Das RISKY-Muster in diesem Beispiel passt zu rm -r, rm -rf, git reset --hard und git push mit --force und verfehlt andere Schreibweisen wie git push -f. Dieses Modul fragt, bevor es einen Bash-Befehl ausführt, der dem Muster entspricht:
const RISKY = /\brm\s+-rf?\b|\bgit\s+reset\s+--hard\b|\bgit\s+push\b.*--force/
export function register(on) {
on('tool.call', { tool: 'Bash' }, async ($, e, next) => {
// Lassen Sie jeden anderen Befehl ohne Frage durch
if (!RISKY.test(e.command)) return next(e)
// Beginnen Sie mit der sicheren Antwort, damit eine Frage, die niemand beantwortet, den Befehl ablehnt
let answer = 'Refuse'
try {
// Der Tool-Aufruf wartet hier, bis der Benutzer eine der beiden Bezeichnungen auswählt
answer = await $.ui.ask('Run this command? ' + e.command, ['Run it', 'Refuse'])
} catch {
// Der Benutzer hat die Frage verworfen oder dies ist ein claude -p-Lauf mit niemandem zum Fragen
}
if (answer !== 'Run it') {
// Antwort ohne Aufruf von next, daher wird der Befehl nicht ausgeführt
return { deny: 'The user declined this command. Ask before trying a different approach.' }
}
return next(e)
})
}
Wenn Claude einen Befehl wie rm -rf build versucht, erscheint die Frage mit dem Befehl darin, und der Befehl wartet auf die Antwort:
- Der Benutzer wählt Run it: Der Hook ruft
next(e)auf, und die übliche Berechtigungsprüfung wird danach immer noch ausgeführt - Der Benutzer wählt Refuse: Der Befehl wird nicht ausgeführt, und Claude liest den
deny-Text - Der Benutzer gibt eine Antwort ein:
$.ui.askwird zur eingegebenen Antwort aufgelöst. Der Hook vergleicht sie mitRun it, daher lehnt jeder andere Text den Befehl ab. - Niemand antwortet:
$.ui.askwird abgelehnt, wenn der Benutzer die Frage verwirft oder Chat about this auswählt, und in einemclaude -p-Lauf, daher lässt dercatch-Block die Antwort beiRefuse
Halten Sie das Warten in einem Mods-API-Aufruf wie $.ui.ask, da diese Zeit nicht gegen das 10-Sekunden-Zeitlimit des Hooks zählt. Zeit, die mit dem Warten auf ein eigenes Promise verbracht wird, zählt. Claude Code überspringt einen Hook, der das Zeitlimit überschreitet, daher würde der gehaltene Befehl ausgeführt.
Schreiben Sie eine Eingabeaufforderung um oder fügen Sie sie hinzu
Ein prompt.submit-Hook sieht jede Eingabeaufforderung, bevor der Turn beginnt, sodass er den Text umschreiben oder hinzufügen kann. e.text ist das, was eingegeben wurde.
| Um dies zu tun | Geben Sie dies zurück |
|---|---|
| Schreiben Sie die Eingabeaufforderung um. Die Nachricht im Transkript zeigt den neuen Text. | next({ ...e, text: newText }) |
| Fügen Sie Text hinzu, den nur Claude liest, nach der Eingabeaufforderung | next({ ...e, context: [...(e.context ?? []), extraText] }) |
| Verhindern Sie, dass die Eingabeaufforderung gesendet wird | { drop: 'the reason' } |
Dieser Hook fügt den aktuellen Branch-Namen für Claude hinzu, wenn eine Eingabeaufforderung einen Pull Request erwähnt:
on('prompt.submit', async ($, e, next) => {
// Geben Sie eine Eingabeaufforderung weiter, die keinen Pull Request erwähnt, wie sie ist
if (!/\bPR\b|pull request/i.test(e.text)) return next(e)
const git = await $.process.run(['git', 'branch', '--show-current'])
// Außerhalb eines Git-Repositorys schlägt der Befehl fehl, daher gibt es keinen Branch zum Hinzufügen
if (git.exitCode !== 0) return next(e)
// Behalten Sie jeden Kontext, den ein früherer Hook hinzugefügt hat, und fügen Sie eine weitere Zeile für Claude hinzu
return next({ ...e, context: [...(e.context ?? []), 'Current branch: ' + git.stdout.trim()] })
})
Wenn Sie eine Eingabeaufforderung wie open a PR for this change senden, sieht Ihre Nachricht im Transkript gleich aus, und Claude liest auch eine Zeile wie Current branch: feature/auth danach. Eine Eingabeaufforderung, die keinen Pull Request erwähnt, wird unverändert weitergeleitet, und git wird nicht ausgeführt.
Andere Ereignisse behandeln den Rest dessen, was Claude liest: prompt.section für jeden Abschnitt der Systemaufforderung, prompt.context für den Kontext, der mit der ersten Nachricht gesendet wird, und skill.prompt für den Text einer Fähigkeit. Text aus diesen Hooks, der sich zwischen Anfragen ändert, invalidiert den Prompt-Cache.
Folgen Sie einem Turn
Ein Turn ist alles, was Claude als Antwort auf eine Eingabeaufforderung tut. Hooken Sie turn.start, turn.step und turn.complete, um einen zu folgen:
| Ereignis | Wann es ausgelöst wird | Was ein Hook tun kann |
|---|---|---|
turn.start |
Ein Turn beginnt | Beobachten. e.turnId identifiziert den Turn in den anderen beiden Ereignissen. |
turn.step |
Claude Code wird eine Anfrage an das Modell senden. Ein Turn mit Tool-Aufrufen hat mehrere. e.agentId wird für eine Anfrage eines Subagenten gesetzt. |
Lesen Sie die Token-Nutzung jeder Anfrage, senden Sie sie mit next({ ...e, model }) an ein anderes Modell, oder beantworten Sie ohne Aufruf des Modells |
turn.complete |
Der Turn endete, einschließlich eines Turns, den der Benutzer unterbrochen hat, wobei e.isAborted true ist. e.answer ist Claude's endgültiger Text, e.durationMs wie lange es dauerte, und e.usage die Token-Gesamtwerte des Turns. Ein Turn eines Subagenten wird es mit e.agentId gesetzt auslösen. |
Beobachten, oder geben Sie ein Objekt mit einem text-Feld zurück, z. B. { text: 'Done in 12 seconds' }, um eine Zeile unter der Antwort anzuzeigen |
Schreiben Sie einen turn.step-Hook als asynchronen Generator, da das Ereignis streamt. yield* next(e) leitet die Antwort weiter, während sie streamt, und wird zum fertigen Ergebnis ausgewertet. Dieser Hook protokolliert, wie viel von jeder Anfrage die Claude-API aus dem Prompt-Cache bedient hat:
// function* macht den Hook zu einem Generator, der die Antwort Stück für Stück weitergeben kann
on('turn.step', async function* ($, e, next) {
// Senden Sie die Anfrage, leiten Sie jedes Stück weiter, während es ankommt, und behalten Sie das fertige Ergebnis
const result = yield* next(e)
// Überspringen Sie ein Ergebnis, das keine Token-Zählungen meldet
if (result.usage) {
$.ui.log('cache read ' + result.usage.cache_read_input_tokens + ' · wrote ' + result.usage.cache_creation_input_tokens)
}
// Geben Sie das Ergebnis unverändert zurück, damit der Turn wie gewohnt fortfährt
return result
})
Claude's Antwort streamt auf den Bildschirm, wie es ohne den Mod wäre. Nachdem jede Anfrage beendet ist, gibt eine schwache Zeile im Transkript die Anzahl der aus dem Cache gelesenen Token und die Anzahl der darin geschriebenen Token an. Ein Turn mit Tool-Aufrufen hat mehrere Anfragen, daher fügt er mehrere Zeilen hinzu.
result.usage enthält die vier Token-Zählungen, die die Claude-API für eine Anfrage meldet, plus das model, das geantwortet hat: input_tokens, output_tokens, cache_read_input_tokens und cache_creation_input_tokens. Der Hook wird auch für Anfragen von Subagenten ausgeführt, daher überprüfen Sie e.agentId, wenn Sie nur die Hauptkonversation möchten.
Hooken Sie die Settings-Hook-Ereignisse
Settings-Hooks sind die Befehls-, HTTP-, Eingabeaufforderungs- und Agent-Hooks, die Sie in Einstellungsdateien konfigurieren. Jedes Settings-Hook-Ereignis, z. B. Stop, SessionEnd oder PostToolUse, ist auch ein Ereignis mit dem Namen classic. gefolgt vom Namen des Settings-Hook-Ereignisses, z. B. classic.Stop. e ist das JSON, das ein Settings-Hook auf stdin empfängt, einschließlich transcript_path.
Dieser Hook verwendet Stop, das ausgelöst wird, wenn Claude die Antwort beendet, um zu protokollieren, wo das Transkript der Sitzung gespeichert ist:
on('classic.Stop', async ($, e, next) => {
// e hat die gleichen Felder, die ein Stop-Hook in einer Einstellungsdatei von stdin liest
$.ui.log('Transcript saved at ' + e.transcript_path)
// Geben Sie das Ereignis weiter, damit Stop-Hooks in Ihren Einstellungsdateien immer noch ausgeführt werden
return next(e)
})
Jedes Mal, wenn Claude die Antwort beendet, gibt eine schwache Zeile im Transkript den Pfad der Transkriptdatei an. Der Hook gibt next(e) zurück, daher beobachtet er das Ereignis und ändert nichts daran, wie der Turn endet.
Führen Sie neben anderen Mods aus
Mehrere Mods können das gleiche Ereignis hooken, und jeder von ihnen kann fehlschlagen. Wenn Ihr Mod Tool-Aufrufe blockiert, überprüfen Sie seine Position in der Kette und was passiert, wenn sein Hook fehlschlägt.
Die Reihenfolge, in der Mods ausgeführt werden
Hooks auf dem gleichen Ereignis bilden eine Middleware-Kette. Jeder next eines Mods ruft den Hook des folgenden Mods auf, und der letzte next erreicht Claude Codes eigenes Verhalten. Der erste Mod ist am weitesten außen: Er sieht das Ereignis vor den anderen und das Ergebnis nach ihnen, und er entscheidet, ob die anderen überhaupt ausgeführt werden. Ein späterer Mod kann einen früheren nicht daran hindern, ein Ereignis zu sehen.
Claude Code ordnet die Kette danach, woher jeder Mod kommt:
- Der eingebaute Guard
sec-default@builtin, ein in Claude Code eingebauter Mod, den/pluginalscc-plugin-sec-defaultauflistet, wo er lädt, Mods, die Ihre Organisation inprependPluginsauflistet, und dann jeden anderen Mod, der als Mod Ihrer Organisation zählt und nicht inappendPluginsist - Mods, die Sie installieren
- Mods, die Ihre Organisation in
appendPluginsauflistet - Andere in Claude Code eingebaute Mods
Unter den Mods, die Sie installieren, wird ein Mod vor den Mods ausgeführt, die er unter dependencies in seinem Manifest auflistet. Innerhalb eines Moduls werden Hooks in der Reihenfolge ausgeführt, in der register on aufgerufen hat.
Wo Settings-Hooks in der Reihenfolge ausgeführt werden
Die PreToolUse-Hooks, die in Einstellungsdateien konfiguriert sind, werden auch während eines Tool-Aufrufs an festen Punkten in der Kette von Mods ausgeführt:
PreToolUse-Hooks aus verwalteten Einstellungen: werden vor dem Hooktool.calldes ersten Mods ausgeführt, und ein Block von einem von ihnen ist endgültig, daher sieht kein Mod den Aufruf.PreToolUse-Hooks aus jeder anderen Einstellungsdatei und aushooks/hooks.jsonvon Plugins: werden nach dem letzten Aufruf vonnexteines Mods ausgeführt, als Teil von Claude Codes eigenem Verhalten. Ein Mod, dertool.callbeantwortet, ohnenextaufzurufen, hindert sie daran, ausgeführt zu werden, und ein Mod, dernextaufruft, sieht ihre Entscheidung im Ergebnis, das er zurückgibt.
tool.check ist das Ereignis, bei dem Claude Code entscheidet, ob ein Tool-Aufruf ausgeführt werden darf. Es wird nach diesen Hooks und den Berechtigungsregeln ausgelöst, und next(e) wird zu ihrer Entscheidung aufgelöst. Ein Hook auf tool.check kann eine andere Entscheidung zurückgeben, z. B. { decision: 'allow' }, daher kann er einen Aufruf genehmigen, den ein Hook in der zweiten Gruppe blockiert hat. Erweitern Sie Berechtigungen mit Hooks listet auf, welche Entscheidungen einen Mod überlagern.
Behandeln Sie einen Hook, der fehlschlägt
Ein Hook, der fehlschlägt, bricht die Sitzung nicht, und Sie können entscheiden, was stattdessen passiert. Wenn ein Hook ohne .catch-Handler wirft, das Zeitlimit überschreitet oder ein Ergebnis der falschen Form zurückgibt, hängt das, was als nächstes passiert, davon ab, ob er next aufgerufen hatte:
- Es ist fehlgeschlagen, bevor
nextaufgerufen wurde: Claude Code überspringt es, und der nächste Handler wird an seiner Stelle ausgeführt - Es ist fehlgeschlagen, nachdem
nextaufgelöst wurde: Dieses Ergebnis bleibt bestehen, und nichts wird ein zweites Mal ausgeführt
Eine Zeile benennt den Mod, das Ereignis und den Grund, z. B. my-mod: tool.call hook skipped: threw Error: boom. Wo Sie es lesen, hängt von der Sitzung ab, wie Finden Sie heraus, warum ein Mod nichts tut auflistet. Ein ui.render-Hook, dessen Zeichnung nicht validiert, wird anders gemeldet, wie Erstellen Sie einen Baum aus Elementen beschreibt.
Um einen Hook, der Aufrufe blockiert, fehlgeschlagen zu schließen, fügen Sie einen .catch-Fehlerhandler hinzu, der stattdessen antwortet. Hier ist guard Ihre Hook-Funktion:
// on gibt eine Registrierung zurück, und .catch fügt einen Handler an diesen einen Hook an
on('tool.call', { tool: 'Bash' }, guard).catch(async ($, e, next) => {
// next.error.kind ist 'throw' oder 'timeout', was sagt, wie guard fehlgeschlagen ist
return { deny: 'The command guard failed, so this command was not run: ' + next.error.kind }
})
Während guard funktioniert, wird der Handler nie ausgeführt. Wenn guard bei einem Bash-Aufruf wirft oder das Zeitlimit überschreitet, ruft Claude Code den Handler mit dem gleichen Ereignis auf. Der Handler gibt { deny } zurück, daher wird der Befehl nicht ausgeführt, und Claude liest den Text mit throw oder timeout am Ende. Ohne den Handler würde Claude Code guard überspringen und den Befehl ausführen. Der Handler hat eine Sekunde Zeit zum Antworten.
Nächste Schritte
- Verwenden Sie die Mods-API: Fügen Sie Befehle und Tools hinzu, rufen Sie ein Modell auf und führen Sie Arbeit auf einem Timer aus
- Zeichnen Sie in der Schnittstelle: Zeigen Sie, was Ihre Hooks in einem Bereich oder über der Eingabeaufforderung sammeln
- Testen Sie einen Mod: Lösen Sie eines dieser Ereignisse aus einem Test aus
- Mods-Referenz: Jedes Ereignis, jede Mods-API-Methode und die Limits