on(eventName, handler).
Construye tu primer mod antes de empezar aquí. Para cada evento y sus campos exactos, consulta la referencia o lee los tipos para tu compilación.
Cómo un hook maneja un evento
Un hook se sitúa entre un evento y lo que Claude Code haría al respecto, por lo que puede observar el evento, reescribirlo o responderlo él mismo. Recibe tres argumentos: la API de mods como$, el evento como e, y el siguiente manejador como next. Los manejadores de un evento forman una cadena de middleware. next(e) llama al siguiente manejador, que es el hook de otro mod o, al final de la cadena, el comportamiento propio de Claude Code, y se resuelve al resultado. Lo que tu hook hace con next decide cuál de los tres hace.
Observar un evento
Para observar un evento sin cambiarlo, haz tu trabajo y devuelvenext(e). Este hook registra cada herramienta que Claude está a punto de usar:
● my-mod: Claude is about to use Bash en la transcripción, donde my-mod es el nombre de tu plugin. La herramienta se ejecuta como lo haría sin el mod.
Para actuar después del evento, await next(e), haz tu trabajo y devuelve el resultado. Este hook registra cada herramienta después de que se ha ejecutado:
next(e) se resolvió.
Reescribir un evento
Para cambiar lo que Claude Code actúa, como el texto de una indicación, llama anext con una copia modificada del evento. El evento en sí es inmutable: está congelado en cada profundidad, y asignar a un campo lanza una excepción. Este hook recorta cada indicación antes de que se envíe:
await next(e), luego devuelve una copia del resultado con un campo reemplazado.
Responder un evento
Para manejar un evento tú mismo, devuelve un resultado sin llamar anext. Eso cortocircuita la cadena, por lo que los mods posteriores y el comportamiento propio de Claude Code no se ejecutan. Este hook rechaza cada comando Bash:
deny como el resultado de la herramienta. Cada evento tiene su propia forma de resultado, que la referencia de eventos enumera.
Filtrar qué eventos maneja un hook
Para ejecutar un hook solo para algunos eventos, pasa un filtro como segundo argumento aon. Claude Code llama al filtro un matcher. Es un objeto cuyos campos se comparan con los del evento, y el hook se ejecuta solo cuando cada campo coincide. Un campo puede ser un valor, una matriz de valores permitidos o una expresión regular.
Cada línea en este ejemplo registra la misma función, hook, para un conjunto más estrecho de llamadas de herramientas:
hook se ejecuta una vez para una llamada Bash, Edit o Write, y una vez para una llamada a una herramienta cuyo nombre comienza con mcp__github__. Una llamada a cualquier otra herramienta, como Read, no coincide con ninguna de las tres, por lo que hook no se ejecuta para ella.
El nombre del evento puede ser un comodín. 'classic.*' coincide con cada evento de hook de configuración. '*' coincide con cada evento excepto los eventos de telemetría, que enganchas por nombre o como 'telemetry.*'.
Registra cada evento una vez por matcher. Si llamas a on dos veces para session.start sin un matcher, el módulo falla al cargar con on("session.start") is registered twice without a matcher. Pon todo lo que tu mod hace al inicio de la sesión en un hook.
Enganchar lo que Claude está haciendo
Engancha estos eventos para ver o cambiar una llamada de herramienta, una indicación o un turno mientras sucede. Para cada evento y lo que un hook puede devolver, consulta la referencia de eventos.Guardar o cambiar una llamada de herramienta
Un hooktool.call ve cada herramienta que Claude está a punto de usar, por lo que puede rechazar la llamada, cambiar sus argumentos o dejarla pasar. tool.call se dispara cuando Claude Code está a punto de ejecutar una herramienta, incluidas las llamadas que hacen los subagentes y las llamadas a herramientas MCP. e.tool es el nombre de la herramienta y los argumentos de la herramienta son campos de e, como e.command para Bash. Cuando llamas a next(e), Claude Code ejecuta la verificación de permisos y luego la herramienta.
Este hook rechaza un comando Bash que hace un push forzado y le dice a Claude por qué:
git push --force, el comando no se ejecuta y no aparece ningún aviso de permiso, porque el hook nunca llama a next. Claude lee el texto deny como el resultado de la herramienta, así que escríbelo como una instrucción en la que Claude pueda actuar. Cada otro comando Bash se ejecuta como lo haría sin el mod.
Para actuar después de que una herramienta se ha ejecutado, await next(e), haz tu trabajo y devuelve lo que next te dio. Este hook registra cada archivo .mdx que Claude cambia, con $.ui.log, que añade una línea atenuada a la transcripción que Claude no lee:
.mdx, una línea atenuada en la transcripción nombra el archivo. Nada se registra para otro tipo de archivo, o para una llamada que fue rechazada o falló. La vista de Claude de la llamada no cambia, porque el hook devuelve el resultado que recibió.
Para cambiar una llamada, pasa argumentos cambiados a next. Para reintentar una llamada, llama a next(e) de nuevo: un hook que ve isError en el primer resultado puede ejecutar la herramienta una segunda vez y devolver ese resultado. Para responder una llamada tú mismo, devuelve un objeto con un campo result, como { result: 'Skipped by my-mod' }, sin llamar a next. Cuando haces eso, no aparece ningún aviso de permiso y la herramienta no se ejecuta, por lo que el resultado que devuelves es todo lo que Claude aprende sobre lo que sucedió.
Los hooks en la configuración administrada de tu organización se ejecutan antes que el hook tool.call de cualquier mod, y un bloqueo de uno de ellos es final.
Mantener una llamada de herramienta hasta que el usuario decida
Un hook puede pausar una llamada de herramienta y preguntarle al usuario qué hacer antes de que continúe. Un hooktool.call puede await antes de llamar a next o devolver, y la llamada de herramienta permanece pendiente hasta entonces. Para hacer la pregunta al usuario, llama a $.ui.ask. Muestra tu pregunta encima de una lista numerada de tus opciones, en el diálogo que Claude usa para preguntarte algo, y se resuelve a la etiqueta que el usuario elige. Después de tus opciones, el diálogo añade una fila para escribir una respuesta diferente y una fila Chat about this.
El patrón RISKY en este ejemplo coincide con rm -r, rm -rf, git reset --hard y git push con --force, y se pierde otras ortografías como git push -f. Este módulo pregunta antes de ejecutar un comando Bash que coincida con el patrón:
rm -rf build, la pregunta aparece con el comando en ella, y el comando espera la respuesta:
- El usuario elige Run it: el hook llama a
next(e), y la verificación de permisos habitual aún se ejecuta después - El usuario elige Refuse: el comando no se ejecuta y Claude lee el texto
deny - El usuario escribe una respuesta:
$.ui.askse resuelve al texto escrito. El hook lo compara conRun it, por lo que cualquier otro texto rechaza el comando. - Nadie responde:
$.ui.askrechaza cuando el usuario descarta la pregunta o elige Chat about this, y en una ejecución declaude -p, por lo que el bloquecatchdeja la respuesta enRefuse
$.ui.ask, porque ese tiempo no cuenta contra el límite de tiempo de 10 segundos del hook. El tiempo dedicado a esperar una promesa propia sí cuenta. Claude Code omite un hook que agota el tiempo, por lo que el comando retenido se ejecutaría.
Reescribir o añadir a una indicación
Un hookprompt.submit ve cada indicación antes de que comience el turno, por lo que puede reescribir el texto o añadirle. e.text es lo que se escribió.
Este hook añade el nombre de la rama actual para Claude siempre que una indicación menciona una solicitud de extracción:
open a PR for this change, tu mensaje se ve igual en la transcripción, y Claude también lee una línea como Current branch: feature/auth después de ella. Una indicación que no menciona una solicitud de extracción pasa sin cambios, y git no se ejecuta.
Otros eventos cubren el resto de lo que Claude lee: prompt.section para cada sección de la indicación del sistema, prompt.context para el contexto enviado con el primer mensaje, y skill.prompt para el texto de una skill. El texto de estos hooks que cambia entre solicitudes invalida el caché de indicaciones.
Seguir un turno
Un turno es todo lo que Claude hace en respuesta a una indicación. Enganchaturn.start, turn.step y turn.complete para seguir uno:
Escribe un hook
turn.step como un generador asincrónico, porque el evento transmite. yield* next(e) reenvía la respuesta mientras se transmite y se evalúa al resultado terminado. Este hook registra cuánto de cada solicitud sirvió la API de Claude desde el caché de indicaciones:
result.usage contiene los cuatro conteos de tokens que la API de Claude reporta para una solicitud, más el model que respondió: input_tokens, output_tokens, cache_read_input_tokens y cache_creation_input_tokens. El hook se ejecuta para las solicitudes de subagentes también, por lo que verifica e.agentId cuando quieres solo la conversación principal.
Enganchar los eventos de hook de configuración
Los hooks de configuración son los hooks de comando, HTTP, indicación y agente que configuras en archivos de configuración. Cada evento de hook de configuración, comoStop, SessionEnd o PostToolUse, es también un evento nombrado classic. seguido del nombre del evento de hook de configuración, como classic.Stop. e es el JSON que un hook de configuración recibe en stdin, incluido transcript_path.
Este hook usa Stop, que se dispara cuando Claude termina de responder, para registrar dónde se guarda la transcripción de la sesión:
next(e), por lo que observa el evento y no cambia nada sobre cómo termina el turno.
Ejecutar junto a otros mods
Varios mods pueden enganchar el mismo evento, y cualquiera de ellos puede fallar. Si tu mod bloquea llamadas de herramientas, verifica su posición en la cadena y qué sucede cuando su hook falla.El orden en que se ejecutan los mods
Los hooks en el mismo evento forman una cadena de middleware. Cadanext de un mod llama al hook del siguiente mod, y el último next llega al comportamiento propio de Claude Code. El primer mod es el más externo: ve el evento antes que los otros y el resultado después de ellos, y decide si los otros se ejecutan en absoluto. Un mod posterior no puede detener a uno anterior de ver un evento.
Claude Code ordena la cadena por dónde viene cada mod:
- El guardia incorporado
sec-default@builtin, un mod incorporado en Claude Code que/pluginenumera comocc-plugin-sec-default, donde se carga, mods que tu organización enumera enprependPlugins, y luego cualquier otro mod que cuente como de tu organización y no esté enappendPlugins - Mods que instalas
- Mods que tu organización enumera en
appendPlugins - Otros mods incorporados en Claude Code
dependencies en su manifiesto. Dentro de un módulo, los hooks se ejecutan en el orden en que register llamó a on.
Dónde se ejecutan los hooks de configuración en el orden
Los hooksPreToolUse configurados en archivos de configuración también se ejecutan durante una llamada de herramienta, en puntos fijos en la cadena de mods:
- Hooks
PreToolUsede configuración administrada: se ejecutan antes que el hooktool.calldel primer mod, y un bloqueo de uno de ellos es final, por lo que ningún mod ve la llamada. - Hooks
PreToolUsede cada otro archivo de configuración y dehooks/hooks.jsonde plugins: se ejecutan después de que el último mod llama anext, como parte del comportamiento propio de Claude Code. Un mod que respondetool.callsin llamar anextlos mantiene de ejecutarse, y un mod que llama anextve su decisión en el resultado que devuelve.
tool.check es el evento donde Claude Code decide si una llamada de herramienta puede ejecutarse. Se dispara después de esos hooks y las reglas de permisos han decidido, y next(e) se resuelve a su decisión. Un hook en tool.check puede devolver una decisión diferente, como { decision: 'allow' }, por lo que puede aprobar una llamada que un hook en el segundo grupo bloqueó. Extender permisos con hooks enumera qué decisiones se mantienen sobre un mod.
Manejar un hook que falla
Un hook que falla no rompe la sesión, y puedes decidir qué sucede en su lugar. Cuando un hook sin un manejador.catch lanza una excepción, agota el tiempo o devuelve un resultado de forma incorrecta, lo que sucede después depende de si había llamado a next:
- Falló antes de llamar a
next: Claude Code lo omite, y el siguiente manejador se ejecuta en su lugar - Falló después de que
nextse resolvió: ese resultado se mantiene, y nada se ejecuta una segunda vez
my-mod: tool.call hook skipped: threw Error: boom. Dónde lo lees depende de la sesión, como Averigua por qué un mod no hace nada enumera. Un hook ui.render cuyo dibujo no valida se reporta de manera diferente, como Construir un árbol a partir de elementos describe.
Para hacer que un hook que bloquea llamadas falle cerrado, añade un manejador de error .catch que responda en su lugar. Aquí, guard es tu función de hook:
guard funciona, el manejador nunca se ejecuta. Cuando guard lanza una excepción o agota el tiempo en una llamada Bash, Claude Code llama al manejador con el mismo evento. El manejador devuelve { deny }, por lo que el comando no se ejecuta, y Claude lee el texto con throw o timeout al final. Sin el manejador, Claude Code omitería guard y ejecutaría el comando. El manejador tiene un segundo para responder.
Próximos pasos
- Usa la API de mods: añade comandos y herramientas, llama a un modelo y ejecuta trabajo en un temporizador
- Dibuja en la interfaz: muestra lo que tus hooks recopilan en un panel o encima de la indicación
- Prueba un mod: dispara cualquiera de estos eventos desde una prueba
- Referencia de mods: cada evento, cada método de API de mods y los límites