Ciclo de vida de los hooks
Claude Code ejecuta hooks en puntos específicos durante una sesión. Cuando se activa un evento y un matcher coincide, Claude Code pasa contexto JSON sobre el evento a su controlador de hook. Para hooks de comando, la entrada llega en stdin. Para hooks HTTP, llega como el cuerpo de la solicitud POST. Su controlador puede entonces inspeccionar la entrada, tomar medidas y opcionalmente devolver una decisión. Los eventos se dividen en tres cadencias:- por sesión:
SessionStartySessionEnd - por turno:
UserPromptSubmit,StopyStopFailure - en cada llamada a herramienta dentro del bucle agentico:
PreToolUseyPostToolUse, excepto las llamadasEndConversation, que omiten ambas
Cómo se resuelve un hook
Para ver cómo encajan el evento, el matcher y el controlador, considere este hookPreToolUse que bloquea comandos de shell destructivos.
- macOS/Linux
- Windows (PowerShell)
El El script lee la entrada JSON desde stdin, extrae el comando y devuelve una Este script, como los otros ejemplos de Bash en esta página que analizan entrada JSON, utiliza
matcher se reduce a llamadas a herramientas Bash y la condición if se reduce aún más a subcomandos Bash que coinciden con rm *, por lo que block-rm.sh solo se genera cuando ambos filtros coinciden:permissionDecision de "deny" si contiene rm -rf. Guárdelo en .claude/hooks/block-rm.sh en su proyecto y hágalo ejecutable con chmod +x .claude/hooks/block-rm.sh para que Claude Code pueda ejecutarlo:jq, así que instale jq y asegúrese de que esté en su PATH antes de intentarlos.Bash "rm -rf /tmp/build" contra la configuración de macOS/Linux. Esto es lo que sucede:
1
Se activa el evento
El evento
PreToolUse se activa. Claude Code envía la entrada de la herramienta como JSON en stdin al hook:2
El matcher verifica
El matcher
"Bash" coincide con el nombre de la herramienta, por lo que se activa este grupo de hooks. Si omite el matcher o usa "*", el grupo se activa en cada ocurrencia del evento.3
La condición if verifica
La condición
if "Bash(rm *)" coincide porque rm -rf /tmp/build es un subcomando que coincide con rm *, por lo que se genera este controlador. Si el comando hubiera sido npm test, la verificación if habría fallado y block-rm.sh nunca se habría ejecutado, evitando la sobrecarga de generación de procesos. El campo if es opcional; sin él, cada controlador en el grupo coincidente se ejecuta.4
Se ejecuta el controlador de hooks
El script inspecciona el comando completo y encuentra Si el comando hubiera sido una variante más segura de
rm -rf, por lo que imprime una decisión en stdout:rm como rm file.txt, el script habría alcanzado exit 0 en su lugar. El código de salida 0 sin salida significa que el hook no tiene decisión que reportar, por lo que la llamada a la herramienta continúa a través del flujo de permisos normal. El hook puede denegar la llamada, pero permanecer en silencio no la aprueba.5
Claude Code actúa sobre el resultado
Claude Code lee la decisión JSON, bloquea la llamada a la herramienta y muestra a Claude la razón.
Configuración
Los hooks se definen en archivos de configuración JSON. La configuración tiene tres niveles de anidamiento:- Elige un evento de hook al que responder, como
PreToolUseoStop - Añade un grupo de matcher para filtrar cuándo se activa, como “solo para la herramienta Bash”
- Define uno o más manejadores de hook para ejecutar cuando coincida
Esta página utiliza términos específicos para cada nivel: evento de hook para el punto del ciclo de vida, grupo de matcher para el filtro, y manejador de hook para el comando de shell, punto final HTTP, herramienta MCP, prompt o agente que se ejecuta. “Hook” por sí solo se refiere a la característica general.
Ubicaciones de hooks
Dónde definas un hook determina su alcance:
Las sesiones en la nube no leen tu
~/.claude/settings.json local. En un entorno autohospedado, Claude Code también ejecuta los hooks que el operador sembró desde ~/.claude/ del host del ejecutor, y ejecuta los hooks en el archivo de configuración administrada de la imagen del ejecutor cuando ese archivo está entre las fuentes administradas que Claude Code aplica, lo que por defecto significa solo cuando ni la configuración administrada por servidor ni una política de Claude Code entregada por MDM suministran el nivel administrado. Consulta qué se transfiere de tu configuración para saber qué archivos de configuración y plugins, y por lo tanto qué hooks, llegan a una sesión en la nube.
Para obtener detalles sobre la resolución de archivos de configuración, consulta configuración.
Los hooks de archivos de configuración, configuración de política administrada y plugins también se ejecutan dentro de subagentes. Cuando un subagente llama a una herramienta, eventos de herramienta como PreToolUse y PostToolUse activan los mismos hooks configurados que en la conversación principal, y la entrada lleva los campos de entrada comunes agent_id y agent_type que identifican al subagente.
Los administradores empresariales pueden usar allowManagedHooksOnly para restringir qué hooks se ejecutan:
- Tus hooks de usuario, proyecto, local y plugin están bloqueados. Los hooks de plugins forzados a habilitarse en la configuración administrada
enabledPluginsestán exentos - Claude Code también reduce tu configuración
statusLine,fileSuggestionysubagentStatusLinea la configuración administrada - Claude Code también deshabilita plugins con una fuente
command, incluidos los plugins forzados a habilitarse en la configuración administradaenabledPlugins, a menos quedisableCommandPluginSourcesesté explícitamente establecido enfalse. Las fuentescommandrequieren Claude Code v2.1.229 o posterior - Claude Code también bloquea los comandos
headersHelperdel marketplace a menos quedisableCommandPluginSourcesesté explícitamente establecido enfalse, excepto para un marketplace que la propia configuración administrada declare
allowManagedHooksOnly.
Las entradas de hook se fusionan entre niveles de configuración en lugar de reemplazarse entre sí: la configuración de usuario, proyecto y local añaden sus propios hooks sin eliminar los administrados, y la configuración disableAllHooks no puede deshabilitar hooks administrados desde fuera de la configuración administrada.
Las listas de permitidos de hooks HTTP se aplican a hooks de todas las fuentes, incluida la configuración de política administrada:
allowedHttpHookUrls: cuando se define en cualquier nivel de configuración, Claude Code ejecuta un manejador de hook HTTP solo si su URL coincide con la lista de permitidos fusionadahttpHookAllowedEnvVars: cuando se define, Claude Code interpola solo las variables de entorno en esa lista en los encabezados de hook
Patrones de matcher
El campomatcher filtra cuándo se activan los hooks. Cómo se evalúa un matcher depende de los caracteres que contiene:
Un matcher en la ruta de expresión regular se prueba con
RegExp.prototype.test de JavaScript, que tiene éxito en una coincidencia en cualquier lugar del valor. Edit.* coincide tanto con Edit como con NotebookEdit; envuelve el patrón en ^ y $, como en ^Edit$, cuando necesites una coincidencia de cadena completa.
Los guiones en el conjunto de coincidencia exacta requieren Claude Code v2.1.195 o posterior. En versiones anteriores, un nombre con guiones como code-reviewer se evalúa como una expresión regular sin anclar, por lo que también se activa para senior-code-reviewer; anclalo como ^code-reviewer$ en esas versiones para coincidir solo con ese nombre.
FileChanged y StopFailure utilizan un conjunto de coincidencia exacta más estrecho de solo letras, dígitos, _ y |. Un guión, espacio o coma en un matcher para esos dos eventos lo mantiene en la ruta de expresión regular, y solo | separa alternativas. Todos los demás eventos con soporte de matcher en la tabla que sigue aceptan | o ,.
El evento FileChanged no sigue estas reglas al construir su lista de vigilancia. Consulta FileChanged.
Cada tipo de evento coincide en un campo diferente:
Hacer coincidir
StopFailure en cloud_credential_error requiere Claude Code v2.1.267 o posterior, la primera versión que reporta fallos de carga de credenciales bajo ese valor en lugar de server_error o unknown.
Para la mayoría de eventos, Claude Code evalúa el matcher contra un campo de la entrada JSON que envía a tu hook en stdin. Para eventos de herramienta, ese campo es tool_name. Para PreModelSwitch y PostModelSwitch, Claude Code evalúa el matcher contra el nombre canónico que deriva de to_model, como se describe en PreModelSwitch. Cada sección de evento de hook lista el conjunto completo de valores de matcher y el esquema de entrada para ese evento.
Este ejemplo ejecuta un script de linting solo cuando Claude escribe o edita un archivo:
matcher a un evento sin soporte de matcher, se ignora silenciosamente.
Para eventos de herramienta, puedes filtrar más estrictamente estableciendo el campo if en manejadores de hook individuales. if utiliza sintaxis de regla de permisos para coincidir contra el nombre de la herramienta y los argumentos juntos, por lo que "Bash(git *)" se ejecuta cuando cualquier subcomando de la entrada de Bash coincide con git * y "Edit(*.ts)" se ejecuta solo para archivos TypeScript.
Coincidir herramientas MCP
Las herramientas del servidor MCP aparecen como herramientas regulares en eventos de herramienta (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest, PermissionDenied), por lo que puedes hacerlas coincidir de la misma manera que cualquier otro nombre de herramienta.
Las herramientas MCP siguen el patrón de nomenclatura mcp__<server>__<tool>, por ejemplo:
mcp__memory__create_entities: herramienta crear entidades del servidor Memorymcp__filesystem__read_file: herramienta leer archivo del servidor Filesystemmcp__github__search_repositories: herramienta de búsqueda del servidor GitHub
.* al prefijo del servidor. El .* es obligatorio: un matcher como mcp__memory o mcp__brave-search contiene solo caracteres de coincidencia exacta, por lo que se compara como una cadena exacta y no coincide con ninguna herramienta.
mcp__memory__.*coincide con todas las herramientas del servidormemorymcp__brave-search__.*coincide con todas las herramientas de un servidor cuyo nombre contiene un guiónmcp__.*__write.*coincide con cualquier herramienta cuyo nombre comienza conwritede cualquier servidor
mcp__brave-search se evalúa como una expresión regular sin anclar y coincide con cada herramienta de ese servidor. La forma mcp__brave-search__.* funciona en cada versión.
Las herramientas de un servidor MCP incluido en plugin utilizan un segmento de servidor con alcance que incluye el nombre del plugin: mcp__plugin_<plugin-name>_<server-name>__<tool>. Un matcher escrito contra la clave del servidor desnuda nunca se activa para estas herramientas. Para un plugin llamado my-plugin que incluye un servidor bajo la clave db, una herramienta query aparece como mcp__plugin_my-plugin_db__query, por lo que el matcher para cada herramienta de ese servidor es mcp__plugin_my-plugin_db__.*. Utiliza el mismo nombre de herramienta con alcance en el campo if de un manejador. Consulta Servidores MCP incluidos en plugin para saber cómo se construye el nombre con alcance.
Este ejemplo registra todas las operaciones del servidor de memoria y valida operaciones de escritura de cualquier servidor MCP:
Campos de manejador de hook
Cada objeto en el arrayhooks interno es un manejador de hook: el comando de shell, punto final HTTP, herramienta MCP, prompt LLM o agente que se ejecuta cuando el matcher coincide. Hay cinco tipos:
- Hooks de comando (
type: "command"): ejecutan un comando de shell. Tu script recibe la entrada JSON del evento en stdin y comunica resultados de vuelta a través de códigos de salida y stdout. - Hooks HTTP (
type: "http"): envían la entrada JSON del evento como una solicitud HTTP POST a una URL. El punto final comunica resultados de vuelta a través del cuerpo de respuesta utilizando el mismo formato de salida JSON que los hooks de comando. - Hooks de herramienta MCP (
type: "mcp_tool"): llaman a una herramienta en un servidor MCP configurado. La salida de texto de la herramienta se trata como stdout de hook de comando. - Hooks de prompt (
type: "prompt"): envían un prompt a un modelo Claude para evaluación de un solo turno. El modelo devuelve su decisión como JSON. Consulta Hooks basados en prompt. - Hooks de agente (
type: "agent"): generan un subagente que puede usar herramientas como Read, Grep y Glob para verificar condiciones antes de devolver una decisión. Los hooks de agente son experimentales y pueden cambiar. Consulta Hooks basados en agente.
$CLAUDE_CODE_REMOTE es "true" en entornos web remotos y no está establecida en la CLI local. Claude Code v2.1.199 y posterior establece $CLAUDE_CODE_BRIDGE_SESSION_ID en el ID de sesión de Control Remoto mientras la sesión local tiene una conexión activa de Control Remoto.
Campos comunes
Estos campos se aplican a todos los tipos de hook:
El campo
if contiene exactamente una regla de permisos. No hay sintaxis &&, || o de lista para combinar reglas; para aplicar múltiples condiciones, define un manejador de hook separado para cada una.
En una condición if para una herramienta de archivo, un patrón de directorio de un solo segmento como "Edit(src/**)" coincide solo con el directorio src en el directorio de trabajo y los archivos bajo él. Para coincidir con un directorio llamado src a cualquier profundidad, escribe "Edit(**/src/**)". Antes de v2.1.214, "Edit(src/**)" coincidía con un directorio llamado src a cualquier profundidad bajo el directorio de trabajo.
Para patrones de Bash, si tu comando de hook se ejecuta depende de la forma del patrón y del comando de Bash que Claude está invocando. Las asignaciones VAR=value iniciales se eliminan antes de hacer coincidir.
Cuando Claude Code no puede determinar qué comandos ejecuta la entrada de Bash, ejecuta tu hook independientemente del patrón. Porque el filtro
if es de mejor esfuerzo, utiliza el sistema de permisos en lugar de un hook para aplicar una autorización o denegación dura.
Campos de hook de comando
Además de los campos comunes, los hooks de comando aceptan estos campos:
Un hook de comando se ejecuta como forma exec cuando
args está establecido, y forma shell cuando args se omite. Establece args siempre que el hook haga referencia a un marcador de posición de ruta, ya que cada elemento se pasa como un argumento. Omite args cuando necesites características de shell como pipes o &&, o cuando ninguna preocupación se aplique.
Forma exec se ejecuta cuando args está presente. Claude Code resuelve command como un ejecutable en PATH y lo genera directamente con args como el vector de argumentos. No hay shell, por lo que cada elemento de args es exactamente un argumento tal como está escrito, y los marcadores de posición de ruta como ${CLAUDE_PLUGIN_ROOT} se sustituyen en command y en cada elemento de args como cadenas simples. Los caracteres especiales como apóstrofes, $ y backticks pasan sin cambios porque no hay shell para interpretarlos. No ocurre tokenización de shell en ninguna plataforma.
Forma shell se ejecuta cuando args se omite. La cadena command se pasa a un shell: sh -c en macOS y Linux, Git Bash en Windows, o PowerShell cuando Git Bash no está instalado. Establece el campo shell para elegir explícitamente. El shell tokeniza la cadena, expande variables e interpreta pipes, &&, redirecciones y globs.
En Windows, la forma exec requiere que
command se resuelva en un ejecutable real como .exe. Los shims .cmd y .bat que npm, npx, eslint y otras herramientas instalan en node_modules/.bin no son ejecutables y no se pueden generar sin un shell. Para ejecutarlos en forma exec, invoca el script subyacente con node directamente, por ejemplo "command": "node", "args": ["${CLAUDE_PLUGIN_ROOT}/node_modules/eslint/bin/eslint.js"]. El patrón node más ruta de script funciona en cada plataforma porque node.exe es un binario real. Para ejecutar un shim .cmd o .bat por nombre, usa forma shell.CLAUDE_PROJECT_DIR, CLAUDE_PLUGIN_ROOT y CLAUDE_PLUGIN_DATA en el proceso generado, por lo que un script puede leer process.env.CLAUDE_PLUGIN_ROOT independientemente de cómo se haya lanzado.
Los hooks de plugin además sustituyen valores ${user_config.*}, solo en forma exec: el valor se sustituye en command y en cada elemento de args como una cadena simple, por lo que ningún shell lo re-analiza.
Un hook de plugin en forma shell cuyo command hace referencia a ${user_config.*} falla con un error en lugar de ejecutarse. Para usar un valor de opción de un hook en forma shell, lee la variable de entorno $CLAUDE_PLUGIN_OPTION_<KEY>, como $CLAUDE_PLUGIN_OPTION_WEBHOOK_URL para una opción webhook_url, o establece args para cambiar el hook a forma exec. Antes de v2.1.207, los comandos de hook de plugin en forma shell también sustituían ${user_config.*}.
En forma exec,
command es solo el nombre o ruta del ejecutable. Si command es un nombre desnudo sin separador de ruta y contiene espacios junto con args, Claude Code registra una advertencia porque la generación fallará: no hay un ejecutable llamado node script.js. Mueve los tokens adicionales a args. Las rutas absolutas con espacios, como C:\Program Files\nodejs\node.exe, son un ejecutable válido único y no activan la advertencia.Campos de hook HTTP
Además de los campos comunes, los hooks HTTP aceptan estos campos:
Claude Code envía la entrada JSON del hook como el cuerpo de solicitud POST con
Content-Type: application/json. El cuerpo de respuesta utiliza el mismo formato de salida JSON que los hooks de comando.
El manejo de errores difiere de los hooks de comando; consulta Manejo de respuesta HTTP.
Este ejemplo envía eventos PreToolUse a un servicio de validación local, autenticándose con un token de la variable de entorno MY_TOKEN:
Campos de hook de herramienta MCP
Además de los campos comunes, los hooks de herramienta MCP aceptan estos campos:
Este ejemplo llama a la herramienta
security_scan en el servidor MCP my_server después de cada Write o Edit, pasando la ruta del archivo editado:
isError: true, el hook produce un error sin bloqueo y la ejecución continúa.
En eventos donde un hook puede bloquear o cambiar el resultado, como PreToolUse o Stop, Claude Code espera a que se conecte un servidor antes de llamar a la herramienta, durante como máximo MCP_TIMEOUT y dentro del propio timeout del hook. En eventos observacionales, como Notification o SessionEnd, no espera.
Un servidor que muestra el estado cached se conecta cuando el hook llama a su herramienta. Si el servidor no está conectado en ese punto, el hook produce un error sin bloqueo y la ejecución continúa. El hook nunca inicia un flujo OAuth, por lo que autentica el servidor desde /mcp primero.
SessionStart al lanzar, incluso con --continue o --resume, y cada evento Setup se activan antes de que los servidores MCP de la sesión estén disponibles para los hooks. Claude Code omite sus hooks mcp_tool sin llamar a la herramienta, y el registro de depuración registra mcp_tool hooks are not available for the 'SessionStart' hook event (no MCP client context), o el mismo mensaje nombrando Setup. Cuando SessionStart se activa de nuevo más tarde en la sesión, después de /clear o una compactación, sus hooks mcp_tool se ejecutan. Para cualquier cosa que la sesión necesite al lanzar, usa un hook type: "command" en SessionStart en su lugar.
Campos de hook de prompt y agente
Además de los campos comunes, los hooks de prompt y agente aceptan estos campos:Referencia de scripts por ruta
Utiliza estos marcadores de posición para hacer referencia a scripts de hook relativos a la raíz del proyecto o plugin, independientemente del directorio de trabajo cuando se ejecuta el hook:${CLAUDE_PROJECT_DIR}: la raíz del proyecto donde comenzó la sesión. Claude Code también establece esta variable en el entorno de servidores MCP stdio y servidores LSP de plugin.${CLAUDE_PLUGIN_ROOT}: el directorio de instalación del plugin, para scripts incluidos con un plugin. Consulta variables de entorno de plugin para saber cómo se comporta la ruta entre actualizaciones.${CLAUDE_PLUGIN_DATA}: el directorio de datos persistentes del plugin, para dependencias y estado que deben sobrevivir a las actualizaciones del plugin.
Los worktrees son diferentes. Si Claude entra en un worktree durante la sesión, Claude Code mantiene
${CLAUDE_PROJECT_DIR} donde estaba y pasa la ruta del worktree a tus hooks de una manera diferente:${CLAUDE_PROJECT_DIR}se queda en su lugar: aún apunta a la raíz del proyecto donde comenzó la sesión, por lo que un comando como${CLAUDE_PROJECT_DIR}/.claude/hooks/check-style.shaún ejecuta el script en el checkout principal.cwdsigue a Claude: el campocwden la entrada JSON del hook es la raíz del worktree después de que Claude entra en un worktree, y el nuevo directorio después de que Claude ejecutacd. Léelo cuando un hook necesite saber en qué directorio está trabajando Claude.
- Project scripts
- Plugin scripts
Este ejemplo usa
${CLAUDE_PROJECT_DIR} para ejecutar un verificador de estilo desde el directorio .claude/hooks/ del proyecto después de cualquier llamada de herramienta Write o Edit:Hooks en skills y agentes
Además de archivos de configuración y plugins, los hooks pueden definirse directamente en skills y subagentes usando frontmatter, en el mismo formato de configuración que los hooks basados en configuración. Cuánto tiempo Claude Code los mantiene registrados depende del componente:- Hooks de subagente: Claude Code los ejecuta solo mientras ese subagente se está ejecutando y los elimina cuando termina. Claude Code convierte un hook
Stopaquí aSubagentStop, el evento que se activa cuando un subagente se completa. - Hooks de skill: Claude Code los registra cuando tú o Claude invocas la skill y los mantiene ejecutándose durante el resto de la sesión, en turnos después del turno propio de la skill también. Para que Claude Code elimine un hook después de su primera ejecución exitosa en su lugar, establece
once: trueen él.
PreToolUse que ejecuta un script de validación de seguridad antes de cada comando Bash:
-p en una carpeta que no has confiado.
Los hooks de frontmatter en un subagente de proyecto se ejecutan solo después de que aceptes el diálogo de confianza de espacio de trabajo para la carpeta de la que proviene el archivo del agente. Una sesión -p no cuenta como aceptarlo. Lo que se ejecuta antes de confiar en una carpeta compara esto con la regla del archivo de configuración, y la página de subagentes lista qué alcances están exentos. Antes de v2.1.218, estos hooks podían ejecutarse desde carpetas que no habías confiado.
El menú /hooks
Escribe /hooks en Claude Code para abrir un navegador de solo lectura para tus hooks configurados. El menú muestra cada evento de hook con un recuento de hooks configurados, te permite profundizar en matchers y muestra los detalles completos de cada manejador de hook. Úsalo para verificar la configuración, comprobar desde qué archivo de configuración proviene un hook o inspeccionar el comando, prompt o URL de un hook.
El menú muestra los cinco tipos de hook: command, prompt, agent, http y mcp_tool. Cada hook está etiquetado con un prefijo [type] y una fuente que indica dónde se definió:
User Settings: de~/.claude/settings.jsonProject Settings: de.claude/settings.jsonLocal Settings: de.claude/settings.local.jsonPlugin Hooks: dehooks/hooks.jsonde un pluginSession Hooks: registrado en memoria para la sesión actual
Deshabilitar o eliminar hooks
Para eliminar un hook, elimina su entrada del archivo JSON de configuración. Para deshabilitar temporalmente todos los hooks sin eliminarlos, establece"disableAllHooks": true en tu archivo de configuración. Claude Code lee el valor que queda después de que se aplica la precedencia de configuración, por lo que un "disableAllHooks": false en .claude/settings.json de un proyecto anula un true en tu configuración de usuario. Para desactivar los hooks para una ejecución sin importar lo que diga la configuración del proyecto, pasa --settings '{"disableAllHooks": true}', que tiene precedencia sobre la configuración de proyecto y local. No hay forma de deshabilitar un hook individual mientras lo mantienes en la configuración.
La configuración disableAllHooks respeta la jerarquía de configuración administrada. Si un administrador ha configurado hooks a través de la configuración de política administrada, disableAllHooks establecido en la configuración de usuario, proyecto o local no puede deshabilitar esos hooks administrados. Solo disableAllHooks establecido en el nivel de configuración administrada puede deshabilitar hooks administrados. Para el alcance completo de cada nivel, consulta disableAllHooks.
Las ediciones directas a hooks en archivos de configuración normalmente se recogen automáticamente por el observador de archivos.
Entrada y salida de hooks
Los hooks de comando reciben datos JSON a través de stdin y comunican resultados a través de códigos de salida, stdout y stderr. Los hooks HTTP reciben el mismo JSON que el cuerpo de la solicitud POST y comunican resultados a través del cuerpo de la respuesta HTTP. Esta sección cubre campos y comportamiento comunes a todos los eventos. Cada sección de evento bajo Hook events incluye su esquema de entrada específico y opciones de control de decisión. En macOS y Linux, los hooks de comando se ejecutan en su propia sesión sin una terminal de control. El proceso de hook y cualquier proceso secundario no pueden abrir/dev/tty o enviar secuencias de escape directamente a la interfaz de Claude Code. Windows no tiene /dev/tty.
Para mostrar un mensaje al usuario en cualquier plataforma, devuelva systemMessage en la salida JSON. Algunos eventos lo descartan o lo entregan en otro lugar, y cada sección de evento lo indica. Para activar una notificación de escritorio, establecer un título de ventana o sonar la campana, devuelva terminalSequence en su lugar.
Campos de entrada comunes
Los eventos de hook reciben estos campos como JSON, además de campos específicos del evento documentados en cada sección hook event. Para hooks de comando, este JSON llega a través de stdin. Para hooks HTTP, llega como el cuerpo de la solicitud POST.
Cuando se ejecuta con
--agent o dentro de un subagente, se incluyen dos campos adicionales:
Solo los hooks
SessionStart pueden recibir un campo model, y Claude Code no siempre lo incluye. Los hooks PreModelSwitch y PostModelSwitch reciben from_model y to_model en su lugar, así que use un hook PostModelSwitch para seguir el modelo mientras cambia durante una sesión.
No hay variable de entorno $CLAUDE_MODEL. El hook puede leer $ANTHROPIC_MODEL si lo establece en su shell, pero ese valor no cambia cuando cambia de modelos con /model durante una sesión.
Un proceso de hook hereda el entorno principal, aparte de las variables exportadoras OTEL_* que Claude Code elimina de cada subproceso que genera y, cuando CLAUDE_CODE_SUBPROCESS_ENV_SCRUB se establece en 1, las variables que elimina.
Por ejemplo, un hook PreToolUse para un comando Bash recibe esto en stdin:
tool_name, tool_input y tool_use_id son específicos del evento. Cada sección hook event documenta los campos adicionales para ese evento.
Salida de código de salida
El código de salida de su comando de hook le dice a Claude Code si la acción debe proceder, ser bloqueada o ser ignorada. El código de salida no actúa solo. Claude Code lee campos JSON output desde stdout en cada código de salida, no solo en 0, y para eventos que usan el modelo de decisión estándar, un objeto analizado que pasa la validación del esquema tiene efecto junto con el código. El bloqueo de Exit 2 es el único resultado que JSON no puede anular. Dos tablas poseen las excepciones por evento: Exit code 2 behavior per event dice qué hacen los códigos de salida para cada evento, y Decision control dice qué campos de decisión honra cada evento. Los campos universales comosystemMessage funcionan en la mayoría de eventos y se enumeran en la tabla JSON output.
Exit code 0
Exit 0 significa éxito, y es el código de salida previsto cuando imprime JSON para control estructurado. Para la mayoría de eventos, Claude Code escribe stdout en el registro de depuración y no lo muestra en la transcripción. Las excepciones sonUserPromptSubmit, UserPromptExpansion, SessionStart y PostModelSwitch, donde Claude Code agrega stdout de texto plano como contexto que Claude puede ver y actuar.
Si Claude Code lee su stdout como JSON output o como texto plano depende de cómo comienza y termina, ignorando espacios en blanco circundantes:
- Comienza con
{y termina con}: Claude Code lo analiza como JSON. Cuando la salida son dos o más líneas que cada una se analiza como JSON por su cuenta, y ninguna línea es un objeto JSON output que establece un campo, Claude Code trata toda la salida como texto plano. Cuando una de esas líneas sí establece un campo, toda la salida es un fallo de análisis, descrito a continuación. - Comienza con
{pero no termina con}: Claude Code lo trata como texto plano. - Comienza con cualquier otra cosa: Claude Code lo trata como texto plano, una matriz JSON o una cadena JSON entrecomillada incluida.
<hook name> hook error con el mensaje de validación. Lo mismo sucede en cualquier código de salida que no sea 2, mientras que exit 2 aún bloquea.
Para eventos que usan el modelo de decisión estándar, cuando Claude Code intenta analizar su stdout como JSON y no puede, reporta un error sin bloqueo en cada código de salida que no sea 2. La transcripción muestra un aviso <hook name> hook error con el mensaje de análisis. En los eventos que agregan stdout de texto plano como contexto, Claude Code no agrega el texto. Antes de v2.1.248, Claude Code trataba ese stdout como texto plano.
Stderr de un hook que sale 0 va solo al registro de depuración, nunca a la transcripción, y Claude nunca lo ve. Para leerlo usted mismo, habilite debug logging. Para mostrar una advertencia a Claude desde un hook PostToolUse o PostToolUseFailure, salga 2 en su lugar para que Claude vea stderr aunque la herramienta ya se haya ejecutado.
Exit code 2
Exit 2 significa un error de bloqueo. En eventos que pueden bloquear, exit 2 bloquea independientemente de si imprime JSON: incluso unpermissionDecision JSON de "allow" no puede anularlo. Claude Code aún lee cualquier JSON output válido en stdout. En Elicitation y ElicitationResult, el hookSpecificOutput de un hook exit-2 se ignora.
El mensaje de bloqueo es la razón de la decisión de bloqueo de su JSON cuando hace una, y su texto stderr en caso contrario. Lo que el bloqueo hace varía según el evento: PreToolUse bloquea la llamada a herramienta, UserPromptSubmit rechaza el prompt, y así sucesivamente. Exit code 2 behavior per event enumera el efecto para cada evento, y cada sección de evento dice dónde va el mensaje.
Un hook que sale 2 mientras imprime JSON que falla la validación del esquema JSON output aún bloquea: Claude Code usa stderr como la razón de bloqueo y registra el fallo de validación en el registro de depuración. Antes de v2.1.214, Claude Code trataba esa combinación como un error sin bloqueo y la acción procedía.
Este script bloquea comandos rm saliendo 2 y deja cada otro comando al flujo de permiso normal:
Otros códigos de salida
Cualquier otro código de salida no bloquea por su cuenta para la mayoría de eventos de hook. Lo que sucede depende de su stdout:- Con un objeto analizado que pasa la validación del esquema, para eventos que usan el modelo de decisión estándar, Claude Code ignora el código de salida y solo el JSON decide el resultado:
- Cada campo que el evento admite se honra, incluyendo
permissionDecision,additionalContext,updatedInputysystemMessage, y el hook no se reporta como un error. - Decision control enumera los campos de decisión por evento; campos universales como
systemMessagesiguen la tabla JSON output.
- Cada campo que el evento admite se honra, incluyendo
- Con un objeto analizado que falla la validación del esquema, para eventos que usan el modelo de decisión estándar, es el mismo error sin bloqueo que en exit 0: la acción procede, y el aviso
<hook name> hook errorlleva el mensaje de validación. - Con stdout que Claude Code intenta analizar como JSON y no puede, Claude Code reporta el mismo error sin bloqueo que en exit 0 para eventos que usan el modelo de decisión estándar. La acción procede, y el aviso lleva el mensaje de análisis.
- Con stdout que Claude Code trata como texto plano, o con stdout vacío, es un error sin bloqueo para la mayoría de eventos de hook: la acción procede, y la transcripción muestra un aviso
<hook name> hook errorseguido de la primera línea de stderr, prefijado conFailed with non-blocking status code:. Para capturar el stderr completo, habilite debug logging.
WorktreeCreate falla la creación en cualquier salida distinta de cero sin importar lo que diga su JSON, y eventos que descartan la salida del hook completamente, como StopFailure, ignoran su JSON en cada código de salida, aparte de campos de efecto secundario como terminalSequence, que aún se activan.
Un hook que no puede iniciarse cae en el mismo cubo sin bloqueo. Cuando la ruta del script no existe o no es ejecutable, el shell sale con un código como 127 y ve el mismo aviso con el mensaje del intérprete, por ejemplo Failed with non-blocking status code: /bin/sh: /path/to/hook.sh: No such file or directory. Para la mayoría de eventos de hook, la acción procede. Cuando configura un hook de política, observe este aviso en su primera ejecución: una ruta mal escrita en settings.json deja la puerta silenciosamente deshabilitada.
Tiempos de espera
Aparte de un hook de comando que ejecuta conasync: true, Claude Code cancela un hook command, http o mcp_tool que alcanza su timeout, descartando la salida del hook, por lo que en la mayoría de eventos un hook agotado no renderiza decisión.
En PreModelSwitch, un hook cancelado en su timeout bloquea el cambio de modelo. En PreToolUse, las dos familias de hooks difieren:
- Un hook
command,httpomcp_toolagotado no bloquea la llamada a herramienta. La llamada continúa a través del flujo de permiso normal, así que no cuente con un hook estancado para actuar como puerta. - Un hook de callback Agent SDK que excede su timeout bloquea la llamada a herramienta.
Comportamiento del código de salida 2 por evento
Exit code 2 es la forma en que un hook señala “detente, no hagas esto”. El efecto depende del evento, porque algunos eventos representan acciones que pueden bloquearse (como una llamada a herramienta que aún no ha sucedido) y otros representan cosas que ya sucedieron o no pueden prevenirse.
Para
SessionStart, SubagentStart y PostModelSwitch, Claude Code renderiza el stderr del código de salida 2 en la transcripción como un aviso <hook name> hook error, de la misma manera que renderiza un error sin bloqueo. Claude no lo ve, y la sesión o subagente procede. Para SubagentStart, el aviso aparece en la propia transcripción del subagente, no en la conversación principal.
Manejo de respuesta HTTP
Los hooks HTTP usan códigos de estado HTTP y cuerpos de respuesta en lugar de códigos de salida y stdout. Los resultados a continuación se aplican a la mayoría de eventos; un evento con su propio contrato de fallo en la tabla por evento, comoWorktreeCreate, aplica ese contrato a un hook HTTP fallido también:
- 2xx con un cuerpo vacío: éxito, equivalente a código de salida 0 sin salida
- 2xx con un cuerpo de objeto JSON: analizado usando el mismo esquema JSON output que los hooks de comando. Un cuerpo que falla la validación del esquema es un error sin bloqueo
- 2xx con cualquier otro cuerpo, como texto plano: error sin bloqueo, manejado igual que un estado que no es 2xx. Claude Code no agrega el texto al contexto de Claude
- Estado que no es 2xx: error sin bloqueo, la ejecución continúa
- Fallo de conexión: error sin bloqueo, la ejecución continúa
- Tiempo de espera: el hook se cancela, como se describe bajo Timeouts
Salida JSON
Los códigos de salida solo le permiten bloquear o permanecer en silencio, pero la salida JSON le da un control más granular. En lugar de salir con código 2 para bloquear, salga 0 e imprima un objeto JSON en stdout. Claude Code lee campos específicos de ese JSON para controlar el comportamiento, incluyendo decision control para bloquear, permitir o escalar al usuario.Elija un enfoque por hook: use códigos de salida solos para señalizar, o salga 0 e imprima JSON para control estructurado. Si los mezcla, exit 2 mantiene su efecto de bloqueo, y Claude Code aún lee los campos JSON, con la excepción de elicitación única anotada bajo Exit code 2.
additionalContext, systemMessage e initialUserMessage, y su stdout plano, están limitadas a 10.000 caracteres:
- Alcance: Claude Code mide cada cadena por su cuenta, incluso cuando varios hooks se ejecutan para el mismo evento. Para salida JSON, cada campo se mide por separado; stdout plano se mide en su totalidad.
- Sobre el límite: Claude Code guarda la salida en un archivo en el directorio de sesión y la reemplaza con la ruta del archivo y una vista previa de hasta los primeros 2.000 caracteres. Un resultado de Bash válido grande se maneja de la misma manera, descrito bajo Output limits. A diferencia de ese techo de Bash, este límite no tiene configuración o variable de entorno para aumentarlo.
- Lectura del archivo: Claude Code no le pide a Claude que lea el archivo, así que mantenga cualquier cosa que Claude siempre deba ver dentro del límite.
- Campos universales como
continuese enumeran en la tabla a continuación. Cada evento los acepta, pero algunos eventos los descartan o entregansystemMessageen otro lugar que no sea la transcripción. Cada sección de evento lo indica.terminalSequencefunciona en esos eventos también, con las excepciones enumeradas bajo Emit terminal notifications. decisionyreasonde nivel superior son utilizados por algunos eventos para bloquear o proporcionar retroalimentación.hookSpecificOutputes un objeto anidado para eventos que necesitan control más rico. Requiere un campohookEventNameestablecido en el nombre del evento.
Para detener Claude completamente:
PreToolUse y PostToolUse, la parada se aplica incluso cuando la llamada a herramienta falla o se completa mientras Claude aún está transmitiendo una respuesta.
Emitir notificaciones de terminal
Los hooks se ejecutan sin una terminal de control, por lo que escribir secuencias de escape directamente en/dev/tty falla. En su lugar, devuelva la secuencia de escape en el campo terminalSequence y Claude Code la emite por usted a través de su propia ruta de escritura de terminal. Esto es libre de carreras, funciona dentro de tmux y GNU screen, y funciona en Windows donde no hay /dev/tty.
El campo acepta una cadena de una o más secuencias de escape permitidas:
- OSC
0,1,2: títulos de ventana e icono - OSC
9: notificaciones de iTerm2, ConEmu, Windows Terminal y WezTerm, incluyendo progreso de barra de tareas9;4 - OSC
99: notificaciones de Kitty - OSC
777: notificaciones de urxvt, Ghostty y Warp - BEL desnudo
systemMessage y continue, como Notification y StopFailure. Tiene dos límites:
- Claude Code escribe la secuencia solo en una sesión interactiva, y solo mientras su interfaz está en pantalla. En modo no interactivo con la bandera
-py en Agent SDK, ignora el campo. - Un hook de comando
WorktreeCreateno puede devolver JSON, porque Claude Code lee su stdout como la ruta de worktree. Un hook HTTPWorktreeCreatedevuelve JSON y puede incluir el campo.
Notification. La secuencia de escape se construye con escapes octales printf para que los bytes de control nunca aparezcan en la línea de comandos del shell, y jq -n --arg construye la salida JSON para que las comillas, barras invertidas y saltos de línea en el mensaje de notificación se escapen correctamente:
{ "terminalSequence": "..." } es la misma desde cualquier shell o lenguaje.
Agregar contexto para Claude
El campoadditionalContext pasa una cadena de su hook a la ventana de contexto de Claude. Claude Code envuelve la cadena en un recordatorio del sistema e la inserta en la conversación en el punto donde se activó el hook. Claude lee el recordatorio en la siguiente solicitud del modelo, pero no aparece como un mensaje de chat en la interfaz.
Devuelva additionalContext dentro de hookSpecificOutput junto al nombre del evento:
- SessionStart y SubagentStart: al inicio de la conversación, antes del primer prompt
- UserPromptSubmit y UserPromptExpansion: junto al prompt enviado
- PreToolUse, PostToolUse, PostToolUseFailure y PostToolBatch: junto al resultado de la herramienta
- Stop y SubagentStop: al final del turno. La conversación continúa para que Claude pueda actuar sobre la retroalimentación. Consulte Stop decision control
- PostModelSwitch: con la siguiente solicitud después del cambio. Consulte PostModelSwitch decision control para el tiempo
additionalContext para el mismo evento, Claude recibe todos los valores.
Si un valor excede 10.000 caracteres, Claude Code escribe el texto en un archivo en el directorio de sesión y pasa a Claude la ruta del archivo con una vista previa de hasta los primeros 2.000 caracteres en su lugar. Claude puede leer el archivo, pero Claude Code no se lo pide.
Use additionalContext para información que Claude debe conocer sobre el estado actual de su entorno o la operación que acaba de ejecutarse:
- Estado del entorno: la rama actual, destino de implementación o banderas de características activas
- Reglas de proyecto condicionales: qué comando de prueba se aplica al archivo que acaba de editar, qué directorios son de solo lectura en este worktree
- Datos externos: problemas abiertos asignados a usted, resultados recientes de CI, contenido obtenido de un servicio interno
bun test” se leen como información del proyecto. El texto enmarcado como comandos de sistema fuera de banda puede activar las defensas de inyección de prompts de Claude, lo que hace que Claude le muestre el texto en lugar de tratarlo como contexto.
Claude Code guarda el texto inyectado en la transcripción de sesión. Para eventos a mitad de sesión como PostToolUse o UserPromptSubmit, cuando reanuda con --continue o --resume, Claude Code reproduce el texto guardado en lugar de volver a ejecutar el hook para turnos anteriores, por lo que valores como marcas de tiempo o SHAs de commit se vuelven obsoletos. Los hooks SessionStart se ejecutan nuevamente al reanudar con source establecido en "resume", o "fork" si agregó --fork-session, por lo que pueden actualizar su contexto.
Control de decisión
No todos los eventos admiten bloqueo o control de comportamiento a través de JSON. Los eventos que lo hacen cada uno usan un conjunto diferente de campos para expresar esa decisión. Use esta tabla como referencia rápida antes de escribir un hook:
Algunos eventos también pueden reescribir contenido en lugar de solo permitir o bloquearlo:
PreToolUse:updatedInputdirectamente bajohookSpecificOutputreemplaza los argumentos de una herramienta antes de que se ejecute. Consulte PreToolUse decision controlPermissionRequest:updatedInputdentro del objetodecision. Consulte PermissionRequest decision controlPostToolUse:updatedToolOutputreemplaza el resultado de la herramienta. Consulte PostToolUse decision controlUserPromptSubmit: no puede reemplazar el prompt; solo inyectaadditionalContextjunto a él
PreToolUse para entradas de herramientas salientes y PostToolUse para resultados de herramientas entrantes.
Aquí hay ejemplos de cada patrón en acción:
- Decisión de nivel superior
- PreToolUse
- PermissionRequest
El único valor para
decision es "block". Para permitir que la acción continúe, omita decision de su JSON, o salga 0 sin ningún JSON en absoluto:Eventos de hooks
Cada evento corresponde a un punto en el ciclo de vida de Claude Code donde los hooks pueden ejecutarse. Las secciones a continuación están ordenadas para coincidir con el ciclo de vida: desde la configuración de la sesión a través del bucle agéntico hasta el final de la sesión. Cada sección describe cuándo se dispara el evento, qué matchers admite, la entrada JSON que recibe y cómo controlar el comportamiento a través de la salida.SessionStart
Se ejecuta cuando Claude Code inicia una nueva sesión o reanuda una sesión existente. Útil para cargar contexto de desarrollo como problemas existentes o cambios recientes en tu base de código, o configurar variables de entorno. Para contexto estático que no requiere un script, usa CLAUDE.md en su lugar. SessionStart se ejecuta en cada sesión, así que mantén estos hooks rápidos. Solo se admiten hookstype: "command" y type: "mcp_tool". Consulta Campos de hook de herramienta MCP para saber cuándo se ejecutan los hooks mcp_tool.
El valor del matcher corresponde a cómo se inició la sesión:
Antes de v2.1.214, las sesiones bifurcadas reportaban origen
"resume".
Cuando inicias una sesión interactiva, reanudas una conversación al iniciar con --continue o --resume, o ejecutas /clear, los hooks SessionStart se ejecutan en segundo plano. Puedes escribir de inmediato, y una conversación que reanudaste aparece sin esperar a que se completen los hooks. La primera respuesta de Claude aún espera a que se completen los hooks, por lo que su contexto llega a Claude.
Cuando cambias de conversación con /resume dentro de una sesión, el cambio espera a que se completen los hooks. Si ejecutas /clear o cambias a otra conversación mientras los hooks de fondo aún se están ejecutando, nada de lo que devuelven se aplica a la sesión.
La misma espera se aplica al iniciar, incluyendo una sesión reanudada: un prompt que envíes mientras los hooks SessionStart aún se están ejecutando no llega a Claude hasta que se completen.
Durante cualquiera de estas esperas, presiona Esc para recuperar el prompt en la entrada sin enviarlo. Los hooks siguen ejecutándose.
Entrada de SessionStart
Además de los campos de entrada comunes, los hooks SessionStart recibensource y opcionalmente model, agent_type, y session_title:
Cuando
source es "resume" o "fork" y la transcripción contiene al menos una respuesta de Claude, los hooks SessionStart también reciben los cuatro campos a continuación. Tu hook puede usarlos para reportar qué cuesta reanudar una conversación obsoleta antes de la primera solicitud, por ejemplo en un systemMessage. Estos campos requieren Claude Code v2.1.251 o posterior.
Este ejemplo muestra la entrada para una sesión reanudada 90 minutos después de su última respuesta:
Control de decisión de SessionStart
Claude Code añade stdout que trata como texto plano al contexto de Claude. Además de los campos de salida JSON disponibles para todos los hooks, puedes devolver estos campos específicos del evento:sessionTitle.
Usa reloadSkills cuando un hook SessionStart instala o actualiza skills. El descubrimiento de skills normalmente se ejecuta antes de que se completen los hooks SessionStart, por lo que los archivos que el hook escribe en ~/.claude/skills/ o .claude/skills/ de otro modo solo aparecerían en la siguiente sesión. Este ejemplo sincroniza un repositorio de skills compartido y solicita el re-escaneo:
fatal: a stderr. Stderr de un hook SessionStart que sale con 0 es solo informativo, por lo que la solicitud reloadSkills aún se aplica.
Persistir variables de entorno
Los hooks SessionStart tienen acceso a la variable de entornoCLAUDE_ENV_FILE, que proporciona una ruta de archivo donde puedes persistir variables de entorno para comandos Bash posteriores.
Para establecer variables de entorno individuales, escribe declaraciones export en CLAUDE_ENV_FILE. Usa append (>>) para preservar variables establecidas por otros hooks:
CLAUDE_ENV_FILE está disponible para hooks SessionStart, Setup, CwdChanged, y FileChanged. Otros tipos de hooks no tienen acceso a esta variable.Setup
Se dispara solo cuando inicias Claude Code con--init-only, o con --init o --maintenance en modo no interactivo con la bandera -p. No se dispara en el inicio normal. Úsalo para instalación de dependencias única o limpieza programada que actives explícitamente desde CI o scripts, separado del inicio normal de sesión. Para inicialización por sesión, usa SessionStart en su lugar.
El valor del matcher corresponde a la bandera CLI que activó el hook:
Cuando ejecutas
claude --init-only, Claude Code ejecuta hooks Setup y hooks SessionStart con el matcher startup, luego sale sin iniciar una conversación.
Cuando inicias o continúas una conversación con -p, también necesitas proporcionar un prompt, como argumento o canalizando en stdin. Puedes omitir el prompt cuando un hook SessionStart proporciona initialUserMessage o cuando reanudas una sesión con una llamada de herramienta diferida.
En caso de éxito, --init-only no imprime nada en la terminal. Para confirmar que los hooks se ejecutaron, comienza con claude --debug-file <path> --init-only, reemplazando <path> con una ubicación de archivo de registro, y verifica el registro para las entradas de hook Setup y SessionStart.
Debido a que Setup no se dispara en cada inicio, un plugin que necesita una dependencia instalada no puede confiar solo en Setup. El patrón práctico es verificar la dependencia en el primer uso e instalar si falta, por ejemplo un hook o skill que prueba ${CLAUDE_PLUGIN_DATA}/node_modules y ejecuta npm install si está ausente. Consulta el directorio de datos persistentes para saber dónde almacenar dependencias instaladas. Si distribuyes tu plugin a través de un marketplace, es posible que no necesites este patrón: Claude Code instala automáticamente las dependencias de paquetes Node.js elegibles cuando almacena en caché el plugin.
Entrada de Setup
Además de los campos de entrada comunes, los hooks Setup reciben un campotrigger establecido en "init" o "maintenance":
Control de decisión de Setup
Los hooks Setup no pueden bloquear; la ejecución continúa en cualquier código de salida. En cada código de salida, Claude Code descarta los campos de salida JSON de un hook Setup, comosystemMessage, continue, y hookSpecificOutput.additionalContext. Con -p, la salida estándar, error estándar y código de salida de un hook Setup aparecen en la salida de la ejecución solo como eventos hook_response cuando inicias con --output-format stream-json --verbose.
Los hooks Setup tienen acceso a CLAUDE_ENV_FILE. Las variables escritas en ese archivo persisten en comandos Bash posteriores para la sesión, tal como en hooks SessionStart. Solo se ejecutan hooks type: "command" en Setup. Un hook type: "mcp_tool" en Setup siempre se omite, como se describe en Campos de hook de herramienta MCP.
InstructionsLoaded
Se dispara cuando se carga un archivoCLAUDE.md o .claude/rules/*.md en el contexto. Este evento se dispara al inicio de la sesión para archivos cargados con entusiasmo y nuevamente más tarde cuando se cargan archivos de forma perezosa, por ejemplo cuando Claude accede a un subdirectorio que contiene un CLAUDE.md anidado o cuando las reglas condicionales con frontmatter paths: coinciden. El hook no admite bloqueo o control de decisión. Se ejecuta de forma asincrónica con fines de observabilidad.
Este evento no se dispara cuando Claude lee AGENTS.md directamente a través de la configuración Instrucciones del proyecto. Se dispara cuando un CLAUDE.md importa tu AGENTS.md, con load_reason establecido en include como para cualquier otro archivo importado, y cuando CLAUDE.md es un enlace simbólico a él, como una carga normal de CLAUDE.md.
El matcher se ejecuta contra load_reason. Por ejemplo, usa "matcher": "session_start" para dispararse solo para archivos cargados al inicio de la sesión, o "matcher": "path_glob_match|nested_traversal" para dispararse solo para cargas perezosas.
Entrada de InstructionsLoaded
Además de los campos de entrada comunes, los hooks InstructionsLoaded reciben estos campos:Control de decisión de InstructionsLoaded
Los hooks InstructionsLoaded no tienen control de decisión. No pueden bloquear o modificar la carga de instrucciones. Claude Code descarta sus campos de salida JSON, comosystemMessage y continue. Usa este evento para auditoría de registros, seguimiento de cumplimiento u observabilidad.
UserPromptSubmit
Se ejecuta cuando el usuario envía un prompt, antes de que Claude lo procese. Esto te permite añadir contexto adicional basado en el prompt/conversación, validar prompts o bloquear ciertos tipos de prompts. Los hooksUserPromptSubmit tienen un tiempo de espera predeterminado de 30 segundos para tipos command, http, y mcp_tool, más corto que el predeterminado de 600 segundos para esos tipos en la mayoría de otros eventos. Debido a que este hook se ejecuta antes de cada prompt y bloquea el procesamiento del modelo hasta que se completa, un hook atascado paraliza la sesión. Si tu hook necesita más tiempo, establece el campo timeout en la entrada del hook.
Aparte de un hook de comando que ejecutas con async: true, un hook UserPromptSubmit de comando, HTTP o herramienta MCP que alcanza su tiempo de espera se cancela y su salida, incluyendo cualquier additionalContext, se descarta. El prompt aún llega a Claude sin ese contexto. La transcripción muestra un aviso que nombra el hook, el tiempo de espera que se disparó y que la salida se descartó.
Un hook de callback del Agent SDK en UserPromptSubmit que alcanza su tiempo de espera bloquea el prompt con un mensaje que nombra el hook y el tiempo de espera, porque un callback allí puede estar actuando como una puerta de política que no debe fallar abierta. La sesión continúa. Antes de v2.1.208, un tiempo de espera de callback en ese evento terminaba el turno con un error de ejecución.
Entrada de UserPromptSubmit
Además de los campos de entrada comunes, los hooks UserPromptSubmit reciben el campoprompt que contiene el texto que el usuario envió. El contenido pegado que se redujo a un marcador de posición [Pasted text #N] llega expandido en su lugar. En sesiones donde Claude Code marca el texto pegado para Claude, ese contenido expandido se encuentra entre una línea <pasted_content id="…"> y una línea </pasted_content id="…">, así que ten en cuenta esas líneas si tu hook analiza el prompt.
Control de decisión de UserPromptSubmit
Los hooksUserPromptSubmit pueden controlar si se procesa un prompt de usuario y añadir contexto. Todos los campos de salida JSON están disponibles.
Hay dos formas de añadir contexto a la conversación en código de salida 0:
- Stdout de texto plano: Claude Code añade stdout que trata como texto plano al contexto de Claude
- JSON con
additionalContext: usa el formato JSON a continuación para más control. El campoadditionalContextse añade como contexto
additionalContext se inyectan cada uno como un recordatorio del sistema que comienza con el nombre del hook; Claude lee ambos. Para confirmar la entrega, verifica el registro de depuración.
Para bloquear un prompt, devuelve un objeto JSON con decision establecido en "block":
Un hook que bloquea saliendo con 2 se enruta de la misma manera que
reason: el mensaje de bloqueo muestra el texto stderr al usuario, y no se añade al contexto.
UserPromptExpansion
Se ejecuta cuando un comando escrito por el usuario se expande en un prompt antes de llegar a Claude. Úsalo para bloquear comandos específicos de invocación directa, inyectar contexto para una skill particular o registrar qué comandos invocan los usuarios. Por ejemplo, un hook que coincide condeploy puede bloquear /deploy a menos que esté presente un archivo de aprobación, o un hook que coincide con una skill de revisión puede añadir la lista de verificación de revisión del equipo como additionalContext.
Este evento cubre la ruta que PreToolUse no cubre: un hook PreToolUse que coincide con la herramienta Skill se dispara solo cuando Claude llama a la herramienta, pero escribir /skillname directamente omite PreToolUse. UserPromptExpansion se dispara en esa ruta directa.
Coincide en command_name. Deja el matcher vacío para dispararse en cada comando de tipo prompt.
Entrada de UserPromptExpansion
Además de los campos de entrada comunes, los hooks UserPromptExpansion recibenexpansion_type, command_name, command_args, command_source, y la cadena prompt original. El campo expansion_type es slash_command para skills y comandos personalizados, o mcp_prompt para prompts del servidor MCP.
Control de decisión de UserPromptExpansion
Los hooksUserPromptExpansion pueden bloquear la expansión o añadir contexto. Todos los campos de salida JSON están disponibles.
Un hook que bloquea saliendo con 2 se enruta de la misma manera que
reason: el mensaje de bloqueo muestra el texto stderr al usuario.
MessageDisplay
Se ejecuta mientras un mensaje del asistente se transmite a la pantalla. Claude Code muestra el mensaje en incrementos: cada vez que un lote de líneas recién completadas está listo para renderizar, el hook se ejecuta una vez con esas líneas y Claude Code renderiza el texto de reemplazo del hook en su lugar. Un mensaje largo produce varias llamadas; un mensaje corto puede producir solo una. Usa MessageDisplay para:- eliminar markdown para una visualización mínima
- transformar el texto que una aplicación del Agent SDK muestra a sus usuarios
- redactar claves API o nombres de host internos de las respuestas de Claude
timeout en la entrada del hook.
MessageDisplay es solo para visualización: el texto de reemplazo cambia solo lo que se renderiza en pantalla. La transcripción y lo que Claude ve mantienen el texto original, por lo que Claude nunca ve el reemplazo, y el modo detallado muestra el original. El hook recibe solo texto de mensaje del asistente, por lo que los resultados de herramientas y el texto que escribes se renderiza sin cambios.
MessageDisplay no admite matchers y se dispara para cada mensaje del asistente que transmite texto; los mensajes sin texto, como respuestas de solo llamada de herramienta, no lo activan.
En ejecuciones no interactivas, incluyendo consultas del Agent SDK y claude -p, MessageDisplay se ejecuta una vez por mensaje del asistente en lugar de una vez por lote de líneas. La llamada única llega después de que se completa el mensaje y lleva el texto completo del mensaje: index es 0, final es true, y delta contiene el mensaje completo. Un hook que recopila el texto delta para cada mensaje recibe el mismo texto total en ambos modos.
Entrada de MessageDisplay
Además de los campos de entrada comunes, los hooks MessageDisplay reciben identificadores para el turno y el mensaje, la posición de esta llamada dentro del mensaje, y el nuevo texto endelta. Los límites de lotes dependen de cómo se transmita el texto, así que usa index y final para rastrear el progreso a través de un mensaje en lugar de esperar que las líneas se agrupen de una manera particular.
Salida de MessageDisplay
Además de los campos de salida JSON disponibles para todos los hooks, los hooks MessageDisplay pueden devolverdisplayContent para reemplazar el delta en pantalla:
Los hooks MessageDisplay no tienen control de decisión. No pueden bloquear el mensaje o cambiar lo que se almacena en la transcripción o se envía a Claude. Claude Code actúa sobre
displayContent de su salida JSON y descarta systemMessage y continue.
Este ejemplo elimina el formato markdown de las respuestas de Claude para una visualización de texto plano. El script lee cada lote de stdin, elimina marcadores en negrita y backticks de código en línea de delta, y devuelve el resultado como displayContent.
- macOS/Linux
- Windows (PowerShell)
Registra un hook de comando para el evento en tu archivo de configuración:Guarda este script en
.claude/hooks/plain-display.sh en tu proyecto y hazlo ejecutable con chmod +x:jq falta, Claude Code muestra el texto original y nota el fallo solo en salida de depuración, no en la sesión.
PreToolUse
Se ejecuta después de que Claude crea parámetros de herramienta y antes de procesar la llamada de herramienta. Coincide en cualquier nombre de herramienta exceptoEndConversation: herramientas integradas como Bash, PowerShell, Edit, Write, Read, Glob, Grep, Agent, Workflow, WebFetch, WebSearch, AskUserQuestion, y ExitPlanMode, y cualquier nombre de herramienta MCP.
Para ejecutar un hook cuando un archivo específico cambia en el disco, sin importar qué lo escribió, usa FileChanged en lugar de coincidir con herramientas de edición de archivos por nombre. A diferencia de PreToolUse, Claude Code ejecuta hooks FileChanged después del cambio, y no tienen control de decisión, por lo que no pueden bloquear la escritura.
Usa control de decisión PreToolUse para permitir, negar, preguntar o diferir la llamada de herramienta.
Un hook de callback del Agent SDK en PreToolUse que excede su tiempo de espera bloquea la llamada de herramienta, y Claude recibe un resultado de error que nombra el tiempo de espera. Una negación explícita devuelta por otro hook aún tiene precedencia.
Entrada de PreToolUse
Además de los campos de entrada comunes, los hooks PreToolUse recibentool_name, tool_input, y tool_use_id.
Para una herramienta MCP, la entrada también lleva mcp_server, un objeto con el name del servidor y un source que dice de dónde vino la definición del servidor. Los valores source incluyen plugin, sdk, y alcances de configuración como user y project. McpServerProvenance en la referencia del Agent SDK los enumera todos y dice cómo tratar uno que no reconozcas. Basa las decisiones de confianza en source en lugar de en name o el prefijo de nombre de herramienta mcp__<server>__. El campo mcp_server requiere Claude Code v2.1.274 o posterior.
Para las herramientas de archivo Write, Edit, y Read, tool_input.file_path siempre es absoluto:
- Claude Code expande
~y rutas relativas antes de que se ejecuten los hooks, por lo que un hook que coincida en rutas no puede ser eludido a través de~o un deletreo relativo de la misma ruta - En Windows, la ruta llega con separadores de barra invertida, incluso cuando tu hook se ejecuta bajo Git Bash donde
$PWDse ve como/c/project - Una comparación escrita con barras diagonales, como una verificación
/src/, nunca coincide con una ruta de barra invertida, y la llamada de herramienta continúa como si el hook no tuviera nada que bloquear - Normaliza separadores antes de comparar:
FILE_PATH="${FILE_PATH//\\//}"en Bash, ofile_path.replace("\\", "/")en Python, luego coincide con un segmento de ruta como/src/en lugar de anclar con^, ya que la ruta es absoluta
Write en Windows entrega:
tool_input dependen de la herramienta:
Ejecuta comandos de shell.
Cuando un comando Bash cambia archivos en un repositorio Git, Claude Code puede registrar qué cambió. Registra los cambios en cada modo de permiso cuando la configuración
bashEditDiffEnabled activa el registro; la entrada de esa configuración dice qué archivos pueden establecerla. De lo contrario, los registra solo en modo automático y modo bypassPermissions, y solo cuando Claude Code dirige a Claude a editar archivos a través de Bash. Establece bashEditDiffEnabled en false para desactivar el registro. Los comandos de fondo y los comandos de solo lectura no llevan diff.
Tu hook PostToolUse luego recibe los archivos cambiados en tool_response.bashEditDiff. La lista cubre qué cambió bajo el repositorio mientras se ejecutaba el comando. Los archivos que Git ignora y los archivos en submódulos no se enumeran. Requiere Claude Code v2.1.269 o posterior.
La lista es mejor esfuerzo y en beta pública. Claude Code puede perder un cambio, incluir un archivo que otro proceso cambió al mismo tiempo, o detenerse en sus límites de tamaño. La forma del campo puede cambiar. Usa la lista para encontrar qué revisar, no para aplicar una política.
changedFiles y files enumeran qué cambió el comando; los campos restantes dicen qué tan completa y confiable es esa lista.
Ejecuta comandos de PowerShell. Consulta la herramienta PowerShell para disponibilidad por plataforma.
Los campos coinciden con la herramienta Bash, con la cadena de comando en
command:
Coincide en
Bash|PowerShell en hooks que inspeccionan comandos de shell, para que cubran ambas herramientas:
- En Windows, dondequiera que la herramienta PowerShell esté habilitada, Claude trata PowerShell como el shell principal y enruta comandos de shell a través de él.
- En Windows sin Git Bash, la herramienta se habilita automáticamente y Claude Code no registra la herramienta Bash en absoluto.
- Un hook que coincide solo con
Bashnunca se dispara allí.
Reemplaza una cadena en un archivo existente.
Lee contenidos de archivo.
Encuentra archivos que coincidan con un patrón glob.
Busca contenidos de archivo con expresiones regulares.
Obtiene y procesa contenido web.
Busca en la web.
Genera un subagente.
Cuando una llamada Agent en primer plano se completa, tu hook PostToolUse recibe el resultado del subagente y telemetría de ejecución en
tool_response. Lee estos campos para inspeccionar la ejecución; para resúmenes de tokens y costos en subagentes, usa los contadores de tokens y costos filtrados a query_source "subagent", ya que totalTokens y usage cubren solo la solicitud final:
En Claude Code v2.1.271 o posterior, un subagente que se ejecuta con la herramienta
SubagentHandback, que Claude Code proporciona en modo automático, entrega su informe a través de esa herramienta en lugar de devolverlo como texto. El campo content de su resultado completed entonces lleva una nota breve sobre esa entrega en lugar del informe en sí. Para leer el informe, coincide un hook PreToolUse o PostToolUse en SubagentHandback y lee tool_input.message.
Para subagentes en segundo plano, la herramienta devuelve cuando la tarea se mueve al segundo plano, por lo que tool_response no lleva campos de uso: un lanzamiento en segundo plano devuelve inmediatamente, y una tarea en primer plano que Claude Code pone en segundo plano a mitad de ejecución devuelve en esa transición. Tiene status: "async_launched", agentId, description, prompt, outputFile, y resolvedModel.
En una respuesta completed, resolvedModel nombra el modelo en el que comenzó el subagente, que puede diferir del valor model en tool_input, como cuando availableModels u otra anulación se aplica. En una respuesta async_launched, resolvedModel nombra el modelo en uso cuando el agente se movió al segundo plano, por lo que un intercambio que ocurrió antes de pasar al segundo plano se refleja allí. El comportamiento de modelsUsed y resolvedModel en tiempo de segundo plano requieren Claude Code v2.1.212 o posterior.
Hace al usuario una a cuatro preguntas de opción múltiple.
Presenta un plan y pide al usuario que lo apruebe antes de que Claude salga del modo plan. Claude escribe el plan en un archivo en el disco antes de llamar a la herramienta, por lo que la
tool_input literal del modelo es típicamente vacía. Claude Code inyecta el contenido del plan y la ruta del archivo antes de pasar la entrada a los hooks.
En
PostToolUse, tool_response es un objeto con campos plan y filePath que contienen el plan aprobado, más banderas de estado internas. Lee tool_response.plan para el contenido del plan en lugar de releer el archivo del disco.
Control de decisión de PreToolUse
Los hooksPreToolUse pueden controlar si procede una llamada de herramienta. A diferencia de otros hooks que usan un campo decision de nivel superior, PreToolUse devuelve su decisión dentro de un objeto hookSpecificOutput. Esto le da control más rico: cuatro resultados (permitir, negar, preguntar o diferir) más la capacidad de modificar la entrada de herramienta antes de la ejecución.
Cuando múltiples hooks PreToolUse devuelven decisiones diferentes, la precedencia es
deny > defer > ask > allow.
Un hook que bloquea saliendo con 2 se enruta de la misma manera que "deny": Claude ve el mensaje stderr como la razón de la negación.
Cuando un hook devuelve "ask", el prompt de permiso mostrado al usuario incluye una etiqueta que identifica de dónde vino el hook: [settings] para un hook de cualquier archivo de configuración o del frontmatter del agente, [plugin:<name>] para el hook de un plugin, o [skill] para un hook del frontmatter de skill. Esto ayuda a los usuarios a entender qué fuente de configuración está solicitando confirmación.
Un "ask" de un hook también fuerza un prompt de permiso en modo automático: el clasificador aún puede negar la llamada de herramienta, pero no puede aprobar la llamada silenciosamente. Antes de v2.1.211, el clasificador podría aprobar un comando Bash ejecutándose fuera del sandbox sin mostrar el prompt que el hook solicitó; el clasificador aún aplicaba sus propias reglas de seguridad a ese comando, y una negación de hook siempre se honraba.
-p, Claude Code ofrece AskUserQuestion y ExitPlanMode solo cuando la ejecución tiene un host de permiso para recibir el prompt, como un callback canUseTool del Agent SDK. Estas herramientas requieren interacción del usuario. Devolver permissionDecision: "allow" junto con updatedInput satisface ese requisito: el hook lee la entrada de la herramienta de stdin, recopila la respuesta a través de tu propia UI, y la devuelve en updatedInput para que la herramienta se ejecute sin solicitar. Devolver "allow" solo no es suficiente para estas herramientas. Para AskUserQuestion, devuelve el array questions original y añade un objeto answers que mapea el texto de cada pregunta a la respuesta elegida.
A partir de v2.1.199, una herramienta MCP cuyo servidor la marca con _meta["anthropic/requiresUserInteraction"] es más estricta: un hook no puede omitir su prompt de aprobación con "allow", con o sin updatedInput, porque Claude Code no puede confirmar que el hook recopiló la interacción que la herramienta necesita.
PreToolUse previamente usaba campos
decision y reason de nivel superior, pero estos están deprecados para este evento. Usa hookSpecificOutput.permissionDecision y hookSpecificOutput.permissionDecisionReason en su lugar. Los valores deprecados "approve" y "block" se mapean a "allow" y "deny" respectivamente. Otros eventos como PostToolUse y Stop continúan usando decision y reason de nivel superior como su formato actual.Diferir una llamada de herramienta para más tarde
"defer" es para integraciones que ejecutan claude -p como un subproceso y leen su salida JSON, como una aplicación del Agent SDK o una UI personalizada construida sobre Claude Code. Permite que ese proceso de llamada pause Claude en una llamada de herramienta, recopile entrada a través de su propia interfaz, y reanude donde se quedó. Claude Code honra este valor solo en modo no interactivo con la bandera -p. En sesiones interactivas registra una advertencia e ignora el resultado del hook.
La herramienta AskUserQuestion es el caso típico: Claude quiere hacer una pregunta al usuario, pero no hay terminal para responder. Una ejecución -p ofrece AskUserQuestion solo cuando tiene un host de permiso, como una herramienta MCP que pasas con --permission-prompt-tool, así que comienza la ejecución con una. El viaje de ida y vuelta funciona así:
- Claude llama a
AskUserQuestion. Se dispara el hookPreToolUse. - El hook devuelve
permissionDecision: "defer". La herramienta no se ejecuta. El proceso sale constop_reason: "tool_deferred"y la llamada de herramienta pendiente preservada en la transcripción. - El proceso de llamada lee
deferred_tool_usedel resultado del SDK, muestra la pregunta en su propia UI, y espera una respuesta. - El proceso de llamada ejecuta
claude -p --resume <session-id>con el mismo host de permiso. Se dispara la misma llamada de herramientaPreToolUsenuevamente. - El hook devuelve
permissionDecision: "allow"con la respuesta enupdatedInput. La herramienta se ejecuta y Claude continúa.
deferred_tool_use lleva el id, name, e input de la herramienta. El input son los parámetros que Claude generó para la llamada de herramienta, capturados antes de la ejecución:
cleanupPeriodDays, que elimina archivos de sesión después de 30 días por defecto, siguiendo las reglas de barrido de retención. Si la respuesta no está lista cuando reanudas, el hook puede devolver "defer" nuevamente y el proceso sale de la misma manera. El proceso de llamada controla cuándo romper el bucle eventualmente devolviendo "allow" o "deny" del hook.
"defer" solo funciona cuando Claude hace una única llamada de herramienta en el turno. Si Claude hace varias llamadas de herramienta a la vez, "defer" se ignora con una advertencia y la herramienta procede a través del flujo de permiso normal. La restricción existe porque reanudar solo puede re-ejecutar una herramienta: no hay forma de diferir una llamada de un lote sin dejar las otras sin resolver.
Si la herramienta diferida ya no está disponible cuando reanudas, el proceso sale con stop_reason: "tool_deferred_unavailable" e is_error: true antes de que se dispare el hook. Esto sucede cuando un servidor MCP que proporcionó la herramienta no está conectado para la sesión reanudada. El payload deferred_tool_use aún se incluye para que puedas identificar qué herramienta desapareció.
Para reanudar una sesión diferida en modo plan, pasa
--permission-prompt-tool junto con --resume para que Claude Code pueda presentar el plan para aprobación. Si pasas ciertos otros indicadores de inicio, la ejecución reanudada no vuelve al modo plan; consulta Reanudar en modo plan con -p. Requiere Claude Code v2.1.246 o posterior.Cuando reanudas con -p, Claude Code no restaura ningún otro modo de permiso almacenado. Comienza la ejecución en el modo de permiso que comenzaría una nueva ejecución claude -p, así que pasa --permission-mode o --dangerously-skip-permissions nuevamente si la sesión diferida usó uno. Cuando reanudas con claude --resume <session-id> sin -p, Claude Code restaura el modo de permiso almacenado, con las excepciones enumeradas en modo de permiso en reanudación.PermissionRequest
Se ejecuta cuando Claude Code está a punto de pedirte permiso para usar una herramienta. En sesiones que no pueden mostrar un prompt, como subagentes en segundo plano en modo no interactivo, Claude Code aún ejecuta estos hooks, y si ningún hook devuelve una decisión, niega la llamada de herramienta. Usa control de decisión PermissionRequest para permitir o negar en nombre del usuario. Usa este evento cuando necesites una señal en el momento en que Claude solicita permiso para usar una herramienta. Claude Code ejecuta un hook Notification con el tipopermission_prompt solo después de que el prompt haya esperado aproximadamente seis segundos.
Claude Code no ejecuta hooks PermissionRequest para la solicitud de red de un comando en sandbox. Para obtener una señal para ese prompt, usa el tipo de notificación permission_prompt.
Coincide en nombre de herramienta, los mismos valores que PreToolUse.
Entrada de PermissionRequest
Los hooks PermissionRequest reciben campostool_name y tool_input como los hooks PreToolUse, pero sin tool_use_id. Para una herramienta MCP, también reciben el objeto mcp_server. Un array permission_suggestions opcional contiene las actualizaciones de permiso que Claude Code sugiere para esta solicitud, como añadir una regla de permiso o cambiar el modo de permiso.
El array permission_suggestions no es una lista exacta de las opciones que ves, porque cada diálogo de permiso construye sus propias opciones. Algunos diálogos, como el de ediciones de archivo, no leen el array en absoluto y derivan sus opciones de la solicitud en sí. Un diálogo que sí lo lee aún puede retener una opción cuya sugerencia permanece en el array, por ejemplo cuando allowManagedPermissionRulesOnly oculta opciones de guardado de reglas. También puede ofrecer opciones que no tienen entrada de sugerencia, como Sí, y cambiar a modo automático, que cambia el modo de permiso directamente en lugar de a través de una actualización de permiso.
Los hooks PreToolUse se ejecutan antes de cada llamada de herramienta, independientemente de si necesita permiso. Los hooks PermissionRequest se ejecutan solo cuando Claude Code está a punto de pedirte permiso, o cuando de otro modo auto-negaría una llamada que no puede solicitar. Ninguno de los dos eventos se dispara para EndConversation.
Control de decisión de PermissionRequest
Los hooksPermissionRequest pueden permitir o negar solicitudes de permiso. Además de los campos de salida JSON disponibles para todos los hooks, tu script de hook puede devolver un objeto decision con estos campos específicos del evento:
Un hook que sale con 2 sin un objeto
decision deja el flujo de permiso sin cambios, y su stderr se descarta. Solo el objeto decision puede otorgar o negar la solicitud.
Entradas de actualización de permiso
El campo de salidaupdatedPermissions y el campo de entrada permission_suggestions ambos usan el mismo array de objetos de entrada. Cada entrada tiene un type que determina sus otros campos, y un destination que controla dónde se escribe el cambio.
setMode con bypassPermissions solo toma efecto si iniciaste la sesión con modo bypass ya disponible: --dangerously-skip-permissions, --permission-mode bypassPermissions, --allow-dangerously-skip-permissions, o permissions.defaultMode: "bypassPermissions" en configuración de usuario, --settings, o configuración gestionada. De lo contrario, la actualización es una no-op. La actualización también es una no-op cuando permissions.disableBypassPermissionsMode deshabilita el modo, o cuando la sesión comienza en modo restringido.bypassPermissions nunca se persiste como defaultMode independientemente de destination.destination en cada entrada determina si el cambio permanece en memoria o persiste en un archivo de configuración.
Un hook puede devolver una de las
permission_suggestions que recibió como su propia salida updatedPermissions.
PostToolUse
Se ejecuta inmediatamente después de que una herramienta se completa exitosamente. Coincide en nombre de herramienta, los mismos valores que PreToolUse. Coincide más ampliamente cuando el nombre de la herramienta no es el filtro correcto:- Para ejecutar un hook después de que cualquier herramienta se complete exitosamente, omite el
matchero establécelo en"*". Tu hook puede entonces descubrir qué cambió por sí mismo, por ejemplo ejecutandogit status --porcelain, que también enumera archivos sin seguimiento quegit diffpierde. Para llamadas de herramienta que fallan, añade el mismo hook bajo PostToolUseFailure. - Para ejecutar un hook cuando un archivo específico cambia en el disco, sin importar qué lo escribió, usa FileChanged. Claude Code no ejecuta un hook
PostToolUseque coincida conEdit|Writecuando un comandoBasho un proceso fuera de Claude Code reescribe el mismo archivo.
Entrada de PostToolUse
Los hooksPostToolUse se disparan después de que una herramienta ya se ha ejecutado exitosamente. La entrada incluye tanto tool_input, los argumentos enviados a la herramienta, como tool_response, el resultado que devolvió. El esquema exacto para ambos depende de la herramienta. Las rutas de tool_input de herramienta de archivo llegan en el mismo formato que para PreToolUse: siempre absoluto, con los separadores nativos de la plataforma, así que barras invertidas en Windows. Para una herramienta MCP, la entrada también lleva el objeto mcp_server.
Control de decisión de PostToolUse
Los hooksPostToolUse pueden proporcionar retroalimentación a Claude después de la ejecución de la herramienta. Además de los campos de salida JSON disponibles para todos los hooks, tu script de hook puede devolver estos campos específicos del evento:
El ejemplo a continuación reemplaza la salida de una llamada
Bash. El valor de reemplazo coincide con la forma de salida de la herramienta Bash:
Anotar un resultado para el clasificador de modo automático
DevuelveclassifierContext para enviar una nota breve sobre el resultado de la llamada de herramienta al clasificador de modo automático en lugar de a Claude. El clasificador nunca recibe resultados de herramientas en sí, por lo que este campo es la forma soportada de decirle algo sobre lo que devolvió una llamada antes de que revise acciones posteriores. El campo requiere Claude Code v2.1.236 o posterior.
El ejemplo a continuación dice al clasificador de dónde vino la salida de una consulta:
- Hooks configurados en Claude Code: para hooks de archivos de configuración, plugins, skills y frontmatter del agente, el clasificador trata la nota como contexto no verificado proporcionado por la aplicación. La nota nunca establece intención del usuario, y si afirma que aprobaste o solicitaste algo, el clasificador verifica esa afirmación contra tus propios mensajes en la conversación
- Callbacks del Agent SDK en proceso: cuando una aplicación que integra Claude Code registra el hook como un callback del SDK de TypeScript y devuelve la nota durante la sesión en vivo, el clasificador puede pesar una declaración del usuario retransmitida en la nota como intención del usuario. Tal declaración puede satisfacer un requisito de consentimiento que el clasificador aceptaría de un mensaje que envíes, pero nunca levanta un bloqueo que tu propio mensaje tampoco pudiera levantar. Después de que se reanuda una sesión, Claude Code trata las notas restauradas como contexto no verificado. Cuando hooks de ambos grupos anotan la misma llamada, el clasificador trata la nota combinada como no verificada
- Longitud: Claude Code limita las notas para una llamada de herramienta a 2,000 caracteres y trunca el resto. El límite se comparte en cada hook que responde a esa llamada
- Solo respuestas sincrónicas: Claude Code ignora el campo en la respuesta de un hook que se ejecuta en segundo plano, porque esa respuesta llega después de que Claude Code registra el resultado de la herramienta
- Llamadas que el clasificador no registra: la transcripción del clasificador omite búsquedas de solo lectura como lecturas de archivo y búsquedas. Claude Code descarta una nota adjunta a una de esas llamadas
- Interacción con reescrituras: cuando la nota describe salida que estás reemplazando con
updatedToolOutput, devuelve ambos campos en la misma respuesta del hook. Claude Code descarta la nota si esa reescritura se rechaza u otro hook la reemplaza. Claude Code entrega una nota que devuelves sin una reescritura incluso cuando otro hook reescribe la salida
PostToolUseFailure
Se ejecuta cuando una herramienta que comenzó a ejecutarse falla: la herramienta lanzó un error, o una herramienta MCP devolvió un resultado de error. Úsalo para registrar fallos, enviar alertas o proporcionar retroalimentación correctiva a Claude. Coincide en nombre de herramienta, los mismos valores que PreToolUse.Este evento no se dispara para llamadas de herramienta rechazadas antes de la ejecución: un nombre de herramienta desconocido, entrada que falla en validación de esquema o específica de herramienta, o una negación de permiso. Los rechazos de validación se devuelven como resultados
tool_use_error y ocurren antes de que se ejecuten los hooks, por lo que no disparan ni PreToolUse ni PostToolUseFailure. Las negaciones de permiso disparan PreToolUse pero no este evento; consulta PermissionDenied.Entrada de PostToolUseFailure
Los hooks PostToolUseFailure reciben los mismos campostool_name y tool_input que PostToolUse, junto con información de error como campos de nivel superior. Para una herramienta MCP, también reciben el objeto mcp_server. Por ejemplo, un comando npm test fallido podría entregar:
La cadena
error es generalmente el mismo texto que Claude recibe como resultado de la herramienta fallida. Su formato varía según la herramienta y el fallo. Clave tu hook en tool_name, is_interrupt, y la primera línea Exit code N; trata el resto de la cadena como texto de visualización, no como un formato estable.
- Para Bash y PowerShell, un comando que se ejecutó y salió produce una primera línea
Exit code N, luego cualquier salida que el comando produjo como un bloque con stdout y stderr intercalados - Un payload también puede llevar un mensaje de fallo desnudo sin línea de código de salida, cuando Claude Code no pudo iniciar el proceso de shell en sí
- Claude Code trunca a mitad de cadenas largas alrededor de un marcador
... [N characters truncated] ..., e puede insertar líneas propias, comoCommand timed out after 2m 0s
Control de decisión de PostToolUseFailure
Los hooksPostToolUseFailure pueden proporcionar contexto a Claude después de un fallo de herramienta. Además de los campos de salida JSON disponibles para todos los hooks, tu script de hook puede devolver estos campos específicos del evento:
PostToolBatch
Se ejecuta una vez después de que cada llamada de herramienta en un lote se haya resuelto, antes de que Claude Code envíe la siguiente solicitud al modelo.PostToolUse se dispara una vez por herramienta, lo que significa que se dispara concurrentemente cuando Claude hace llamadas de herramienta paralelas. PostToolBatch se dispara exactamente una vez con el lote completo, por lo que es el lugar correcto para inyectar contexto que dependa del conjunto de herramientas que se ejecutaron en lugar de en cualquier herramienta única. No hay matcher para este evento.
Entrada de PostToolBatch
Además de los campos de entrada comunes, los hooks PostToolBatch recibentool_calls, un array que describe cada llamada de herramienta en el lote:
tool_response contiene el mismo contenido que el modelo recibe en el bloque tool_result correspondiente. El valor es una cadena serializada o array de bloque de contenido, exactamente como lo emitió la herramienta. Las respuestas pueden ser grandes, así que analiza solo los campos que necesitas.
La forma
tool_response difiere de la de PostToolUse. PostToolUse pasa el objeto Output estructurado de la herramienta, como {filePath: "...", type: "create"} para Write; PostToolBatch pasa el contenido tool_result serializado que el modelo ve.Control de decisión de PostToolBatch
Los hooksPostToolBatch pueden inyectar contexto para Claude. Además de los campos de salida JSON disponibles para todos los hooks, tu script de hook puede devolver estos campos específicos del evento:
decision: "block" o continue: false detiene el bucle agéntico antes de la siguiente llamada del modelo. El mensaje de bloqueo viene del JSON reason o stopReason, o de stderr en salida 2. Lo ves como una advertencia en la transcripción, y permanece en la conversación, por lo que Claude lo ve cuando la conversación continúa.
PermissionDenied
Se ejecuta cuando modo automático niega una llamada de herramienta, incluyendo cuando niega sin un veredicto del clasificador porque una verificación de seguridad separada del modo automático rechazó la propia solicitud del clasificador o su respuesta no se analizó. Este hook solo se dispara en modo automático: no se ejecuta cuando niegas manualmente un diálogo de permiso, cuando un hookPreToolUse bloquea una llamada, o cuando una regla deny coincide. Úsalo para registrar negaciones, ajustar configuración o decirle al modelo que puede reintentar la llamada de herramienta.
Coincide en nombre de herramienta, los mismos valores que PreToolUse.
Entrada de PermissionDenied
Además de los campos de entrada comunes, los hooks PermissionDenied recibentool_name, tool_input, tool_use_id, y reason. Para una herramienta MCP, también reciben el objeto mcp_server.
Control de decisión de PermissionDenied
Los hooks PermissionDenied pueden decirle al modelo que puede reintentar la llamada de herramienta negada. Devuelve un objeto JSON conhookSpecificOutput.retry establecido en true:
retry es true, Claude Code añade un mensaje a la conversación diciéndole al modelo que puede reintentar la llamada de herramienta. Claude Code no revierte la negación en sí. Si tu hook no devuelve JSON, o devuelve retry: false, la negación se mantiene y el modelo recibe el mensaje de rechazo original.
Claude Code ignora retry: true cuando el clasificador produjo ningún veredicto en la acción: su respuesta no se analizó, o una verificación de seguridad separada del modo automático rechazó la propia solicitud del clasificador. Para esas negaciones, Claude Code ya le dice al modelo en el mensaje de rechazo si debe reintentar más tarde o continuar.
Notification
Se ejecuta cuando Claude Code envía notificaciones. Coincide en tipo de notificación. Omite el matcher para ejecutar hooks para todos los tipos de notificación. Recibes estos eventos de hook incluso con notificaciones de escritorio desactivadas: la configuraciónpreferredNotifChannel, incluyendo notifications_disabled, cambia solo cómo se te alerta, no si tu hook se ejecuta.
Los tipos
agent_needs_input y agent_completed requieren Claude Code v2.1.198 o posterior.
Los tipos quota_auto_resume_fired, quota_auto_resume_stale, y quota_auto_resume_disabled requieren Claude Code v2.1.234 o posterior.
En sesiones de terminal, permission_prompt para una solicitud de red de un comando en sandbox requiere Claude Code v2.1.246 o posterior.
agent_needs_input para una pregunta de configuración de terminal de compañero requiere Claude Code v2.1.248 o posterior.
Los tipos
permission_prompt, idle_prompt, elicitation_dialog, y elicitation_url_dialog comparten su tiempo con notificaciones de escritorio, así que en sesiones de terminal solo los ves cuando pareces estar lejos de la terminal:- Espera
permission_promptuna vez que no hayas escrito durante aproximadamente seis segundos. El temporizador comienza cuando aparece el prompt de permiso, y cada pulsación de tecla lo difiere. Para ejecutar un hook inmediatamente cuando Claude solicita permiso para usar una herramienta, usa PermissionRequest en su lugar. - Espera
idle_promptaproximadamente 60 segundos después de que Claude termine de responder, y solo si no has escrito desde entonces. Claude Code no envíaidle_promptmientras espera a que se reinicie un límite de uso de claude.ai. Cuando la espera termina por sí sola, uno de los tiposquota_auto_resume_*se dispara en su lugar. - Espera
elicitation_dialogpara un formulario de elicitación, oelicitation_url_dialogpara una solicitud de URL de navegador, una vez que no hayas escrito durante aproximadamente seis segundos. Ambos comparten la misma puerta de seis segundos quepermission_prompt: el temporizador comienza cuando aparece el diálogo, y cada pulsación de tecla lo difiere.
permission_prompt diferente en sesiones donde envía solicitudes de permiso al callback canUseTool del Agent SDK, que es cómo Claude Desktop y la extensión VS Code alojan Claude Code:
- Espera
permission_promptaproximadamente seis segundos después de que Claude solicita permiso. Claude Code no lo difiere mientras escribes. - Si tú o un hook PermissionRequest respondes antes, Claude Code no ejecuta
permission_prompt. - Establece
CLAUDE_CODE_DISABLE_PERMISSION_PROMPT_NOTIFY_HOOKSen1para desactivarpermission_prompten estas sesiones.
permission_prompt no se disparaba en estas sesiones.
Usa matchers separados para ejecutar diferentes manejadores dependiendo del tipo de notificación. Esta configuración activa un script de alerta específico de permiso cuando Claude necesita aprobación de permiso y una notificación diferente cuando Claude ha estado inactivo:
Entrada de Notification
Además de los campos de entrada comunes, los hooks Notification recibenmessage con el texto de notificación, un title opcional, y notification_type indicando qué tipo se disparó.
systemMessage y continue pero aún emite terminalSequence, que es en lo que se basa el ejemplo de notificación de escritorio. Los hooks Notification están destinados a efectos secundarios como reenviar la notificación a un servicio externo.
SubagentStart
Se ejecuta cuando Claude genera un subagente con la herramienta Agent, cuando Claude reanuda un subagente, y cada vez que un equipo de agentes compañero en proceso maneja un nuevo mensaje. Admite matchers para filtrar por nombre de tipo de agente. Para agentes integrados, este es el nombre del agente comogeneral-purpose, Explore, o Plan. Para subagentes personalizados, este es el campo name del frontmatter del agente, no el nombre del archivo.
Para subagentes enviados por un plugin, el tipo de agente es el identificador con alcance de plugin como my-plugin:reviewer, no el nombre de frontmatter desnudo. El colon coloca un nombre con alcance de plugin en la ruta de expresión regular, así que ancla el matcher con ^ y $ para una coincidencia exacta: ^my-plugin:reviewer$.
Entrada de SubagentStart
Además de los campos de entrada comunes, los hooks SubagentStart recibenagent_id con el identificador único para el subagente y agent_type con el nombre del agente que el matcher filtra.
SubagentStop
Se ejecuta cuando un subagente de Claude Code ha terminado de responder. Coincide en tipo de agente, los mismos valores que SubagentStart.Entrada de SubagentStop
Además de los campos de entrada comunes, los hooks SubagentStop recibenstop_hook_active, agent_id, agent_type, agent_transcript_path, y last_assistant_message. El campo agent_type es el valor utilizado para filtrado de matcher. El transcript_path es la transcripción de la sesión principal, mientras que agent_transcript_path es la propia transcripción del subagente almacenada en una carpeta subagents/ anidada. El campo last_assistant_message contiene el contenido de texto de la respuesta final del subagente, por lo que los hooks pueden acceder a él sin analizar el archivo de transcripción.
No cada evento SubagentStop proviene de un subagente que Claude generó. Claude Code también ejecuta agentes internos para algunas de sus propias características, como sugerencias de prompt y preguntas secundarias /btw, y SubagentStop se dispara cuando uno de esos termina también. Para esos eventos, agent_type es el nombre del agente que la sesión en sí ejecuta, como uno establecido con --agent o la configuración agent, y una cadena vacía cuando la sesión se ejecuta sin uno.
Un matcher que nombra tipos de agentes no coincide con un agent_type vacío. Un hook cuyo matcher está omitido, "", o "*", o es una expresión regular que coincide con una cadena vacía, se ejecuta para eventos con un agent_type vacío también.
En Claude Code v2.1.271 o posterior, un subagente que se ejecuta con la herramienta SubagentHandback entrega su informe a través de esa herramienta antes de que se detenga. El campo last_assistant_message entonces contiene el texto de cierre del subagente, si lo hay, que no es el informe entregado. El informe es la entrada message de esa llamada, que un hook PreToolUse o PostToolUse que coincide en SubagentHandback recibe como tool_input.message.
Los hooks SubagentStop también reciben los arrays background_tasks y session_crons descritos en Entrada de Stop. Ambos arrays están limitados a la sesión padre, no al subagente.
hookSpecificOutput.additionalContext con hookEventName establecido en "SubagentStop", para retroalimentación sin error que mantiene el subagente ejecutándose. Devolver decision: "block" con un reason mantiene el subagente ejecutándose y entrega reason al subagente como su siguiente instrucción. Un hook que bloquea saliendo con 2 entrega su mensaje stderr de la misma manera. Para inyectar contexto en la sesión padre después de que un subagente devuelve, usa un hook PostToolUse en la herramienta Agent en su lugar.
TaskCreated
Se ejecuta cuando se está creando una tarea a través de la herramientaTaskCreate. Úsalo para aplicar convenciones de nomenclatura, requerir descripciones de tareas o evitar que se creen ciertas tareas. En una sesión sin las herramientas Task, este evento no se dispara.
Los hooks TaskCreated no admiten matchers y se disparan en cada ocurrencia.
Entrada de TaskCreated
Además de los campos de entrada comunes, los hooks TaskCreated recibentask_id, task_subject, y opcionalmente task_description, teammate_name, y team_name.
Control de decisión de TaskCreated
Un hook TaskCreated puede bloquear la creación de dos formas. De cualquier forma, Claude Code elimina la tarea y devuelve tu mensaje a Claude como el error de la herramienta. Claude Code ignoracontinue: false de este evento y Claude sigue trabajando.
- Código de salida 2: Claude Code devuelve el texto stderr como el mensaje.
- JSON
{"decision": "block", "reason": "..."}: Claude Code devuelvereasoncomo el mensaje.
TaskCompleted
Se ejecuta cuando se está marcando una tarea como completada. Esto se dispara en dos situaciones: cuando cualquier agente marca explícitamente una tarea como completada a través de la herramienta TaskUpdate, o cuando un equipo de agentes compañero termina su turno con tareas en progreso. Úsalo para aplicar criterios de finalización como pasar pruebas o verificaciones de lint antes de que una tarea pueda cerrarse. Los hooks TaskCompleted no admiten matchers y se disparan en cada ocurrencia.Entrada de TaskCompleted
Además de los campos de entrada comunes, los hooks TaskCompleted recibentask_id, task_subject, y opcionalmente task_description, teammate_name, y team_name.
Control de decisión de TaskCompleted
Los hooks TaskCompleted admiten dos formas de controlar la finalización de tareas:- Código de salida 2: la tarea no se marca como completada y el mensaje stderr se devuelve al modelo como retroalimentación.
- JSON
{"continue": false, "stopReason": "..."}: cuando un compañero terminando su turno activó el evento, detiene completamente al compañero, coincidiendo con el comportamiento del hookStop. ElstopReasonse muestra al usuario. Cuando la herramientaTaskUpdateactivó el evento, Claude Code ignoracontinue: false; el código de salida 2 aún bloquea la finalización.
Stop
Se ejecuta cuando el agente principal de Claude Code ha terminado de responder. No se ejecuta si la detención ocurrió debido a una interrupción del usuario. Los errores de API disparan StopFailure en su lugar.Entrada de Stop
Además de los campos de entrada comunes, los hooks Stop recibenstop_hook_active, last_assistant_message, background_tasks, y session_crons. El campo stop_hook_active es true cuando Claude Code ya está continuando como resultado de un hook stop. Verifica este valor o procesa la transcripción para evitar bloquear en una condición que nunca se resolverá. Claude Code aplica un límite de 8 continuaciones consecutivas: después de que los hooks stop hayan continuado el turno ocho veces seguidas, Claude Code anula el siguiente bloqueo y termina el turno. Para elevar el límite, establece CLAUDE_CODE_STOP_HOOK_BLOCK_CAP.
El campo last_assistant_message contiene el contenido de texto de la respuesta final de Claude, por lo que los hooks pueden acceder a él sin analizar el archivo de transcripción. Para hooks que actúan en el turno recién completado, como hooks de lectura en voz alta o notificación, usa este campo en lugar de leer transcript_path: el archivo de transcripción no se garantiza que incluya el mensaje final en el tiempo de Stop en todas las versiones.
Los arrays background_tasks y session_crons permiten a los hooks distinguir “sesión terminada” de “sesión pausada esperando que el trabajo de fondo la despierte”. Ambos arrays están presentes cuando el registro de tareas es alcanzable y están vacíos cuando nada está en vuelo o programado.
Cada entrada en background_tasks describe una tarea en vuelo y usa estos campos:
Cada entrada en
session_crons describe un despertar programado con alcance de sesión, originado de CronCreate, ScheduleWakeup, y /loop:
Este ejemplo muestra una entrada de Stop con una tarea de shell en vuelo y un cron recurrente:
Control de decisión de Stop
Los hooksStop y SubagentStop pueden controlar si Claude continúa. Además de los campos de salida JSON disponibles para todos los hooks, tu script de hook puede devolver estos campos específicos del evento:
Un hook que bloquea saliendo con 2 se enruta de la misma manera que
reason: Claude recibe el mensaje stderr como la explicación de por qué debe continuar.
additionalContext cuando el hook está funcionando como se diseñó y dando orientación a Claude, como “ejecuta la suite de pruebas antes de terminar”. Mantiene la conversación a través de las mismas protecciones de bucle que decision: "block", a saber la entrada stop_hook_active y el límite de 8 continuaciones consecutivas, pero la transcripción la etiqueta como Stop hook feedback y no se muestra ninguna notificación de error de hook:
StopFailure
Se ejecuta en lugar de Stop cuando el turno termina debido a un error de API. Claude Code ignora la salida y el código de salida del hook, aparte determinalSequence. Úsalo para registrar fallos, enviar alertas o tomar acciones de recuperación cuando Claude no puede completar una respuesta debido a límites de velocidad, problemas de autenticación u otros errores de API.
Entrada de StopFailure
Además de los campos de entrada comunes, los hooks StopFailure recibenerror, error_details opcional, y last_assistant_message opcional. El campo error identifica el tipo de error y se utiliza para filtrado de matcher.
TeammateIdle
Se ejecuta cuando un compañero de equipo de agentes está a punto de quedarse inactivo después de terminar su turno. Úsalo para aplicar puertas de calidad antes de que un compañero deje de trabajar, como requerir verificaciones de lint aprobadas o verificar que existan archivos de salida. Los hooks TeammateIdle no admiten matchers y se disparan en cada ocurrencia.Entrada de TeammateIdle
Además de los campos de entrada comunes, los hooks TeammateIdle recibenteammate_name y team_name.
Control de decisión de TeammateIdle
Los hooks TeammateIdle admiten dos formas de controlar el comportamiento del compañero:- Código de salida 2: el compañero recibe el mensaje stderr como retroalimentación y continúa trabajando en lugar de quedarse inactivo.
- JSON
{"continue": false, "stopReason": "..."}: detiene completamente al compañero, coincidiendo con el comportamiento del hookStop. ElstopReasonse muestra al usuario.
ConfigChange
Se ejecuta cuando un archivo de configuración cambia durante una sesión. Úsalo para auditar cambios de configuración, aplicar políticas de seguridad o bloquear modificaciones no autorizadas a archivos de configuración. Claude Code ejecuta hooks ConfigChange cuando un archivo de configuración, un archivo de política gestionada o un archivo de skill cambia. Para política gestionada, los ejecuta solo cuandomanaged-settings.json o un archivo en managed-settings.d/ cambia. Aplica configuración gestionada por servidor y cambios a preferencias gestionadas de macOS o política de registro de Windows sin ejecutarlos. En WSL con wslInheritsWindowsSettings, también aplica un archivo de configuración gestionada de Windows modificado en su sondeo de política sin ejecutarlos.
El matcher filtra en la fuente de configuración:
Este ejemplo registra todos los cambios de configuración para auditoría de seguridad:
Entrada de ConfigChange
Además de los campos de entrada comunes, los hooks ConfigChange recibensource y opcionalmente file_path. El campo source indica qué tipo de configuración cambió, y file_path proporciona la ruta al archivo específico que se modificó.
Control de decisión de ConfigChange
Los hooks ConfigChange pueden bloquear cambios de configuración de tomar efecto. Usa código de salida 2 o un JSONdecision para evitar el cambio. Cuando se bloquea, la nueva configuración no se aplica a la sesión en ejecución.
policy_settings no pueden ser bloqueados. Los hooks aún se disparan para fuentes policy_settings cuando un archivo de configuración gestionada en la máquina cambia, para que puedas usarlos para registrar esas ediciones, pero cualquier decisión de bloqueo se ignora. Esto asegura que la configuración gestionada por empresa siempre tenga efecto. Claude Code no ejecuta hooks ConfigChange cuando llega o se actualiza configuración gestionada por servidor.
Claude Code actúa sobre la decisión de bloqueo de la salida JSON de un hook ConfigChange y descarta systemMessage y continue. Un cambio bloqueado no muestra ningún mensaje para ti o para Claude, ya sea que bloquees con reason o con stderr en salida 2. Claude Code solo escribe una línea en el registro de depuración.
CwdChanged
Se ejecuta cuando un comando de shell en la conversación principal cambia el directorio de trabajo, por ejemplo cuando Claude ejecuta un comandocd. Úsalo para reaccionar a cambios de directorio: recargar variables de entorno, activar cadenas de herramientas específicas del proyecto o ejecutar scripts de configuración automáticamente. Se empareja con FileChanged para herramientas como direnv que gestionan el entorno por directorio.
Los hooks CwdChanged tienen acceso a CLAUDE_ENV_FILE. Las variables escritas en ese archivo persisten en comandos Bash posteriores hasta el siguiente evento CwdChanged, cuando Claude Code las borra.
CwdChanged no admite matchers y se dispara en cada ocurrencia.
Entrada de CwdChanged
Además de los campos de entrada comunes, los hooks CwdChanged recibenold_cwd y new_cwd.
Salida de CwdChanged
Además de los campos de salida JSON disponibles para todos los hooks, los hooks CwdChanged pueden devolverwatchPaths para establecer dinámicamente qué rutas de archivo FileChanged observa:
Los hooks CwdChanged no tienen control de decisión. No pueden bloquear el cambio de directorio.
Claude Code lee
watchPaths y systemMessage de su salida JSON y descarta continue. En sesiones interactivas, muestra el systemMessage como una breve notificación de terminal. El mensaje no llega al flujo de mensajes del SDK.
DirectoryAdded
Se ejecuta después de que añadas un directorio de trabajo a mitad de sesión con el comando/add-dir, o después de que un cliente del SDK añada uno con la solicitud de control register_repo_root. Úsalo para preparar un repositorio recién añadido, por ejemplo instalando sus dependencias.
Claude Code no dispara este evento cuando:
- Pasas un directorio con la bandera de inicio
--add-dir; SessionStart cubre esos directorios - Añades un directorio en la pestaña Workspace
/permissions - Añades un directorio que ya es un directorio de trabajo o está dentro de uno
Entrada de DirectoryAdded
Además de los campos de entrada comunes, los hooks DirectoryAdded recibendirectory y source.
continue de su salida JSON y muestra el resto diferente por fuente:
slash_command: Claude Code entrega elsystemMessagedel hook a Claude como contexto en el siguiente turno de conversación, en lugar de mostrártelo. Un recuento de hooks fallidos aparece en la transcripción. La salida de fallo completo va al registro de depuraciónregister_repo_root: Claude Code escribe la salidasystemMessagey la salida de fallo solo en el registro de depuración
FileChanged
Se ejecuta cuando un archivo observado cambia en el disco. Claude Code detecta cambios con un observador del sistema de archivos, no inspeccionando llamadas de herramientas, por lo que ejecuta el hook sin importar qué cambió el archivo: una llamada de herramientaEdit o Write, un script que Claude ejecuta con Bash, o un proceso fuera de Claude Code completamente. Un uso común es recargar variables de entorno cuando cambian archivos de configuración del proyecto.
El matcher para este evento sirve dos roles:
- Construir la lista de observación: el valor se divide en
|y cada segmento se registra como un nombre de archivo literal en el directorio de trabajo, así que".envrc|.env"observa exactamente esos dos archivos. Los patrones regex no son útiles aquí: un valor como^\.envobservaría un archivo literalmente nombrado^\.env. - Filtrar qué hooks se ejecutan: cuando un archivo observado cambia, el mismo valor filtra qué grupos de hook se ejecutan usando las reglas de matcher estándar contra el nombre base del archivo cambiado.
data.csv después de cualquier cambio, incluyendo un comando Bash o un script externo reescribiendo el archivo:
file_path de la entrada JSON en stdin. Su guardia grep prueba lo mismo que perl elimina, un CR al final de una línea, así que la ejecución después de una normalización sale sin tocar el archivo. Una guardia más suelta se repite para siempre, porque perl -i reescribe el archivo incluso cuando no sustituye nada y Claude Code ejecuta el hook nuevamente después de cada reescritura. Guarda este script en /path/to/normalize-line-endings.sh y hazlo ejecutable:
data.csv con un comando Bash. Claude Code ejecuta el hook y el archivo termina con terminaciones LF.
Para observar archivos que no puedes nombrar por adelantado, devuelve watchPaths de un hook para actualizar la lista de observación dinámicamente. Claude Code comienza el observador solo cuando algo nombra un archivo para observar, así que siembra la lista con un grupo FileChanged cuyo matcher nombra al menos un archivo, o con un hook SessionStart o CwdChanged que devuelve watchPaths. El matcher aún filtra qué grupos de hook se ejecutan cuando un archivo observado cambia, así que da al grupo que maneja rutas dinámicas un matcher omitido, que coincide con cada archivo observado y no añade nada a la lista de observación. Un matcher "*" también coincide con cada archivo, pero Claude Code lo registra en la lista de observación como cualquier otro valor, como un archivo literal nombrado *.
Los hooks FileChanged tienen acceso a CLAUDE_ENV_FILE. Las variables escritas en ese archivo persisten en comandos Bash posteriores hasta el siguiente evento CwdChanged, cuando Claude Code las borra.
Entrada de FileChanged
Además de los campos de entrada comunes, los hooks FileChanged recibenfile_path y event.
Salida de FileChanged
Además de los campos de salida JSON disponibles para todos los hooks, los hooks FileChanged pueden devolverwatchPaths para actualizar dinámicamente qué rutas de archivo se observan:
Los hooks FileChanged no tienen control de decisión. No pueden bloquear el cambio de archivo de ocurrir.
Claude Code lee
watchPaths y systemMessage de su salida JSON y descarta continue. En sesiones interactivas, muestra el systemMessage como una breve notificación de terminal. El mensaje no llega al flujo de mensajes del SDK.
WorktreeCreate
Se ejecuta cuando se está creando un worktree, ya sea desdeclaude --worktree, desde un subagente usando isolation: "worktree", o para una sesión en segundo plano que Claude Code aísla en su propio worktree. Por defecto Claude Code crea la copia de trabajo aislada con git worktree. Configurar un hook WorktreeCreate reemplaza ese comportamiento git predeterminado, permitiéndote usar un sistema de control de versiones diferente como SVN, Perforce o Mercurial.
Debido a que el hook reemplaza el comportamiento predeterminado completamente, .worktreeinclude no se procesa. Si necesitas copiar archivos de configuración local como .env en el nuevo worktree, hazlo dentro de tu script de hook.
El hook debe devolver la ruta al directorio del worktree creado. Claude Code usa esta ruta como el directorio de trabajo para la sesión aislada. Consulta Salida de WorktreeCreate para saber cómo cada tipo de hook devuelve la ruta.
Claude Code actúa sobre el éxito del hook y la ruta devuelta, y descarta systemMessage y continue.
Este ejemplo crea una copia de trabajo SVN e imprime la ruta para que Claude Code la use. Reemplaza la URL del repositorio con la tuya:
name del worktree de la entrada JSON en stdin, verifica una copia fresca en un nuevo directorio e imprime la ruta del directorio. El echo en la última línea es lo que Claude Code lee como la ruta del worktree. Redirige cualquier otra salida a stderr para que no interfiera con la ruta.
Entrada de WorktreeCreate
Además de los campos de entrada comunes, los hooks WorktreeCreate reciben el camponame. Este es un identificador slug para el nuevo worktree, ya sea especificado por el usuario o auto-generado, por ejemplo bold-oak-a3f2.
Salida de WorktreeCreate
Los hooks WorktreeCreate no usan el modelo de decisión de permitir/bloquear estándar. En su lugar, el éxito o fallo del hook determina el resultado. El hook debe devolver la ruta al directorio del worktree creado:- Hooks de comando (
type: "command"): imprime la ruta como la última línea no vacía de stdout. Claude Code elimina códigos de escape ANSI antes de leer esa línea, así que los banners de inicio de shell impresos antes de tuechose ignoran. Redirige cualquier otra salida del hook a stderr. - Hooks HTTP (
type: "http"): devuelve{ "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } }en el cuerpo de la respuesta.
. o .. en él. Si la ruta resultante no es un directorio en el que Claude Code pueda entrar, la sesión imprime un error nombrando la ruta y sale con código 1.
Claude Code rechaza una ruta absoluta que contiene segmentos . o .., y cualquier ruta que pase a través de un enlace simbólico por debajo de la raíz del repositorio, porque un enlace simbólico comprometido en el repositorio podría redirigir el worktree fuera de él. El error nombra el componente rechazado. Devuelve una ruta normalizada que no pase a través de un enlace simbólico dentro del repositorio. Antes de v2.1.216, la creación del worktree seguía la ruta del hook sin este cribado.
WorktreeRemove
Se ejecuta cuando se está eliminando un worktree. Este es el homólogo de limpieza de WorktreeCreate. El evento se dispara cuando:- sales de una sesión
--worktreey eliges eliminarlo - un subagente con
isolation: "worktree"se completa - eliminas una sesión en segundo plano cuyo worktree creó el hook
git worktree remove. Si configuraste un hook WorktreeCreate para un sistema de control de versiones no git, emparéjalo con un hook WorktreeRemove para manejar la limpieza. Sin uno, el directorio del worktree se deja en el disco.
Claude Code descarta los campos de salida JSON de un hook WorktreeRemove, como systemMessage y continue.
Para una eliminación de sesión en segundo plano, Claude Code verifica la ruta del worktree almacenada antes de ejecutar el hook y rechaza una ruta que es un enlace simbólico o pasa a través de uno por debajo de la raíz del repositorio. El hook se ejecuta para un worktree que aún contiene archivos solo cuando confirmas la eliminación en vista de agente; para tal worktree, claude rm mantiene la sesión y el worktree en su lugar. Antes de v2.1.216, el hook se ejecutaba en la ruta almacenada sin estas verificaciones.
Claude Code pasa la ruta devuelta por WorktreeCreate como worktree_path en la entrada del hook. Este ejemplo lee esa ruta y elimina el directorio:
Entrada de WorktreeRemove
Además de los campos de entrada comunes, los hooks WorktreeRemove reciben el campoworktree_path, que es la ruta absoluta al worktree que se está eliminando.
worktree_path aún existe después, la eliminación falla:
- El worktree permanece en el disco, y el comando del hook y stderr van al registro de depuración.
- Si estabas eliminando una sesión en segundo plano, la sesión también permanece. El mensaje de rechazo en vista de agente reporta cómo terminó el hook, como
exited 1, cita el inicio de su stderr, y dice si eliminar la sesión nuevamente elimina el directorio de todas formas.
PreCompact
Se ejecuta antes de que Claude Code esté a punto de ejecutar una operación de compactación. El valor del matcher indica si la compactación fue activada manualmente o automáticamente:
Sale con código 2 para bloquear la compactación. Para un
/compact manual, el mensaje stderr se muestra al usuario. También puedes bloquear devolviendo JSON con "decision": "block".
Bloquear compactación automática tiene diferentes efectos dependiendo de cuándo se dispare. Si la compactación fue activada de forma proactiva antes del límite de contexto, Claude Code la omite y la conversación continúa sin compactar. Si la compactación fue activada para recuperarse de un error de límite de contexto ya devuelto por la API, el error subyacente aparece y la solicitud actual falla.
Claude Code descarta los campos systemMessage y continue de un hook PreCompact.
Entrada de PreCompact
Además de los campos de entrada comunes, los hooks PreCompact recibentrigger y custom_instructions. Para manual, custom_instructions contiene lo que el usuario pasa a /compact y es null cuando no pasa nada. Para auto, custom_instructions es null.
PostCompact
Se ejecuta después de que Claude Code completa una operación de compactación. Úsalo para reaccionar al nuevo estado compactado, por ejemplo para registrar el resumen generado o actualizar el estado externo. Claude Code descarta los campossystemMessage y continue de un hook PostCompact.
Los mismos valores de matcher se aplican que para PreCompact:
Entrada de PostCompact
Además de los campos de entrada comunes, los hooks PostCompact recibentrigger y compact_summary. El campo compact_summary contiene el resumen de conversación generado por la operación de compactación.
PreModelSwitch
Se ejecuta antes de que Claude Code aplique un cambio de modelo que solicitaste o un cliente solicitó. Úsalo para bloquear un cambio, requerir confirmación o mostrar cuánto costará el cambio antes de que suceda. PreModelSwitch requiere Claude Code v2.1.251 o posterior. Claude Code lo ejecuta para estas solicitudes:/model <name>y el selector/model- El selector de modelo
Option+PoAlt+P - La configuración Model en
/config - Activar modo rápido cuando eso cambia el modelo de la sesión
- Una solicitud
set_model, o un cambio de modelo en una solicitudapply_flag_settings, desde un host Agent SDK o Control Remoto
[1m]. Un alias como opus, un ID de modelo fechado, y un ID específico del proveedor como un ID de modelo de Amazon Bedrock todos coinciden con el único nombre canónico al que se resuelven, así que claude-opus-5 cubre cada deletreo de Opus 5.
Cuando Claude Code no puede determinar un nombre canónico para el objetivo, por ejemplo un ID de modelo personalizado que solo tu puerta de enlace LLM conoce, ejecuta cada hook PreModelSwitch independientemente del matcher. Un hook que bloquea debe por lo tanto verificar to_model de su entrada en lugar de confiar solo en el matcher.
Escribe el matcher como un nombre exacto, una lista separada por | como claude-opus-4-6|claude-opus-5, o una expresión regular como .*opus.*. Este ejemplo usa un matcher de nombre exacto y también verifica to_model de la entrada del hook, así que rechaza un cambio a Opus 4.6 saliendo con código 2 y permite cualquier otro objetivo:
- macOS/Linux
- Windows (PowerShell)
El comando verifica
to_model con jq:/model claude-opus-4-6 desde una sesión que ejecuta un modelo diferente. Claude Code mantiene el modelo actual e informa que un hook PreModelSwitch bloqueó el cambio, con tu mensaje como la razón.
Entrada de PreModelSwitch
Además de los campos de entrada comunes, los hooks PreModelSwitch reciben los campos en esta tabla. Los últimos cinco describen cuánto cuesta reenviar la conversación al nuevo modelo, para que un hook pueda mostrar esa cifra antes de que suceda el cambio.
Este ejemplo muestra la entrada para
/model opus en una sesión que ejecuta Sonnet 5:
Control de decisión de PreModelSwitch
Los hooksPreModelSwitch pueden cancelar el cambio, pedir al usuario que lo confirme, o permitir que continúe. El código de salida 2 o un decision: "block" de nivel superior cancela el cambio.
Para control más fino, devuelve permissionDecision y permissionDecisionReason en un objeto hookSpecificOutput, como en PreToolUse. PreModelSwitch acepta "allow", "deny", y "ask". No acepta "defer", updatedInput, o additionalContext. La tabla a continuación describe ambos campos:
Solo
/model en una sesión interactiva puede mostrar el prompt "ask". En cada otra superficie, incluyendo modo no interactivo con la bandera -p, /config, y solicitudes set_model, Claude Code trata "ask" como un rechazo.
Este ejemplo pide al usuario que confirme y cita el recuento de tokens de context_tokens:
deny > ask > allow.
Claude Code muestra al usuario cualquier systemMessage que devuelva tu hook independientemente de la decisión, así que un hook de informe de costos puede devolver {"systemMessage": "..."} y salir 0.
Un hook PreModelSwitch que no responde antes de su tiempo de espera bloquea el cambio. En PreToolUse, por el contrario, un hook de comando que agota el tiempo de espera permite que la llamada de herramienta continúe. El tiempo de espera predeterminado para este evento es 30 segundos. PreModelSwitch ejecuta solo hooks command, http, y mcp_tool, así que los valores predeterminados prompt y agent no se aplican.
Un hook que sale con un código distinto de 0 o 2 y no imprime ninguna decisión JSON no bloquea: Claude Code muestra su stderr y aplica el cambio, como se describe en Otros códigos de salida.
PostModelSwitch
Se ejecuta después de que el modelo de la sesión cambia. Úsalo para dar orientación específica del modelo a Claude sin editar cada CLAUDE.md, por ejemplo una instrucción de toda la organización que se aplica en ciertos modelos. PostModelSwitch requiere Claude Code v2.1.251 o posterior. No puede bloquear, porque el modelo ya ha cambiado. Claude Code ejecuta hooks PostModelSwitch después de cualquiera de estos cambios:- Un cambio que solicitaste o un cliente solicitó
- Un fallback de modelo automático, que cambia el modelo de la sesión
- Una configuración como
opusplanentrando o saliendo del modo plan - Claude Code restaurando el modelo cuando reanudas una sesión
/model opus desde una sesión Sonnet, luego pregúntale a Claude qué orientación tiene sobre el modelo actual.
Entrada de PostModelSwitch
Los hooks PostModelSwitch reciben los mismos campos que PreModelSwitch, conhook_event_name establecido en "PostModelSwitch" y dos valores source más: "auto" para un fallback automático u otro cambio que Claude Code hizo por su cuenta, y "resume" para el modelo restaurado cuando reanudas una sesión.
requested_model es null cuando source es "auto". Cuando source es "resume", es la configuración de modelo guardada que Claude Code restauró.
Control de decisión de PostModelSwitch
Claude Code toma tu stdout de texto plano del hook en salida 0, oadditionalContext de salida JSON, y lo entrega a Claude con la siguiente solicitud después del cambio. Además de los campos de salida JSON disponibles para todos los hooks, puedes devolver:
Si el hook no se ha completado dentro de cinco segundos después de que envíes la siguiente solicitud, Claude Code envía esa solicitud sin la salida y la adjunta a la siguiente solicitud en su lugar. Si el modelo cambia varias veces antes de la siguiente solicitud, Claude Code entrega solo la salida para el cambio del modelo objetivo final.
SessionEnd
Se ejecuta cuando termina una sesión de Claude Code. Útil para tareas de limpieza, registrar estadísticas de sesión o guardar estado de sesión. Admite matchers para filtrar por razón de salida. El camporeason en la entrada del hook indica por qué terminó la sesión:
Entrada de SessionEnd
Además de los campos de entrada comunes, los hooks SessionEnd reciben un camporeason indicando por qué terminó la sesión. Consulta la tabla de razones anterior para todos los valores.
systemMessage.
Los hooks SessionEnd tienen un tiempo de espera predeterminado de 1.5 segundos. Se aplica cuando sales, ejecutas /clear, o cambias sesiones con /resume interactivo. Puedes dar a un hook más tiempo de dos formas:
timeoutpor hook: establecetimeouten la configuración de ese hook. El presupuesto general sube automáticamente para coincidir con eltimeoutpor hook más alto en tus archivos de configuración, hasta 60 segundos. Si subes el presupuesto de esta forma, un hook sin su propiotimeoutaún mantiene el predeterminado. Los tiempos de espera establecidos en hooks proporcionados por plugins no suben el presupuesto.CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS: establece esta variable de entorno en milisegundos para anular el presupuesto explícitamente. El valor que estableces también se convierte en el tiempo de espera para cada hook sin su propiotimeout.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS solo subía el presupuesto general, y un hook sin su propio timeout aún se cancelaba después de 1.5 segundos.
Elicitation
Se ejecuta cuando un servidor MCP solicita entrada del usuario a mitad de tarea. Por defecto, Claude Code muestra un diálogo interactivo para que el usuario responda. Los hooks pueden interceptar esta solicitud y responder programáticamente, omitiendo completamente el diálogo. El campo matcher coincide contra el nombre del servidor MCP.Entrada de Elicitation
Además de los campos de entrada comunes, los hooks Elicitation recibenmcp_server_name, message, y campos opcionales mode, url, elicitation_id, y requested_schema.
Para elicitación de modo formulario, el caso más común:
Salida de Elicitation
Para responder programáticamente sin mostrar el diálogo, devuelve un objeto JSON conhookSpecificOutput:
El código de salida 2 niega la elicitación. Claude Code no muestra tu mensaje stderr en ningún lugar.
Claude Code actúa sobre
hookSpecificOutput de la salida JSON de un hook Elicitation y descarta systemMessage y continue.
ElicitationResult
Se ejecuta después de que un usuario responda a una elicitación de MCP. Los hooks pueden observar, modificar o bloquear la respuesta antes de que se devuelva al servidor MCP. El campo matcher coincide contra el nombre del servidor MCP.Entrada de ElicitationResult
Además de los campos de entrada comunes, los hooks ElicitationResult recibenmcp_server_name, action, y campos opcionales mode, elicitation_id, y content.
Salida de ElicitationResult
Para anular la respuesta del usuario, devuelve un objeto JSON conhookSpecificOutput:
El código de salida 2 bloquea la respuesta, cambiando la acción efectiva a
decline. Claude Code no muestra tu mensaje stderr en ningún lugar.
Claude Code actúa sobre hookSpecificOutput de la salida JSON de un hook ElicitationResult y descarta systemMessage y continue.
Hooks basados en prompts
Además de hooks de comando, HTTP y herramientas MCP, Claude Code admite hooks basados en prompts (type: "prompt") que usan un LLM para evaluar si permitir o bloquear una acción, y hooks de agente (type: "agent") que generan un verificador agentico con acceso a herramientas. No todos los eventos admiten todos los tipos de hooks.
Eventos que admiten los cinco tipos de hooks (command, http, mcp_tool, prompt y agent):
PermissionDeniedPostToolBatchPostToolUsePostToolUseFailurePreToolUseStopSubagentStopTaskCompletedTaskCreatedTeammateIdleUserPromptExpansionUserPromptSubmit
PermissionRequest admite hooks command, http, mcp_tool y prompt pero no hooks agent. Si configura un hook de agente en este evento, Claude Code lo omite y el flujo de permisos continúa sin cambios. Para permitir o denegar desde un hook, devuelva el objeto de decisión desde un hook de comando o HTTP.
Eventos que admiten hooks command, http y mcp_tool pero no prompt o agent:
ConfigChangeCwdChangedDirectoryAddedElicitationElicitationResultFileChangedInstructionsLoadedMessageDisplayNotificationPostCompactPostModelSwitchPreCompactPreModelSwitchSessionEndStopFailureSubagentStartWorktreeCreateWorktreeRemove
SessionStart y Setup admiten hooks command y mcp_tool, y MCP tool hook fields describe cuándo se ejecutan sus hooks mcp_tool. No admiten hooks http, prompt o agent.
Cómo funcionan los hooks basados en prompts
En lugar de ejecutar un comando Bash, los hooks basados en prompts:- Envían la entrada del hook y su prompt a un modelo Claude, por defecto el que Claude Code usa para funcionalidad en segundo plano
- El LLM responde con JSON estructurado que contiene una decisión
- Claude Code procesa la decisión automáticamente
Configuración de hook de prompt
Establezcatype en "prompt" y proporcione una cadena prompt en lugar de un command. Use el marcador de posición $ARGUMENTS para inyectar datos de entrada JSON del hook en su texto de prompt.
Este hook Stop le pide al LLM que evalúe si todas las tareas están completas antes de permitir que Claude finalice:
Esquema de respuesta
El LLM debe responder con JSON que contenga:
Lo que sucede en
ok: false depende del evento:
StopySubagentStop: la razón se retroalimenta a Claude como su siguiente instrucción y el turno continúa, a menos que la respuesta también establezcaimpossible: true, en cuyo caso Claude Code permite la detención y el turno terminaPreToolUse: la llamada de herramienta se deniega; por defecto el turno termina y la razón de denegación aparece en el chat como una línea de advertencia. EstablezcacontinueOnBlock: truepara devolver la razón a Claude como el error de la herramienta para que pueda ajustarse y continuar, equivalente a un hook de comando conpermissionDecision: "deny". Antes de v2.1.210, la razón de denegación se devolvía a Claude como el error de la herramienta y el turno continuabaPostToolUse: por defecto el turno termina y la razón aparece en el chat como una línea de advertencia. EstablezcacontinueOnBlock: truepara retroalimentar la razón a Claude y continuar el turno en lugar de detenerPostToolBatch,UserPromptSubmityUserPromptExpansion: el turno termina y la razón aparece como una línea de advertencia. Estos eventos terminan el turno endecision: "block"independientemente decontinuePostToolUseFailureyTaskCreated: la razón se devuelve a Claude como un error de herramienta y el turno continúa, independientemente decontinueOnBlockTaskCompleted: cuando se activa porque una tarea se marca como completada durante un turno, la razón se devuelve a Claude como un error de herramienta y el turno continúa, independientemente decontinueOnBlock. Cuando se activa porque un compañero se detiene, se comporta comoTeammateIdley detiene al compañero por defectoTeammateIdle: por defecto el compañero se detiene y la razón aparece como una línea de advertencia. EstablezcacontinueOnBlock: truepara retroalimentar la razón al compañero y mantenerlo trabajando en su lugarPermissionRequest:ok: falseno tiene efecto. Para denegar una aprobación desde un hook, use un hook de comando que devuelvahookSpecificOutput.decision.behavior: "deny"PermissionDenied:ok: falseno tiene efecto porque la denegación ya sucedió. La única salida que este evento lee eshookSpecificOutput.retry, que los hooks de prompt y agente no pueden establecer. Se ejecutan en este evento, pero su salida se descarta. Use un hook de comando para devolverretry
Verificar múltiples condiciones antes de detener
Este hookStop usa un prompt detallado para verificar tres condiciones antes de permitir que Claude se detenga. Los hooks SubagentStop usan el mismo formato para evaluar si un subagente debe detenerse. Si el modelo devuelve "ok": false porque la condición aún no se cumple, Claude continúa trabajando con la razón proporcionada como su siguiente instrucción:
Hooks basados en agentes
Los hooks basados en agentes (type: "agent") son como hooks basados en prompts pero con acceso a herramientas de múltiples turnos. En lugar de una única llamada LLM, un hook de agente genera un subagente que puede leer archivos, buscar código e inspeccionar la base de código para verificar condiciones. Los hooks de agente admiten los mismos eventos que los hooks basados en prompts, excepto PermissionRequest.
Cómo funcionan los hooks de agente
Cuando se activa un hook de agente:- Claude Code genera un subagente con su prompt y la entrada JSON del hook
- El subagente puede usar herramientas como Read, Grep y Glob para investigar
- Después de hasta 50 turnos, el subagente devuelve una decisión estructurada
{ "ok": true/false } - Claude Code permite la acción si
okestrue. Siokesfalse, Claude Code maneja el bloqueo de la misma manera que un hook de prompt concontinueOnBlock: trueen ese evento, como se indica en Esquema de respuesta
Configuración de hook de agente
Establezcatype en "agent" y proporcione una cadena prompt, usando $ARGUMENTS como marcador de posición para la entrada JSON del hook. Los campos de configuración son los mismos que los hooks de prompt, excepto que los hooks de agente tienen un tiempo de espera predeterminado más largo de 60 segundos y no tienen campo continueOnBlock.
El esquema de respuesta es { "ok": true } para permitir o { "ok": false, "reason": "..." } para bloquear. En ok: false, Claude Code maneja un hook de agente de la manera que maneja un hook de prompt con continueOnBlock: true en el mismo evento; los hooks de agente no tienen campo continueOnBlock y no admiten el campo impossible del hook de prompt.
Este hook Stop verifica que todas las pruebas unitarias pasen antes de permitir que Claude finalice:
Ejecutar hooks en segundo plano
Por defecto, los hooks bloquean la ejecución de Claude hasta que se completen. Para tareas de larga duración como implementaciones, conjuntos de pruebas o llamadas a API externas, establezca"async": true para ejecutar el hook en segundo plano mientras Claude continúa trabajando. Los hooks asincronos no pueden bloquear o controlar el comportamiento de Claude: campos de respuesta como decision, permissionDecision y continue no tienen efecto, porque la acción que habrían controlado ya se ha completado.
Configurar un hook asincrónico
Agregue"async": true a la configuración de un hook de comando para ejecutarlo en segundo plano sin bloquear a Claude. Este campo solo está disponible en hooks type: "command".
Este hook ejecuta un script de prueba después de cada llamada a herramienta Write. Claude continúa trabajando inmediatamente mientras run-tests.sh se ejecuta. Cuando el script finaliza, su salida se entrega en el siguiente turno de conversación:
timeout en él. Claude Code aún aplica timeout en un hook que ejecuta con asyncRewake.
Claude Code entrega los resultados de un hook asincrónico solo mientras se ejecuta la sesión:
- En modo no interactivo con la bandera
-p, Claude Code mata cualquier hook asincrónico que aún se esté ejecutando al finalizar y lo finaliza con resultadocancelled - Si el trabajo de su hook debe sobrevivir a una sesión
claude -p, inicie un proceso completamente desacoplado desde él
Cómo se ejecutan los hooks asincronos
Cuando se activa un hook asincrónico, Claude Code inicia el proceso del hook e inmediatamente continúa sin esperar a que finalice. El hook recibe la misma entrada JSON a través de stdin que un hook sincrónico. Después de que el proceso de fondo sale, Claude Code entrega los camposadditionalContext y systemMessage de la respuesta JSON del hook a Claude en el siguiente turno de conversación. A diferencia del systemMessage de un hook sincrónico, ninguno de estos campos se le muestra a usted.
Claude Code valida esa respuesta JSON contra el mismo esquema de salida que los hooks sincronos, y descarta cualquier campo cuyo valor tenga el tipo incorrecto, como un systemMessage que no sea una cadena, en lugar de entregarlo. Ejecute con --debug para ver una advertencia que nombre cada campo descartado. Antes de v2.1.202, la salida JSON malformada de un hook asincrónico podría bloquear la sesión, y el bloqueo se repetía cada vez que se reanudaba la sesión.
Las notificaciones de finalización de hooks asincronos se suprimen por defecto. Para verlas, habilite el modo detallado con Ctrl+O o inicie Claude Code con --verbose.
Ejecutar pruebas después de cambios de archivo
Este hook inicia un conjunto de pruebas en segundo plano cada vez que Claude escribe un archivo, luego reporta los resultados a Claude cuando las pruebas finalizan. Guarde este script en.claude/hooks/run-tests-async.sh en su proyecto y hágalo ejecutable con chmod +x:
.claude/settings.json en la raíz de su proyecto. La bandera async: true permite que Claude continúe trabajando mientras se ejecutan las pruebas:
Limitaciones
Los hooks asincronos tienen restricciones adicionales en comparación con los hooks sincronos:- La salida del hook se entrega en el siguiente turno de conversación. Si la sesión está inactiva, la respuesta espera hasta la siguiente interacción del usuario. Excepción: un hook
asyncRewakeque sale con código 2 despierta a Claude inmediatamente incluso cuando la sesión está inactiva. - Cada ejecución crea un proceso de fondo separado. No hay deduplicación en múltiples activaciones del mismo hook asincrónico.
Consideraciones de seguridad
Descargo de responsabilidad
Confianza del espacio de trabajo
Claude Code verifica la confianza del espacio de trabajo antes de ejecutar cualquier hook desde un archivo de configuración. Lo que cuenta como confiable depende del tipo de sesión:- Sesión interactiva: Claude Code retiene los hooks de todos los archivos de configuración, incluido su propio
~/.claude/settings.json, hasta que acepte el diálogo de confianza del espacio de trabajo para la carpeta, o para un directorio principal cuya confianza se extienda a ella - Sesión
-po SDK: Claude Code nunca muestra el diálogo y trata la carpeta como confiable, por lo que los hooks confirmados en el.claude/settings.jsonde un repositorio se ejecutan en una carpeta que nunca ha confiado
claude -p en un repositorio que no escribió, revise sus archivos de configuración .claude/, comience con --bare, o desactive los hooks para esa ejecución con --settings '{"disableAllHooks": true}'. Los hooks de frontmatter en un subagente de proyecto siguen una regla más estricta que los hooks de archivo de configuración. Lo que se ejecuta antes de confiar en una carpeta enumera cada tipo de contenido de repositorio por tipo de sesión.
Mejores prácticas de seguridad
Tenga en cuenta estas prácticas al escribir hooks:- Validar y sanitizar entradas: nunca confíe en datos de entrada ciegamente
- Siempre entrecomillar variables de shell: use
"$VAR"no$VAR - Bloquear traversal de ruta: verifique
..en rutas de archivo - Usar rutas absolutas: especifique rutas completas para scripts. En forma exec, use
${CLAUDE_PROJECT_DIR}y la ruta no necesita entrecomillarse. En forma shell, envuélvala en comillas dobles - Omitir archivos sensibles: evite
.env,.git/, claves, etc.
Herramienta PowerShell en Windows
En Windows, puede ejecutar hooks individuales en PowerShell estableciendo"shell": "powershell" en un hook de comando. Claude Code detecta automáticamente pwsh.exe, el ejecutable de PowerShell 7 y posterior, y recurre a powershell.exe para Windows PowerShell 5.1.
${CLAUDE_PROJECT_DIR} o $env:CLAUDE_PROJECT_DIR. A partir de v2.1.198, Claude Code reescribe los marcadores de posición ${CLAUDE_PROJECT_DIR}, ${CLAUDE_PLUGIN_ROOT} y ${CLAUDE_PLUGIN_DATA} en un comando de forma shell de PowerShell a la forma ${env:NAME} de PowerShell, ya sea que el hook esté definido en settings.json, un plugin o una skill. PowerShell luego resuelve el valor del entorno exportado después del análisis, por lo que el marcador de posición funciona dentro de cadenas entre comillas dobles pero no dentro de cadenas entre comillas simples, donde PowerShell nunca expande variables.
Antes de v2.1.198, esta reescritura se aplicaba solo a hooks de plugins. En versiones anteriores, un hook de settings.json necesita la forma $env: o forma exec, donde ${CLAUDE_PROJECT_DIR} se sustituye en cada elemento args independientemente de dónde se defina el hook.
No escriba la ortografía desnuda $CLAUDE_PROJECT_DIR en un hook de PowerShell. PowerShell la analiza como una variable local indefinida y la resuelve a $null, lo que deja la ruta del script sin su prefijo de directorio raíz del proyecto. Claude Code no reescribe esa forma; en su lugar, registra una advertencia en el registro de depuración.
El ejemplo a continuación muestra un hook de settings.json que ejecuta un script del proyecto con la forma $env:, que funciona en todas las versiones:
Depurar hooks
Los detalles de ejecución de hooks se escriben en el archivo de registro de depuración. Inicie Claude Code conclaude --debug-file <path> para escribir el registro en una ubicación conocida, o ejecute claude --debug y lea el registro en ~/.claude/debug/<session-id>.txt. La bandera --debug no imprime en la terminal.
Por ejemplo, un hook PostToolUse en Write cuyo comando imprime hook-ran produce entradas como:
CLAUDE_CODE_DEBUG_LOG_LEVEL=verbose para ver líneas de registro adicionales como recuentos de matchers de hooks y coincidencia de consultas.
Para solucionar problemas comunes como hooks que no se activan, hooks Stop que siguen bloqueando, o errores de configuración, consulte Limitaciones y solución de problemas en la guía. Para un recorrido de diagnóstico más amplio que cubra /context, /doctor y precedencia de configuración, consulte Depure su configuración.