Saltar al contenido principal
Para una guía de inicio rápido con ejemplos, consulte Automatizar flujos de trabajo con hooks.
Los hooks son comandos de shell definidos por el usuario, puntos finales HTTP o prompts de LLM que se ejecutan automáticamente en puntos específicos del ciclo de vida de Claude Code. Utilice esta referencia para buscar esquemas de eventos, opciones de configuración, formatos de entrada/salida JSON y características avanzadas como hooks asincronos, hooks HTTP y hooks de herramientas MCP. Si está configurando hooks por primera vez, comience con la guía en su lugar.

Ciclo de vida de los hooks

Los hooks se activan en puntos específicos durante una sesión de Claude Code. 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. Algunos eventos se activan una vez por sesión, mientras que otros se activan repetidamente dentro del bucle agentico:
Diagrama del ciclo de vida de hooks que muestra la secuencia de hooks desde SessionStart a través del bucle agentico (PreToolUse, PermissionRequest, PostToolUse, SubagentStart/Stop, TaskCreated, TaskCompleted) hasta Stop o StopFailure, TeammateIdle, PreCompact, PostCompact y SessionEnd, con Elicitation y ElicitationResult anidados dentro de la ejecución de herramientas MCP y WorktreeCreate, WorktreeRemove, Notification, ConfigChange, InstructionsLoaded, CwdChanged y FileChanged como eventos asincronos independientes
La tabla a continuación resume cuándo se activa cada evento. La sección Hook events documenta el esquema de entrada completo y las opciones de control de decisión para cada uno.
EventWhen it fires
SessionStartWhen a session begins or resumes
UserPromptSubmitWhen you submit a prompt, before Claude processes it
PreToolUseBefore a tool call executes. Can block it
PermissionRequestWhen a permission dialog appears
PermissionDeniedWhen a tool call is denied by the auto mode classifier. Return {retry: true} to tell the model it may retry the denied tool call
PostToolUseAfter a tool call succeeds
PostToolUseFailureAfter a tool call fails
NotificationWhen Claude Code sends a notification
SubagentStartWhen a subagent is spawned
SubagentStopWhen a subagent finishes
TaskCreatedWhen a task is being created via TaskCreate
TaskCompletedWhen a task is being marked as completed
StopWhen Claude finishes responding
StopFailureWhen the turn ends due to an API error. Output and exit code are ignored
TeammateIdleWhen an agent team teammate is about to go idle
InstructionsLoadedWhen a CLAUDE.md or .claude/rules/*.md file is loaded into context. Fires at session start and when files are lazily loaded during a session
ConfigChangeWhen a configuration file changes during a session
CwdChangedWhen the working directory changes, for example when Claude executes a cd command. Useful for reactive environment management with tools like direnv
FileChangedWhen a watched file changes on disk. The matcher field specifies which filenames to watch
WorktreeCreateWhen a worktree is being created via --worktree or isolation: "worktree". Replaces default git behavior
WorktreeRemoveWhen a worktree is being removed, either at session exit or when a subagent finishes
PreCompactBefore context compaction
PostCompactAfter context compaction completes
ElicitationWhen an MCP server requests user input during a tool call
ElicitationResultAfter a user responds to an MCP elicitation, before the response is sent back to the server
SessionEndWhen a session terminates

Cómo se resuelve un hook

Para ver cómo encajan estas piezas, considere este hook PreToolUse que bloquea comandos de shell destructivos. El matcher se reduce a llamadas a herramientas Bash y la condición if se reduce aún más a comandos que comienzan con rm, por lo que block-rm.sh solo se genera cuando ambos filtros coinciden:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "if": "Bash(rm *)",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-rm.sh"
          }
        ]
      }
    ]
  }
}
El script lee la entrada JSON desde stdin, extrae el comando y devuelve una permissionDecision de "deny" si contiene rm -rf:
#!/bin/bash
# .claude/hooks/block-rm.sh
COMMAND=$(jq -r '.tool_input.command')

if echo "$COMMAND" | grep -q 'rm -rf'; then
  jq -n '{
    hookSpecificOutput: {
      hookEventName: "PreToolUse",
      permissionDecision: "deny",
      permissionDecisionReason: "Destructive command blocked by hook"
    }
  }'
else
  exit 0  # allow the command
fi
Ahora suponga que Claude Code decide ejecutar Bash "rm -rf /tmp/build". Esto es lo que sucede:
Flujo de resolución de hooks: se activa el evento PreToolUse, el matcher verifica la coincidencia de Bash, la condición if verifica la coincidencia de Bash(rm *), se ejecuta el controlador de hooks, el resultado se devuelve a Claude Code
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:
{ "tool_name": "Bash", "tool_input": { "command": "rm -rf /tmp/build" }, ... }
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 el comando comienza 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 rm -rf, por lo que imprime una decisión en stdout:
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook"
  }
}
Si el comando hubiera sido una variante más segura de rm como rm file.txt, el script habría alcanzado exit 0 en su lugar, lo que le dice a Claude Code que permita la llamada a la herramienta sin más acciones.
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.
La sección Configuration a continuación documenta el esquema completo, y cada sección hook event documenta qué entrada recibe su comando y qué salida puede devolver.

Configuración

Los hooks se definen en archivos de configuración JSON. La configuración tiene tres niveles de anidamiento:
  1. Elija un hook event al que responder, como PreToolUse o Stop
  2. Agregue un matcher group para filtrar cuándo se activa, como “solo para la herramienta Bash”
  3. Defina uno o más hook handlers para ejecutar cuando coincida
Consulte Cómo se resuelve un hook arriba para un recorrido completo con un ejemplo anotado.
Esta página utiliza términos específicos para cada nivel: hook event para el punto del ciclo de vida, matcher group para el filtro y hook handler para el comando de shell, punto final HTTP, prompt o agente que se ejecuta. “Hook” por sí solo se refiere a la característica general.

Ubicaciones de hooks

Dónde defina un hook determina su alcance:
UbicaciónAlcanceCompartible
~/.claude/settings.jsonTodos sus proyectosNo, local en su máquina
.claude/settings.jsonProyecto únicoSí, puede ser confirmado en el repositorio
.claude/settings.local.jsonProyecto únicoNo, ignorado por git
Configuración de política administradaToda la organizaciónSí, controlado por administrador
Plugin hooks/hooks.jsonCuando el plugin está habilitadoSí, incluido con el plugin
Skill o agent frontmatterMientras el componente está activoSí, definido en el archivo del componente
Para obtener detalles sobre la resolución de archivos de configuración, consulte settings. Los administradores empresariales pueden usar allowManagedHooksOnly para bloquear hooks de usuario, proyecto y plugin. Consulte Hook configuration.

Patrones de matcher

El campo matcher es una cadena regex que filtra cuándo se activan los hooks. Use "*", "" u omita matcher completamente para coincidir con todas las ocurrencias. Cada tipo de evento coincide en un campo diferente:
EventoEn qué filtra el matcherValores de matcher de ejemplo
PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequestnombre de la herramientaBash, Edit|Write, mcp__.*
SessionStartcómo comenzó la sesiónstartup, resume, clear, compact
SessionEndpor qué terminó la sesiónclear, resume, logout, prompt_input_exit, bypass_permissions_disabled, other
Notificationtipo de notificaciónpermission_prompt, idle_prompt, auth_success, elicitation_dialog
SubagentStarttipo de agenteBash, Explore, Plan o nombres de agentes personalizados
PreCompact, PostCompactqué desencadenó la compactaciónmanual, auto
SubagentStoptipo de agentelos mismos valores que SubagentStart
ConfigChangefuente de configuraciónuser_settings, project_settings, local_settings, policy_settings, skills
CwdChangedsin soporte de matchersiempre se activa en cada cambio de directorio
FileChangednombre de archivo (basename del archivo cambiado).envrc, .env, cualquier nombre de archivo que desee monitorear
StopFailuretipo de errorrate_limit, authentication_failed, billing_error, invalid_request, server_error, max_output_tokens, unknown
InstructionsLoadedrazón de cargasession_start, nested_traversal, path_glob_match, include, compact
Elicitationnombre del servidor MCPsus nombres de servidor MCP configurados
ElicitationResultnombre del servidor MCPlos mismos valores que Elicitation
UserPromptSubmit, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemovesin soporte de matchersiempre se activa en cada ocurrencia
El matcher es un regex, por lo que Edit|Write coincide con cualquiera de las herramientas y Notebook.* coincide con cualquier herramienta que comience con Notebook. El matcher se ejecuta contra un campo de la entrada JSON que Claude Code envía a su hook en stdin. Para eventos de herramientas, ese campo es tool_name. Cada sección hook event enumera 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:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/lint-check.sh"
          }
        ]
      }
    ]
  }
}
UserPromptSubmit, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove y CwdChanged no admiten matchers y siempre se activan en cada ocurrencia. Si agrega un campo matcher a estos eventos, se ignora silenciosamente. Para eventos de herramientas, puede filtrar más estrechamente estableciendo el campo if en controladores de hooks individuales. if utiliza sintaxis de regla de permiso para coincidir con el nombre de la herramienta y los argumentos juntos, por lo que "Bash(git *)" se ejecuta solo para comandos git y "Edit(*.ts)" se ejecuta solo para archivos TypeScript.

Coincidir herramientas MCP

Las herramientas del servidor MCP aparecen como herramientas normales en eventos de herramientas (PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest), por lo que puede 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 Memory
  • mcp__filesystem__read_file: herramienta leer archivo del servidor Filesystem
  • mcp__github__search_repositories: herramienta de búsqueda del servidor GitHub
Use patrones regex para dirigirse a herramientas MCP específicas o grupos de herramientas:
  • mcp__memory__.* coincide con todas las herramientas del servidor memory
  • mcp__.*__write.* coincide con cualquier herramienta que contenga “write” de cualquier servidor
Este ejemplo registra todas las operaciones del servidor de memoria y valida operaciones de escritura de cualquier servidor MCP:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "mcp__memory__.*",
        "hooks": [
          {
            "type": "command",
            "command": "echo 'Memory operation initiated' >> ~/mcp-operations.log"
          }
        ]
      },
      {
        "matcher": "mcp__.*__write.*",
        "hooks": [
          {
            "type": "command",
            "command": "/home/user/scripts/validate-mcp-write.py"
          }
        ]
      }
    ]
  }
}

Campos del controlador de hooks

Cada objeto en el array hooks interno es un controlador de hook: el comando de shell, punto final HTTP, prompt de LLM o agente que se ejecuta cuando el matcher coincide. Hay cuatro tipos:
  • Command hooks (type: "command"): ejecutan un comando de shell. Su script recibe la entrada JSON del evento en stdin y comunica resultados a través de códigos de salida y stdout.
  • HTTP hooks (type: "http"): envían la entrada JSON del evento como una solicitud HTTP POST a una URL. El punto final comunica resultados a través del cuerpo de la respuesta usando el mismo formato de salida JSON que los hooks de comando.
  • Prompt hooks (type: "prompt"): envían un prompt a un modelo Claude para evaluación de un solo turno. El modelo devuelve una decisión sí/no como JSON. Consulte Prompt-based hooks.
  • Agent hooks (type: "agent"): generan un subagente que puede usar herramientas como Read, Grep y Glob para verificar condiciones antes de devolver una decisión. Consulte Agent-based hooks.

Campos comunes

Estos campos se aplican a todos los tipos de hooks:
CampoRequeridoDescripción
type"command", "http", "prompt" o "agent"
ifnoSintaxis de regla de permiso para filtrar cuándo se ejecuta este hook, como "Bash(git *)" o "Edit(*.ts)". El hook solo se genera si la llamada a herramienta coincide con el patrón. Solo se evalúa en eventos de herramientas: PreToolUse, PostToolUse, PostToolUseFailure y PermissionRequest. En otros eventos, un hook con if establecido nunca se ejecuta. Utiliza la misma sintaxis que reglas de permiso
timeoutnoSegundos antes de cancelar. Valores predeterminados: 600 para comando, 30 para prompt, 60 para agente
statusMessagenoMensaje de spinner personalizado mostrado mientras se ejecuta el hook
oncenoSi es true, se ejecuta solo una vez por sesión y luego se elimina. Solo skills, no agentes. Consulte Hooks in skills and agents

Campos de comando hook

Además de los campos comunes, los hooks de comando aceptan estos campos:
CampoRequeridoDescripción
commandComando de shell a ejecutar
asyncnoSi es true, se ejecuta en segundo plano sin bloquear. Consulte Run hooks in the background
shellnoShell a usar para este hook. Acepta "bash" (predeterminado) o "powershell". Establecer "powershell" ejecuta el comando a través de PowerShell en Windows. No requiere CLAUDE_CODE_USE_POWERSHELL_TOOL ya que los hooks generan PowerShell directamente

Campos de hook HTTP

Además de los campos comunes, los hooks HTTP aceptan estos campos:
CampoRequeridoDescripción
urlURL a la que enviar la solicitud POST
headersnoEncabezados HTTP adicionales como pares clave-valor. Los valores admiten interpolación de variables de entorno usando la sintaxis $VAR_NAME o ${VAR_NAME}. Solo se resuelven las variables enumeradas en allowedEnvVars
allowedEnvVarsnoLista de nombres de variables de entorno que pueden interpolarse en valores de encabezado. Las referencias a variables no enumeradas se reemplazan con cadenas vacías. Requerido para que funcione cualquier interpolación de variable de entorno
Claude Code envía la entrada JSON del hook como el cuerpo de la solicitud POST con Content-Type: application/json. El cuerpo de la respuesta usa el mismo formato de salida JSON que los hooks de comando. El manejo de errores difiere de los hooks de comando: las respuestas que no son 2xx, los fallos de conexión y los tiempos de espera agotados producen errores sin bloqueo que permiten que la ejecución continúe. Para bloquear una llamada a herramienta o denegar un permiso, devuelva una respuesta 2xx con un cuerpo JSON que contenga decision: "block" o un hookSpecificOutput con permissionDecision: "deny". 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:
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "http",
            "url": "http://localhost:8080/hooks/pre-tool-use",
            "timeout": 30,
            "headers": {
              "Authorization": "Bearer $MY_TOKEN"
            },
            "allowedEnvVars": ["MY_TOKEN"]
          }
        ]
      }
    ]
  }
}

Campos de hook de prompt y agente

Además de los campos comunes, los hooks de prompt y agente aceptan estos campos:
CampoRequeridoDescripción
promptTexto del prompt a enviar al modelo. Use $ARGUMENTS como marcador de posición para la entrada JSON del hook
modelnoModelo a usar para evaluación. Por defecto es un modelo rápido
Todos los hooks coincidentes se ejecutan en paralelo, y los controladores idénticos se deduplicarán automáticamente. Los hooks de comando se deduplicarán por cadena de comando, y los hooks HTTP se deduplicarán por URL. Los controladores se ejecutan en el directorio actual con el entorno de Claude Code. La variable de entorno $CLAUDE_CODE_REMOTE se establece en "true" en entornos web remotos y no se establece en la CLI local.

Referenciar scripts por ruta

Use variables de entorno para referenciar scripts de hooks 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. Envuelva entre comillas para manejar rutas con espacios.
  • ${CLAUDE_PLUGIN_ROOT}: el directorio raíz del plugin, para scripts incluidos con un plugin. Cambia en cada actualización de plugin.
  • ${CLAUDE_PLUGIN_DATA}: el directorio de datos persistentes del plugin, para dependencias y estado que deben sobrevivir a las actualizaciones de plugin.
Este ejemplo usa $CLAUDE_PROJECT_DIR para ejecutar un verificador de estilo desde el directorio .claude/hooks/ del proyecto después de cualquier llamada a herramienta Write o Edit:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-style.sh"
          }
        ]
      }
    ]
  }
}

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. Estos hooks se limitan al ciclo de vida del componente y solo se ejecutan cuando ese componente está activo. Se admiten todos los eventos de hook. Para subagentes, los hooks Stop se convierten automáticamente a SubagentStop ya que ese es el evento que se activa cuando un subagente se completa. Los hooks usan el mismo formato de configuración que los hooks basados en configuración pero se limitan a la vida útil del componente y se limpian cuando finaliza. Esta skill define un hook PreToolUse que ejecuta un script de validación de seguridad antes de cada comando Bash:
---
name: secure-operations
description: Perform operations with security checks
hooks:
  PreToolUse:
    - matcher: "Bash"
      hooks:
        - type: command
          command: "./scripts/security-check.sh"
---
Los agentes usan el mismo formato en su frontmatter YAML.

El menú /hooks

Escriba /hooks en Claude Code para abrir un navegador de solo lectura para sus hooks configurados. El menú muestra cada evento de hook con un recuento de hooks configurados, le permite profundizar en matchers y muestra los detalles completos de cada controlador de hook. Úselo para verificar la configuración, verificar desde qué archivo de configuración proviene un hook o inspeccionar el comando, prompt o URL de un hook. El menú muestra los cuatro tipos de hooks: command, prompt, agent e http. Cada hook está etiquetado con un prefijo [type] y una fuente que indica dónde se definió:
  • User: de ~/.claude/settings.json
  • Project: de .claude/settings.json
  • Local: de .claude/settings.local.json
  • Plugin: de hooks/hooks.json de un plugin
  • Session: registrado en memoria para la sesión actual
  • Built-in: registrado internamente por Claude Code
Seleccionar un hook abre una vista de detalle que muestra su evento, matcher, tipo, archivo de origen y el comando, prompt o URL completo. El menú es de solo lectura: para agregar, modificar o eliminar hooks, edite el JSON de configuración directamente o pida a Claude que haga el cambio.

Deshabilitar o eliminar hooks

Para eliminar un hook, elimine su entrada del archivo de configuración JSON. Para deshabilitar temporalmente todos los hooks sin eliminarlos, establezca "disableAllHooks": true en su archivo de configuración. No hay forma de deshabilitar un hook individual mientras se mantiene 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 configuración de política administrada, disableAllHooks establecido en 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. Las ediciones directas de hooks en archivos de configuración normalmente se capturan 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.

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.
CampoDescripción
session_idIdentificador de sesión actual
transcript_pathRuta al JSON de conversación
cwdDirectorio de trabajo actual cuando se invoca el hook
permission_modeModo de permiso actual: "default", "plan", "acceptEdits", "auto", "dontAsk" o "bypassPermissions". No todos los eventos reciben este campo: consulte cada ejemplo JSON de evento a continuación para verificar
hook_event_nameNombre del evento que se activó
Cuando se ejecuta con --agent o dentro de un subagente, se incluyen dos campos adicionales:
CampoDescripción
agent_idIdentificador único para el subagente. Presente solo cuando el hook se activa dentro de una llamada de subagente. Use esto para distinguir llamadas de hook de subagente de llamadas de hilo principal.
agent_typeNombre del agente (por ejemplo, "Explore" o "security-reviewer"). Presente cuando la sesión usa --agent o el hook se activa dentro de un subagente. Para subagentes, el tipo del subagente tiene precedencia sobre el valor --agent de la sesión.
Por ejemplo, un hook PreToolUse para un comando Bash recibe esto en stdin:
{
  "session_id": "abc123",
  "transcript_path": "/home/user/.claude/projects/.../transcript.jsonl",
  "cwd": "/home/user/my-project",
  "permission_mode": "default",
  "hook_event_name": "PreToolUse",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test"
  }
}
Los campos tool_name y tool_input 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. Exit 0 significa éxito. Claude Code analiza stdout para campos de salida JSON. La salida JSON solo se procesa en exit 0. Para la mayoría de eventos, stdout solo se muestra en modo detallado (Ctrl+O). Las excepciones son UserPromptSubmit y SessionStart, donde stdout se agrega como contexto que Claude puede ver y actuar. Exit 2 significa un error de bloqueo. Claude Code ignora stdout y cualquier JSON en él. En su lugar, el texto de stderr se devuelve a Claude como un mensaje de error. El efecto depende del evento: PreToolUse bloquea la llamada a herramienta, UserPromptSubmit rechaza el prompt, y así sucesivamente. Consulte exit code 2 behavior para la lista completa. Cualquier otro código de salida es un error sin bloqueo. stderr se muestra en modo detallado (Ctrl+O) y la ejecución continúa. Por ejemplo, un script de comando de hook que bloquea comandos Bash peligrosos:
#!/bin/bash
# Lee entrada JSON desde stdin, verifica el comando
command=$(jq -r '.tool_input.command' < /dev/stdin)

if [[ "$command" == rm* ]]; then
  echo "Blocked: rm commands are not allowed" >&2
  exit 2  # Blocking error: tool call is prevented
fi

exit 0  # Success: tool call proceeds

Comportamiento del código de salida 2 por evento

El código de salida 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.
Evento de hook¿Puede bloquear?Qué sucede en exit 2
PreToolUseBloquea la llamada a herramienta
PermissionRequestDeniega el permiso
UserPromptSubmitBloquea el procesamiento del prompt y borra el prompt
StopEvita que Claude se detenga, continúa la conversación
SubagentStopEvita que el subagente se detenga
TeammateIdleEvita que el compañero se quede inactivo (el compañero continúa trabajando)
TaskCreatedRevierte la creación de la tarea
TaskCompletedEvita que la tarea se marque como completada
ConfigChangeBloquea que el cambio de configuración tenga efecto (excepto policy_settings)
StopFailureNoLa salida y el código de salida se ignoran
PostToolUseNoMuestra stderr a Claude (la herramienta ya se ejecutó)
PostToolUseFailureNoMuestra stderr a Claude (la herramienta ya falló)
NotificationNoMuestra stderr solo al usuario
SubagentStartNoMuestra stderr solo al usuario
SessionStartNoMuestra stderr solo al usuario
SessionEndNoMuestra stderr solo al usuario
CwdChangedNoMuestra stderr solo al usuario
FileChangedNoMuestra stderr solo al usuario
PreCompactNoMuestra stderr solo al usuario
PostCompactNoMuestra stderr solo al usuario
ElicitationDeniega la elicitación
ElicitationResultBloquea la respuesta (la acción se convierte en decline)
WorktreeCreateCualquier código de salida distinto de cero causa que la creación de worktree falle
WorktreeRemoveNoLos fallos se registran solo en modo de depuración
InstructionsLoadedNoEl código de salida se ignora

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:
  • 2xx con un cuerpo vacío: éxito, equivalente a código de salida 0 sin salida
  • 2xx con un cuerpo de texto plano: éxito, el texto se agrega como contexto
  • 2xx con un cuerpo JSON: éxito, analizado usando el mismo esquema JSON output que los hooks de comando
  • Estado que no es 2xx: error sin bloqueo, la ejecución continúa
  • Fallo de conexión o tiempo de espera agotado: error sin bloqueo, la ejecución continúa
A diferencia de los hooks de comando, los hooks HTTP no pueden señalar un error de bloqueo solo a través de códigos de estado. Para bloquear una llamada a herramienta o denegar un permiso, devuelva una respuesta 2xx con un cuerpo JSON que contenga los campos de decisión apropiados.

Salida JSON

Los códigos de salida le permiten permitir o bloquear, 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.
Debe elegir un enfoque por hook, no ambos: use códigos de salida solos para señalizar, o salga 0 e imprima JSON para control estructurado. Claude Code solo procesa JSON en exit 0. Si sale 2, cualquier JSON se ignora.
El stdout de su hook debe contener solo el objeto JSON. Si su perfil de shell imprime texto al inicio, puede interferir con el análisis JSON. Consulte JSON validation failed en la guía de solución de problemas. El objeto JSON admite tres tipos de campos:
  • Campos universales como continue funcionan en todos los eventos. Estos se enumeran en la tabla a continuación.
  • decision y reason de nivel superior son utilizados por algunos eventos para bloquear o proporcionar retroalimentación.
  • hookSpecificOutput es un objeto anidado para eventos que necesitan control más rico. Requiere un campo hookEventName establecido en el nombre del evento.
CampoPredeterminadoDescripción
continuetrueSi es false, Claude detiene el procesamiento completamente después de que se ejecuta el hook. Tiene precedencia sobre cualquier campo de decisión específico del evento
stopReasonningunoMensaje mostrado al usuario cuando continue es false. No se muestra a Claude
suppressOutputfalseSi es true, oculta stdout de la salida del modo detallado
systemMessageningunoMensaje de advertencia mostrado al usuario
Para detener Claude completamente independientemente del tipo de evento:
{ "continue": false, "stopReason": "Build failed, fix errors before continuing" }

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:
EventosPatrón de decisiónCampos clave
UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop, ConfigChangedecision de nivel superiordecision: "block", reason
TeammateIdle, TaskCreated, TaskCompletedCódigo de salida o continue: falseEl código de salida 2 bloquea la acción con retroalimentación de stderr. JSON {"continue": false, "stopReason": "..."} también detiene al compañero completamente, coincidiendo con el comportamiento del hook Stop
PreToolUsehookSpecificOutputpermissionDecision (allow/deny/ask), permissionDecisionReason
PermissionRequesthookSpecificOutputdecision.behavior (allow/deny)
WorktreeCreateruta stdoutEl hook imprime la ruta en stdout; el hook HTTP devuelve hookSpecificOutput.worktreePath. El fallo del hook o la ruta faltante falla la creación
ElicitationhookSpecificOutputaction (accept/decline/cancel), content (valores de campo de formulario para accept)
ElicitationResulthookSpecificOutputaction (accept/decline/cancel), content (valores de campo de formulario override)
WorktreeRemove, Notification, SessionEnd, PreCompact, PostCompact, InstructionsLoaded, StopFailure, CwdChanged, FileChangedNingunoSin control de decisión. Se usa para efectos secundarios como registro o limpieza
Aquí hay ejemplos de cada patrón en acción:
Utilizado por UserPromptSubmit, PostToolUse, PostToolUseFailure, Stop, SubagentStop y ConfigChange. El único valor es "block". Para permitir que la acción continúe, omita decision de su JSON, o salga 0 sin ningún JSON en absoluto:
{
  "decision": "block",
  "reason": "Test suite must pass before proceeding"
}
Para ejemplos extendidos incluyendo validación de comandos Bash, filtrado de prompts y scripts de aprobación automática, consulte What you can automate en la guía y la implementación de referencia del validador de comandos Bash.

Eventos de hook

Cada evento corresponde a un punto en el ciclo de vida de Claude Code donde los hooks pueden ejecutarse. Las secciones a continuación se ordenan para coincidir con el ciclo de vida: desde la configuración de sesión a través del bucle agentico hasta el final de la sesión. Cada sección describe cuándo se activa 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 su base de código, o configurar variables de entorno. Para contexto estático que no requiere un script, use CLAUDE.md en su lugar. SessionStart se ejecuta en cada sesión, así que mantenga estos hooks rápidos. Solo se admiten hooks type: "command". El valor del matcher corresponde a cómo se inició la sesión:
MatcherCuándo se activa
startupNueva sesión
resume--resume, --continue o /resume
clear/clear
compactCompactación automática o manual

Entrada de SessionStart

Además de los campos de entrada comunes, los hooks SessionStart reciben source, model y opcionalmente agent_type. El campo source indica cómo comenzó la sesión: "startup" para nuevas sesiones, "resume" para sesiones reanudadas, "clear" después de /clear o "compact" después de compactación. El campo model contiene el identificador del modelo. Si inicia Claude Code con claude --agent <name>, un campo agent_type contiene el nombre del agente.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionStart",
  "source": "startup",
  "model": "claude-sonnet-4-6"
}

Control de decisión de SessionStart

Cualquier texto que su script de hook imprima en stdout se agrega como contexto para Claude. Además de los campos de salida JSON disponibles para todos los hooks, puede devolver estos campos específicos del evento:
CampoDescripción
additionalContextCadena agregada al contexto de Claude. Los valores de múltiples hooks se concatenan
{
  "hookSpecificOutput": {
    "hookEventName": "SessionStart",
    "additionalContext": "My additional context here"
  }
}

Persistir variables de entorno

Los hooks SessionStart tienen acceso a la variable de entorno CLAUDE_ENV_FILE, que proporciona una ruta de archivo donde puede persistir variables de entorno para comandos Bash posteriores. Para establecer variables de entorno individuales, escriba declaraciones export en CLAUDE_ENV_FILE. Use append (>>) para preservar variables establecidas por otros hooks:
#!/bin/bash

if [ -n "$CLAUDE_ENV_FILE" ]; then
  echo 'export NODE_ENV=production' >> "$CLAUDE_ENV_FILE"
  echo 'export DEBUG_LOG=true' >> "$CLAUDE_ENV_FILE"
  echo 'export PATH="$PATH:./node_modules/.bin"' >> "$CLAUDE_ENV_FILE"
fi

exit 0
Para capturar todos los cambios de entorno de comandos de configuración, compare las variables exportadas antes y después:
#!/bin/bash

ENV_BEFORE=$(export -p | sort)

# Ejecute sus comandos de configuración que modifican el entorno
source ~/.nvm/nvm.sh
nvm use 20

if [ -n "$CLAUDE_ENV_FILE" ]; then
  ENV_AFTER=$(export -p | sort)
  comm -13 <(echo "$ENV_BEFORE") <(echo "$ENV_AFTER") >> "$CLAUDE_ENV_FILE"
fi

exit 0
Cualquier variable escrita en este archivo estará disponible en todos los comandos Bash posteriores que Claude Code ejecute durante la sesión.
CLAUDE_ENV_FILE está disponible para hooks SessionStart, CwdChanged y FileChanged. Otros tipos de hooks no tienen acceso a esta variable.

InstructionsLoaded

Se activa cuando se carga un archivo CLAUDE.md o .claude/rules/*.md en contexto. Este evento se activa 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 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. El matcher se ejecuta contra load_reason. Por ejemplo, use "matcher": "session_start" para activarse solo para archivos cargados al inicio de la sesión, o "matcher": "path_glob_match|nested_traversal" para activarse solo para cargas perezosas.

Entrada de InstructionsLoaded

Además de los campos de entrada comunes, los hooks InstructionsLoaded reciben estos campos:
CampoDescripción
file_pathRuta absoluta al archivo de instrucciones que se cargó
memory_typeAlcance del archivo: "User", "Project", "Local" o "Managed"
load_reasonPor qué se cargó el archivo: "session_start", "nested_traversal", "path_glob_match", "include" o "compact". El valor "compact" se activa cuando los archivos de instrucciones se recargan después de un evento de compactación
globsPatrones de glob de ruta del frontmatter paths: del archivo, si los hay. Presente solo para cargas path_glob_match
trigger_file_pathRuta al archivo cuyo acceso desencadenó esta carga, para cargas perezosas
parent_file_pathRuta al archivo de instrucciones padre que incluyó este, para cargas include
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "InstructionsLoaded",
  "file_path": "/Users/my-project/CLAUDE.md",
  "memory_type": "Project",
  "load_reason": "session_start"
}

Control de decisión de InstructionsLoaded

Los hooks InstructionsLoaded no tienen control de decisión. No pueden bloquear o modificar la carga de instrucciones. Use este evento para registro de auditoría, seguimiento de cumplimiento u observabilidad.

UserPromptSubmit

Se ejecuta cuando el usuario envía un prompt, antes de que Claude lo procese. Esto le permite agregar contexto adicional basado en el prompt/conversación, validar prompts o bloquear ciertos tipos de prompts.

Entrada de UserPromptSubmit

Además de los campos de entrada comunes, los hooks UserPromptSubmit reciben el campo prompt que contiene el texto que el usuario envió.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "UserPromptSubmit",
  "prompt": "Write a function to calculate the factorial of a number"
}

Control de decisión de UserPromptSubmit

Los hooks UserPromptSubmit pueden controlar si se procesa un prompt de usuario y agregar contexto. Todos los campos de salida JSON están disponibles. Hay dos formas de agregar contexto a la conversación en código de salida 0:
  • Stdout de texto plano: cualquier texto que no sea JSON escrito en stdout se agrega como contexto
  • JSON con additionalContext: use el formato JSON a continuación para más control. El campo additionalContext se agrega como contexto
El stdout plano se muestra como salida de hook en la transcripción. El campo additionalContext se agrega de forma más discreta. Para bloquear un prompt, devuelva un objeto JSON con decision establecido en "block":
CampoDescripción
decision"block" evita que el prompt se procese y lo borra del contexto. Omita para permitir que el prompt continúe
reasonSe muestra al usuario cuando decision es "block". No se agrega al contexto
additionalContextCadena agregada al contexto de Claude
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "UserPromptSubmit",
    "additionalContext": "My additional context here"
  }
}
El formato JSON no es necesario para casos de uso simples. Para agregar contexto, puede imprimir texto plano en stdout con código de salida 0. Use JSON cuando necesite bloquear prompts o desee un control más estructurado.

PreToolUse

Se ejecuta después de que Claude crea parámetros de herramienta y antes de procesar la llamada a herramienta. Coincide en el nombre de la herramienta: Bash, Edit, Write, Read, Glob, Grep, Agent, WebFetch, WebSearch, AskUserQuestion, ExitPlanMode y cualquier nombre de herramienta MCP. Use PreToolUse decision control para permitir, denegar o pedir permiso para usar la herramienta.

Entrada de PreToolUse

Además de los campos de entrada comunes, los hooks PreToolUse reciben tool_name, tool_input y tool_use_id. Los campos tool_input dependen de la herramienta:
Bash
Ejecuta comandos de shell.
CampoTipoEjemploDescripción
commandstring"npm test"El comando de shell a ejecutar
descriptionstring"Run test suite"Descripción opcional de lo que hace el comando
timeoutnumber120000Tiempo de espera opcional en milisegundos
run_in_backgroundbooleanfalseSi se ejecuta el comando en segundo plano
Write
Crea o sobrescribe un archivo.
CampoTipoEjemploDescripción
file_pathstring"/path/to/file.txt"Ruta absoluta al archivo a escribir
contentstring"file content"Contenido a escribir en el archivo
Edit
Reemplaza una cadena en un archivo existente.
CampoTipoEjemploDescripción
file_pathstring"/path/to/file.txt"Ruta absoluta al archivo a editar
old_stringstring"original text"Texto a encontrar y reemplazar
new_stringstring"replacement text"Texto de reemplazo
replace_allbooleanfalseSi se reemplazan todas las ocurrencias
Read
Lee contenidos de archivo.
CampoTipoEjemploDescripción
file_pathstring"/path/to/file.txt"Ruta absoluta al archivo a leer
offsetnumber10Número de línea opcional para comenzar a leer desde
limitnumber50Número opcional de líneas a leer
Glob
Encuentra archivos que coincidan con un patrón glob.
CampoTipoEjemploDescripción
patternstring"**/*.ts"Patrón glob para coincidir archivos contra
pathstring"/path/to/dir"Directorio opcional para buscar. Por defecto es el directorio de trabajo actual
Grep
Busca contenidos de archivo con expresiones regulares.
CampoTipoEjemploDescripción
patternstring"TODO.*fix"Patrón de expresión regular a buscar
pathstring"/path/to/dir"Archivo o directorio opcional para buscar
globstring"*.ts"Patrón glob opcional para filtrar archivos
output_modestring"content""content", "files_with_matches" o "count". Por defecto es "files_with_matches"
-ibooleantrueBúsqueda insensible a mayúsculas y minúsculas
multilinebooleanfalseHabilitar coincidencia multilínea
WebFetch
Obtiene y procesa contenido web.
CampoTipoEjemploDescripción
urlstring"https://example.com/api"URL para obtener contenido de
promptstring"Extract the API endpoints"Prompt a ejecutar en el contenido obtenido
WebSearch
Busca en la web.
CampoTipoEjemploDescripción
querystring"react hooks best practices"Consulta de búsqueda
allowed_domainsarray["docs.example.com"]Opcional: incluir solo resultados de estos dominios
blocked_domainsarray["spam.example.com"]Opcional: excluir resultados de estos dominios
Agent
Genera un subagente.
CampoTipoEjemploDescripción
promptstring"Find all API endpoints"La tarea para que el agente realice
descriptionstring"Find API endpoints"Descripción breve de la tarea
subagent_typestring"Explore"Tipo de agente especializado a usar
modelstring"sonnet"Alias de modelo opcional para anular el predeterminado
AskUserQuestion
Hace al usuario una a cuatro preguntas de opción múltiple.
CampoTipoEjemploDescripción
questionsarray[{"question": "Which framework?", "header": "Framework", "options": [{"label": "React"}], "multiSelect": false}]Preguntas a presentar, cada una con una cadena question, header corto, array options y bandera multiSelect opcional
answersobject{"Which framework?": "React"}Opcional. Asigna texto de pregunta a la etiqueta de opción seleccionada. Las respuestas de selección múltiple unen etiquetas con comas. Claude no establece este campo; suministrarlo a través de updatedInput para responder programáticamente

PreToolUse decision control

Los hooks PreToolUse pueden controlar si procede una llamada a 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: tres resultados (permitir, denegar o preguntar) más la capacidad de modificar la entrada de la herramienta antes de la ejecución.
CampoDescripción
permissionDecision"allow" omite el sistema de permisos. "deny" evita la llamada a herramienta. "ask" solicita al usuario que confirme. Las reglas Deny and ask aún se aplican cuando un hook devuelve "allow"
permissionDecisionReasonPara "allow" y "ask", se muestra al usuario pero no a Claude. Para "deny", se muestra a Claude
updatedInputModifica los parámetros de entrada de la herramienta antes de la ejecución. Reemplaza el objeto de entrada completo, así que incluya campos sin cambios junto con los modificados. Combinar con "allow" para aprobación automática, o "ask" para mostrar la entrada modificada al usuario
additionalContextCadena agregada al contexto de Claude antes de que se ejecute la herramienta
Cuando un hook devuelve "ask", el diálogo de permiso mostrado al usuario incluye una etiqueta que identifica de dónde proviene el hook: por ejemplo, [User], [Project], [Plugin] o [Local]. Esto ayuda a los usuarios a entender qué fuente de configuración está solicitando confirmación.
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "allow",
    "permissionDecisionReason": "My reason here",
    "updatedInput": {
      "field_to_modify": "new value"
    },
    "additionalContext": "Current environment: production. Proceed with caution."
  }
}
AskUserQuestion y ExitPlanMode requieren interacción del usuario y normalmente se bloquean en modo no interactivo con la bandera -p. Devolver permissionDecision: "allow" junto con updatedInput satisface ese requisito: el hook lee la entrada de la herramienta desde stdin, recopila la respuesta a través de su propia interfaz de usuario y la devuelve en updatedInput para que la herramienta se ejecute sin solicitar. Devolver "allow" solo no es suficiente para estas herramientas. Para AskUserQuestion, repita el array questions original y agregue un objeto answers que asigne el texto de cada pregunta a la respuesta elegida.
PreToolUse anteriormente usaba campos decision y reason de nivel superior, pero estos están deprecados para este evento. Use hookSpecificOutput.permissionDecision y hookSpecificOutput.permissionDecisionReason en su lugar. Los valores deprecados "approve" y "block" se asignan a "allow" y "deny" respectivamente. Otros eventos como PostToolUse y Stop continúan usando decision y reason de nivel superior como su formato actual.

PermissionRequest

Se ejecuta cuando se muestra un diálogo de permiso al usuario. Use PermissionRequest decision control para permitir o denegar en nombre del usuario. Coincide en el nombre de la herramienta, los mismos valores que PreToolUse.

Entrada de PermissionRequest

Los hooks PermissionRequest reciben campos tool_name y tool_input como los hooks PreToolUse, pero sin tool_use_id. Un array permission_suggestions opcional contiene las opciones “siempre permitir” que el usuario normalmente vería en el diálogo de permiso. La diferencia es cuándo se activa el hook: los hooks PermissionRequest se ejecutan cuando un diálogo de permiso está a punto de mostrarse al usuario, mientras que los hooks PreToolUse se ejecutan antes de la ejecución de la herramienta independientemente del estado de permiso.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PermissionRequest",
  "tool_name": "Bash",
  "tool_input": {
    "command": "rm -rf node_modules",
    "description": "Remove node_modules directory"
  },
  "permission_suggestions": [
    {
      "type": "addRules",
      "rules": [{ "toolName": "Bash", "ruleContent": "rm -rf node_modules" }],
      "behavior": "allow",
      "destination": "localSettings"
    }
  ]
}

Control de decisión de PermissionRequest

Los hooks PermissionRequest pueden permitir o denegar solicitudes de permiso. Además de los campos de salida JSON disponibles para todos los hooks, su script de hook puede devolver un objeto decision con estos campos específicos del evento:
CampoDescripción
behavior"allow" otorga el permiso, "deny" lo deniega
updatedInputSolo para "allow": modifica los parámetros de entrada de la herramienta antes de la ejecución. Reemplaza el objeto de entrada completo, así que incluya campos sin cambios junto con los modificados
updatedPermissionsSolo para "allow": array de entradas de actualización de permiso a aplicar, como agregar una regla de permiso o cambiar el modo de permiso de sesión
messageSolo para "deny": le dice a Claude por qué se denegó el permiso
interruptSolo para "deny": si es true, detiene a Claude
{
  "hookSpecificOutput": {
    "hookEventName": "PermissionRequest",
    "decision": {
      "behavior": "allow",
      "updatedInput": {
        "command": "npm run lint"
      }
    }
  }
}

Entradas de actualización de permiso

El campo de salida updatedPermissions 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.
typeCamposEfecto
addRulesrules, behavior, destinationAgrega reglas de permiso. rules es un array de objetos {toolName, ruleContent?}. Omita ruleContent para coincidir con toda la herramienta. behavior es "allow", "deny" o "ask"
replaceRulesrules, behavior, destinationReemplaza todas las reglas del behavior dado en el destination con las rules proporcionadas
removeRulesrules, behavior, destinationElimina reglas coincidentes del behavior dado
setModemode, destinationCambia el modo de permiso. Los modos válidos son default, acceptEdits, dontAsk, bypassPermissions y plan
addDirectoriesdirectories, destinationAgrega directorios de trabajo. directories es un array de cadenas de ruta
removeDirectoriesdirectories, destinationElimina directorios de trabajo
El campo destination en cada entrada determina si el cambio permanece en memoria o persiste en un archivo de configuración.
destinationEscribe en
sessionsolo en memoria, descartado cuando termina la sesión
localSettings.claude/settings.local.json
projectSettings.claude/settings.json
userSettings~/.claude/settings.json
Un hook puede ecoar una de las permission_suggestions que recibió como su propia salida updatedPermissions, que es equivalente a que el usuario seleccione esa opción “siempre permitir” en el diálogo.

PostToolUse

Se ejecuta inmediatamente después de que una herramienta se completa exitosamente. Coincide en el nombre de la herramienta, los mismos valores que PreToolUse.

Entrada de PostToolUse

Los hooks PostToolUse se activan 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.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUse",
  "tool_name": "Write",
  "tool_input": {
    "file_path": "/path/to/file.txt",
    "content": "file content"
  },
  "tool_response": {
    "filePath": "/path/to/file.txt",
    "success": true
  },
  "tool_use_id": "toolu_01ABC123..."
}

Control de decisión de PostToolUse

Los hooks PostToolUse 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, su script de hook puede devolver estos campos específicos del evento:
CampoDescripción
decision"block" solicita a Claude con la reason. Omita para permitir que la acción continúe
reasonExplicación mostrada a Claude cuando decision es "block"
additionalContextContexto adicional para que Claude considere
updatedMCPToolOutputSolo para herramientas MCP: reemplaza la salida de la herramienta con el valor proporcionado
{
  "decision": "block",
  "reason": "Explanation for decision",
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": "Additional information for Claude"
  }
}

PostToolUseFailure

Se ejecuta cuando falla la ejecución de una herramienta. Este evento se activa para llamadas a herramientas que lanzan errores o devuelven resultados de fallo. Use esto para registrar fallos, enviar alertas o proporcionar retroalimentación correctiva a Claude. Coincide en el nombre de la herramienta, los mismos valores que PreToolUse.

Entrada de PostToolUseFailure

Los hooks PostToolUseFailure reciben los mismos campos tool_name y tool_input que PostToolUse, junto con información de error como campos de nivel superior:
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "PostToolUseFailure",
  "tool_name": "Bash",
  "tool_input": {
    "command": "npm test",
    "description": "Run test suite"
  },
  "tool_use_id": "toolu_01ABC123...",
  "error": "Command exited with non-zero status code 1",
  "is_interrupt": false
}
CampoDescripción
errorCadena que describe qué salió mal
is_interruptBooleano opcional que indica si el fallo fue causado por interrupción del usuario

Control de decisión de PostToolUseFailure

Los hooks PostToolUseFailure 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, su script de hook puede devolver estos campos específicos del evento:
CampoDescripción
additionalContextContexto adicional para que Claude considere junto con el error
{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUseFailure",
    "additionalContext": "Additional information about the failure for Claude"
  }
}

Notification

Se ejecuta cuando Claude Code envía notificaciones. Coincide en el tipo de notificación: permission_prompt, idle_prompt, auth_success, elicitation_dialog. Omita el matcher para ejecutar hooks para todos los tipos de notificación. Use matchers separados para ejecutar diferentes controladores dependiendo del tipo de notificación. Esta configuración desencadena 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:
{
  "hooks": {
    "Notification": [
      {
        "matcher": "permission_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/permission-alert.sh"
          }
        ]
      },
      {
        "matcher": "idle_prompt",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/idle-notification.sh"
          }
        ]
      }
    ]
  }
}

Entrada de Notification

Además de los campos de entrada comunes, los hooks Notification reciben message con el texto de notificación, un title opcional y notification_type que indica qué tipo se activó.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "Notification",
  "message": "Claude needs your permission to use Bash",
  "title": "Permission needed",
  "notification_type": "permission_prompt"
}
Los hooks Notification no pueden bloquear o modificar notificaciones. Además de los campos de salida JSON disponibles para todos los hooks, puede devolver additionalContext para agregar contexto a la conversación:
CampoDescripción
additionalContextCadena agregada al contexto de Claude

SubagentStart

Se ejecuta cuando se genera un subagente de Claude Code a través de la herramienta Agent. Admite matchers para filtrar por nombre de tipo de agente (agentes integrados como Bash, Explore, Plan o nombres de agentes personalizados de .claude/agents/).

Entrada de SubagentStart

Además de los campos de entrada comunes, los hooks SubagentStart reciben agent_id con el identificador único para el subagente y agent_type con el nombre del agente (agentes integrados como "Bash", "Explore", "Plan" o nombres de agentes personalizados).
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SubagentStart",
  "agent_id": "agent-abc123",
  "agent_type": "Explore"
}
Los hooks SubagentStart no pueden bloquear la creación de subagentes, pero pueden inyectar contexto en el subagente. Además de los campos de salida JSON disponibles para todos los hooks, puede devolver:
CampoDescripción
additionalContextCadena agregada al contexto del subagente
{
  "hookSpecificOutput": {
    "hookEventName": "SubagentStart",
    "additionalContext": "Follow security guidelines for this task"
  }
}

SubagentStop

Se ejecuta cuando un subagente de Claude Code ha terminado de responder. Coincide en el tipo de agente, los mismos valores que SubagentStart.

Entrada de SubagentStop

Además de los campos de entrada comunes, los hooks SubagentStop reciben stop_hook_active, agent_id, agent_type, agent_transcript_path y last_assistant_message. El campo agent_type es el valor usado 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.
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../abc123.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "SubagentStop",
  "stop_hook_active": false,
  "agent_id": "def456",
  "agent_type": "Explore",
  "agent_transcript_path": "~/.claude/projects/.../abc123/subagents/agent-def456.jsonl",
  "last_assistant_message": "Analysis complete. Found 3 potential issues..."
}
Los hooks SubagentStop usan el mismo formato de control de decisión que los hooks Stop.

TaskCreated

Se ejecuta cuando se está creando una tarea a través de la herramienta TaskCreate. Use esto para aplicar convenciones de nomenclatura, requerir descripciones de tareas o evitar que se creen ciertas tareas. Cuando un hook TaskCreated sale con código 2, la tarea no se crea y el mensaje de stderr se devuelve al modelo como retroalimentación. Para detener al compañero completamente en lugar de re-ejecutarlo, devuelva JSON con {"continue": false, "stopReason": "..."}. Los hooks TaskCreated no admiten matchers y se activan en cada ocurrencia.

Entrada de TaskCreated

Además de los campos de entrada comunes, los hooks TaskCreated reciben task_id, task_subject y opcionalmente task_description, teammate_name y team_name.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCreated",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}
CampoDescripción
task_idIdentificador de la tarea que se está creando
task_subjectTítulo de la tarea
task_descriptionDescripción detallada de la tarea. Puede estar ausente
teammate_nameNombre del compañero que crea la tarea. Puede estar ausente
team_nameNombre del equipo. Puede estar ausente

Control de decisión de TaskCreated

Los hooks TaskCreated admiten dos formas de controlar la creación de tareas:
  • Código de salida 2: la tarea no se crea y el mensaje de stderr se devuelve al modelo como retroalimentación.
  • JSON {"continue": false, "stopReason": "..."}: detiene al compañero completamente, coincidiendo con el comportamiento del hook Stop. El stopReason se muestra al usuario.
Este ejemplo bloquea tareas cujos asuntos no siguen el formato requerido:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

if [[ ! "$TASK_SUBJECT" =~ ^\[TICKET-[0-9]+\] ]]; then
  echo "Task subject must start with a ticket number, e.g. '[TICKET-123] Add feature'" >&2
  exit 2
fi

exit 0

TaskCompleted

Se ejecuta cuando una tarea está siendo marcada como completada. Esto se activa en dos situaciones: cuando cualquier agente marca explícitamente una tarea como completada a través de la herramienta TaskUpdate, o cuando un compañero de equipo de agentes termina su turno con tareas en progreso. Use esto para aplicar criterios de finalización como pasar pruebas o verificaciones de lint antes de que una tarea pueda cerrarse. Cuando un hook TaskCompleted sale con código 2, la tarea no se marca como completada y el mensaje de stderr se devuelve al modelo como retroalimentación. Para detener al compañero completamente en lugar de re-ejecutarlo, devuelva JSON con {"continue": false, "stopReason": "..."}. Los hooks TaskCompleted no admiten matchers y se activan en cada ocurrencia.

Entrada de TaskCompleted

Además de los campos de entrada comunes, los hooks TaskCompleted reciben task_id, task_subject y opcionalmente task_description, teammate_name y team_name.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TaskCompleted",
  "task_id": "task-001",
  "task_subject": "Implement user authentication",
  "task_description": "Add login and signup endpoints",
  "teammate_name": "implementer",
  "team_name": "my-project"
}
CampoDescripción
task_idIdentificador de la tarea que se está completando
task_subjectTítulo de la tarea
task_descriptionDescripción detallada de la tarea. Puede estar ausente
teammate_nameNombre del compañero que completa la tarea. Puede estar ausente
team_nameNombre del equipo. Puede estar ausente

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 de stderr se devuelve al modelo como retroalimentación.
  • JSON {"continue": false, "stopReason": "..."}: detiene al compañero completamente, coincidiendo con el comportamiento del hook Stop. El stopReason se muestra al usuario.
Este ejemplo ejecuta pruebas y bloquea la finalización de tareas si fallan:
#!/bin/bash
INPUT=$(cat)
TASK_SUBJECT=$(echo "$INPUT" | jq -r '.task_subject')

# Ejecute el conjunto de pruebas
if ! npm test 2>&1; then
  echo "Tests not passing. Fix failing tests before completing: $TASK_SUBJECT" >&2
  exit 2
fi

exit 0

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 activan StopFailure en su lugar.

Entrada de Stop

Además de los campos de entrada comunes, los hooks Stop reciben stop_hook_active y last_assistant_message. El campo stop_hook_active es true cuando Claude Code ya está continuando como resultado de un hook de parada. Verifique este valor o procese la transcripción para evitar que Claude Code se ejecute indefinidamente. 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.
{
  "session_id": "abc123",
  "transcript_path": "~/.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Stop",
  "stop_hook_active": true,
  "last_assistant_message": "I've completed the refactoring. Here's a summary..."
}

Control de decisión de Stop

Los hooks Stop y SubagentStop pueden controlar si Claude continúa. Además de los campos de salida JSON disponibles para todos los hooks, su script de hook puede devolver estos campos específicos del evento:
CampoDescripción
decision"block" evita que Claude se detenga. Omita para permitir que Claude se detenga
reasonRequerido cuando decision es "block". Le dice a Claude por qué debe continuar
{
  "decision": "block",
  "reason": "Must be provided when Claude is blocked from stopping"
}

StopFailure

Se ejecuta en lugar de Stop cuando el turno termina debido a un error de API. La salida y el código de salida se ignoran. Use esto 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 reciben error, error_details opcional y last_assistant_message opcional. El campo error identifica el tipo de error y se usa para filtrado de matcher.
CampoDescripción
errorTipo de error: rate_limit, authentication_failed, billing_error, invalid_request, server_error, max_output_tokens o unknown
error_detailsDetalles adicionales sobre el error, cuando estén disponibles
last_assistant_messageEl texto de error renderizado mostrado en la conversación. A diferencia de Stop y SubagentStop, donde este campo contiene la salida conversacional de Claude, para StopFailure contiene la cadena de error de API en sí, como "API Error: Rate limit reached"
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "StopFailure",
  "error": "rate_limit",
  "error_details": "429 Too Many Requests",
  "last_assistant_message": "API Error: Rate limit reached"
}
Los hooks StopFailure no tienen control de decisión. Se ejecutan solo con fines de notificación y registro.

TeammateIdle

Se ejecuta cuando un compañero de equipo de agentes está a punto de quedarse inactivo después de terminar su turno. Use esto para aplicar puertas de calidad antes de que un compañero deje de trabajar, como requerir que pasen verificaciones de lint o verificar que existan archivos de salida. Cuando un hook TeammateIdle sale con código 2, el compañero recibe el mensaje de stderr como retroalimentación y continúa trabajando en lugar de quedarse inactivo. Para detener al compañero completamente en lugar de re-ejecutarlo, devuelva JSON con {"continue": false, "stopReason": "..."}. Los hooks TeammateIdle no admiten matchers y se activan en cada ocurrencia.

Entrada de TeammateIdle

Además de los campos de entrada comunes, los hooks TeammateIdle reciben teammate_name y team_name.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "TeammateIdle",
  "teammate_name": "researcher",
  "team_name": "my-project"
}
CampoDescripción
teammate_nameNombre del compañero que está a punto de quedarse inactivo
team_nameNombre del equipo

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 de stderr como retroalimentación y continúa trabajando en lugar de quedarse inactivo.
  • JSON {"continue": false, "stopReason": "..."}: detiene al compañero completamente, coincidiendo con el comportamiento del hook Stop. El stopReason se muestra al usuario.
Este ejemplo verifica que exista un artefacto de compilación antes de permitir que un compañero se quede inactivo:
#!/bin/bash

if [ ! -f "./dist/output.js" ]; then
  echo "Build artifact missing. Run the build before stopping." >&2
  exit 2
fi

exit 0

ConfigChange

Se ejecuta cuando un archivo de configuración cambia durante una sesión. Use esto para auditar cambios de configuración, aplicar políticas de seguridad o bloquear modificaciones no autorizadas a archivos de configuración. Los hooks ConfigChange se activan para cambios en archivos de configuración, configuración de política administrada y archivos de skill. El campo source en la entrada le dice qué tipo de configuración cambió, y el campo file_path opcional proporciona la ruta al archivo cambiado. El matcher filtra en la fuente de configuración:
MatcherCuándo se activa
user_settings~/.claude/settings.json cambia
project_settings.claude/settings.json cambia
local_settings.claude/settings.local.json cambia
policy_settingsCambios de configuración de política administrada
skillsUn archivo de skill en .claude/skills/ cambia
Este ejemplo registra todos los cambios de configuración para auditoría de seguridad:
{
  "hooks": {
    "ConfigChange": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-config-change.sh"
          }
        ]
      }
    ]
  }
}

Entrada de ConfigChange

Además de los campos de entrada comunes, los hooks ConfigChange reciben source 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ó.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "ConfigChange",
  "source": "project_settings",
  "file_path": "/Users/.../my-project/.claude/settings.json"
}

Control de decisión de ConfigChange

Los hooks ConfigChange pueden bloquear cambios de configuración para que no tengan efecto. Use código de salida 2 o un JSON decision para evitar el cambio. Cuando se bloquea, la nueva configuración no se aplica a la sesión en ejecución.
CampoDescripción
decision"block" evita que el cambio de configuración se aplique. Omita para permitir el cambio
reasonExplicación mostrada al usuario cuando decision es "block"
{
  "decision": "block",
  "reason": "Configuration changes to project settings require admin approval"
}
Los cambios de policy_settings no pueden bloquearse. Los hooks aún se activan para fuentes de policy_settings, por lo que puede usarlos para registro de auditoría, pero cualquier decisión de bloqueo se ignora. Esto asegura que la configuración administrada por empresa siempre tenga efecto.

CwdChanged

Se ejecuta cuando el directorio de trabajo cambia durante una sesión, por ejemplo cuando Claude ejecuta un comando cd. Use esto 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 administran el entorno por directorio. Los hooks CwdChanged tienen acceso a CLAUDE_ENV_FILE. Las variables escritas en ese archivo persisten en comandos Bash posteriores para la sesión, al igual que en los hooks SessionStart. Solo se admiten hooks type: "command". CwdChanged no admite matchers y se activa en cada cambio de directorio.

Entrada de CwdChanged

Además de los campos de entrada comunes, los hooks CwdChanged reciben old_cwd y new_cwd.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project/src",
  "hook_event_name": "CwdChanged",
  "old_cwd": "/Users/my-project",
  "new_cwd": "/Users/my-project/src"
}

Salida de CwdChanged

Además de los campos de salida JSON disponibles para todos los hooks, los hooks CwdChanged pueden devolver watchPaths para establecer dinámicamente qué rutas de archivo FileChanged monitorea:
CampoDescripción
watchPathsArray de rutas absolutas. Reemplaza la lista de monitoreo dinámica actual (las rutas de su configuración de matcher siempre se monitorean). Devolver un array vacío borra la lista dinámica, que es típico al entrar en un nuevo directorio
Los hooks CwdChanged no tienen control de decisión. No pueden bloquear el cambio de directorio.

FileChanged

Se ejecuta cuando un archivo monitoreado cambia en el disco. El campo matcher en su configuración de hook controla qué nombres de archivo monitorear: es una lista separada por tuberías de basenames (nombres de archivo sin rutas de directorio, por ejemplo ".envrc|.env"). El mismo valor de matcher también se usa para filtrar qué hooks se ejecutan cuando cambia un archivo, coincidiendo contra el basename del archivo cambiado. Útil para recargar variables de entorno cuando se modifican archivos de configuración del proyecto. Los hooks FileChanged tienen acceso a CLAUDE_ENV_FILE. Las variables escritas en ese archivo persisten en comandos Bash posteriores para la sesión, al igual que en los hooks SessionStart. Solo se admiten hooks type: "command".

Entrada de FileChanged

Además de los campos de entrada comunes, los hooks FileChanged reciben file_path y event.
CampoDescripción
file_pathRuta absoluta al archivo que cambió
eventQué sucedió: "change" (archivo modificado), "add" (archivo creado) o "unlink" (archivo eliminado)
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../transcript.jsonl",
  "cwd": "/Users/my-project",
  "hook_event_name": "FileChanged",
  "file_path": "/Users/my-project/.envrc",
  "event": "change"
}

Salida de FileChanged

Además de los campos de salida JSON disponibles para todos los hooks, los hooks FileChanged pueden devolver watchPaths para actualizar dinámicamente qué rutas de archivo se monitorean:
CampoDescripción
watchPathsArray de rutas absolutas. Reemplaza la lista de monitoreo dinámica actual (las rutas de su configuración de matcher siempre se monitorean). Use esto cuando su script de hook descubra archivos adicionales para monitorear basados en el archivo cambiado
Los hooks FileChanged no tienen control de decisión. No pueden bloquear el cambio de archivo.

WorktreeCreate

Cuando ejecuta claude --worktree o un subagente usa isolation: "worktree", Claude Code crea una copia de trabajo aislada usando git worktree. Si configura un hook WorktreeCreate, reemplaza el comportamiento predeterminado de git, permitiéndole 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 necesita copiar archivos de configuración local como .env en el nuevo worktree, hágalo dentro de su script de hook. El hook debe devolver la ruta absoluta al directorio de worktree creado. Claude Code usa esta ruta como el directorio de trabajo para la sesión aislada. Los hooks de comando la imprimen en stdout; los hooks HTTP la devuelven a través de hookSpecificOutput.worktreePath. Este ejemplo crea una copia de trabajo SVN e imprime la ruta para que Claude Code la use. Reemplace la URL del repositorio con la suya:
{
  "hooks": {
    "WorktreeCreate": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'NAME=$(jq -r .name); DIR=\"$HOME/.claude/worktrees/$NAME\"; svn checkout https://svn.example.com/repo/trunk \"$DIR\" >&2 && echo \"$DIR\"'"
          }
        ]
      }
    ]
  }
}
El hook lee el 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. Redirija 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 campo name. Este es un identificador slug para el nuevo worktree, especificado por el usuario o generado automáticamente (por ejemplo, bold-oak-a3f2).
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeCreate",
  "name": "feature-auth"
}

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 absoluta al directorio de worktree creado:
  • Hooks de comando (type: "command"): imprimen la ruta en stdout.
  • Hooks HTTP (type: "http"): devuelven { "hookSpecificOutput": { "hookEventName": "WorktreeCreate", "worktreePath": "/absolute/path" } } en el cuerpo de la respuesta.
Si el hook falla o no produce ruta, la creación de worktree falla con un error.

WorktreeRemove

La contraparte de limpieza de WorktreeCreate. Este hook se activa cuando se está eliminando un worktree, ya sea cuando sale de una sesión --worktree y elige eliminarlo, o cuando un subagente con isolation: "worktree" finaliza. Para worktrees basados en git, Claude maneja la limpieza automáticamente con git worktree remove. Si configuró un hook WorktreeCreate para un sistema de control de versiones que no es git, emparéjelo con un hook WorktreeRemove para manejar la limpieza. Sin uno, el directorio de worktree se deja en el disco. 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:
{
  "hooks": {
    "WorktreeRemove": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash -c 'jq -r .worktree_path | xargs rm -rf'"
          }
        ]
      }
    ]
  }
}

Entrada de WorktreeRemove

Además de los campos de entrada comunes, los hooks WorktreeRemove reciben el campo worktree_path, que es la ruta absoluta al worktree que se está eliminando.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "WorktreeRemove",
  "worktree_path": "/Users/.../my-project/.claude/worktrees/feature-auth"
}
Los hooks WorktreeRemove no tienen control de decisión. No pueden bloquear la eliminación de worktree pero pueden realizar tareas de limpieza como eliminar estado de control de versiones o archivar cambios. Los fallos de hook se registran solo en modo de depuración.

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 desencadenada manualmente o automáticamente:
MatcherCuándo se activa
manual/compact
autoCompactación automática cuando la ventana de contexto está llena

Entrada de PreCompact

Además de los campos de entrada comunes, los hooks PreCompact reciben trigger e custom_instructions. Para manual, custom_instructions contiene lo que el usuario pasa a /compact. Para auto, custom_instructions está vacío.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PreCompact",
  "trigger": "manual",
  "custom_instructions": ""
}

PostCompact

Se ejecuta después de que Claude Code completa una operación de compactación. Use este evento para reaccionar al nuevo estado compactado, por ejemplo para registrar el resumen generado o actualizar el estado externo. Los mismos valores de matcher se aplican que para PreCompact:
MatcherCuándo se activa
manualDespués de /compact
autoDespués de compactación automática cuando la ventana de contexto está llena

Entrada de PostCompact

Además de los campos de entrada comunes, los hooks PostCompact reciben trigger y compact_summary. El campo compact_summary contiene el resumen de conversación generado por la operación de compactación.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "PostCompact",
  "trigger": "manual",
  "compact_summary": "Summary of the compacted conversation..."
}
Los hooks PostCompact no tienen control de decisión. No pueden afectar el resultado de compactación pero pueden realizar tareas de seguimiento.

SessionEnd

Se ejecuta cuando termina una sesión de Claude Code. Útil para tareas de limpieza, registro de estadísticas de sesión o guardado del estado de sesión. Admite matchers para filtrar por razón de salida. El campo reason en la entrada del hook indica por qué terminó la sesión:
RazónDescripción
clearSesión borrada con comando /clear
resumeSesión cambiada a través de /resume interactivo
logoutUsuario cerró sesión
prompt_input_exitUsuario salió mientras la entrada del prompt era visible
bypass_permissions_disabledEl modo de permisos de omisión fue deshabilitado
otherOtras razones de salida

Entrada de SessionEnd

Además de los campos de entrada comunes, los hooks SessionEnd reciben un campo reason que indica por qué terminó la sesión. Consulte la tabla de razones anterior para todos los valores.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "hook_event_name": "SessionEnd",
  "reason": "other"
}
Los hooks SessionEnd no tienen control de decisión. No pueden bloquear la terminación de sesión pero pueden realizar tareas de limpieza. Los hooks SessionEnd tienen un tiempo de espera predeterminado de 1,5 segundos. Esto se aplica tanto a la salida de sesión como a /clear y al cambio de sesiones a través de /resume interactivo. Si sus hooks necesitan más tiempo, establezca la variable de entorno CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS en un valor más alto en milisegundos. Cualquier configuración de timeout por hook también está limitada por este valor.
CLAUDE_CODE_SESSIONEND_HOOKS_TIMEOUT_MS=5000 claude

Elicitation

Se ejecuta cuando un servidor MCP solicita entrada del usuario a mitad de la 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 el diálogo completamente. El campo matcher coincide con el nombre del servidor MCP.

Entrada de Elicitation

Además de los campos de entrada comunes, los hooks Elicitation reciben mcp_server_name, message y campos opcionales mode, url, elicitation_id y requested_schema. Para elicitación en modo formulario (el caso más común):
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please provide your credentials",
  "mode": "form",
  "requested_schema": {
    "type": "object",
    "properties": {
      "username": { "type": "string", "title": "Username" }
    }
  }
}
Para elicitación en modo URL (autenticación basada en navegador):
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "Elicitation",
  "mcp_server_name": "my-mcp-server",
  "message": "Please authenticate",
  "mode": "url",
  "url": "https://auth.example.com/login"
}

Salida de Elicitation

Para responder programáticamente sin mostrar el diálogo, devuelva un objeto JSON con hookSpecificOutput:
{
  "hookSpecificOutput": {
    "hookEventName": "Elicitation",
    "action": "accept",
    "content": {
      "username": "alice"
    }
  }
}
CampoValoresDescripción
actionaccept, decline, cancelSi aceptar, rechazar o cancelar la solicitud
contentobjectValores de campo de formulario a enviar. Solo se usa cuando action es accept
El código de salida 2 deniega la elicitación y muestra stderr al usuario.

ElicitationResult

Se ejecuta después de que un usuario responde a una elicitación MCP. Los hooks pueden observar, modificar o bloquear la respuesta antes de que se envíe de vuelta al servidor MCP. El campo matcher coincide con el nombre del servidor MCP.

Entrada de ElicitationResult

Además de los campos de entrada comunes, los hooks ElicitationResult reciben mcp_server_name, action y campos opcionales mode, elicitation_id y content.
{
  "session_id": "abc123",
  "transcript_path": "/Users/.../.claude/projects/.../00893aaf-19fa-41d2-8238-13269b9b3ca0.jsonl",
  "cwd": "/Users/...",
  "permission_mode": "default",
  "hook_event_name": "ElicitationResult",
  "mcp_server_name": "my-mcp-server",
  "action": "accept",
  "content": { "username": "alice" },
  "mode": "form",
  "elicitation_id": "elicit-123"
}

Salida de ElicitationResult

Para anular la respuesta del usuario, devuelva un objeto JSON con hookSpecificOutput:
{
  "hookSpecificOutput": {
    "hookEventName": "ElicitationResult",
    "action": "decline",
    "content": {}
  }
}
CampoValoresDescripción
actionaccept, decline, cancelAnula la acción del usuario
contentobjectAnula valores de campo de formulario. Solo significativo cuando action es accept
El código de salida 2 bloquea la respuesta, cambiando la acción efectiva a decline.

Hooks basados en prompts

Además de hooks de comando y HTTP, 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 cuatro tipos de hooks (command, http, prompt y agent):
  • PermissionRequest
  • PostToolUse
  • PostToolUseFailure
  • PreToolUse
  • Stop
  • SubagentStop
  • TaskCompleted
  • TaskCreated
  • UserPromptSubmit
Eventos que admiten hooks command y http pero no prompt o agent:
  • ConfigChange
  • CwdChanged
  • Elicitation
  • ElicitationResult
  • FileChanged
  • InstructionsLoaded
  • Notification
  • PostCompact
  • PreCompact
  • SessionEnd
  • StopFailure
  • SubagentStart
  • TeammateIdle
  • WorktreeCreate
  • WorktreeRemove
SessionStart solo admite hooks command.

Cómo funcionan los hooks basados en prompts

En lugar de ejecutar un comando Bash, los hooks basados en prompts:
  1. Envían la entrada del hook y su prompt a un modelo Claude, Haiku por defecto
  2. El LLM responde con JSON estructurado que contiene una decisión
  3. Claude Code procesa la decisión automáticamente

Configuración de hook de prompt

Establezca type 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. Claude Code envía el prompt combinado e entrada a un modelo Claude rápido, que devuelve una decisión JSON. Este hook Stop le pide al LLM que evalúe si Claude debe detenerse antes de permitir que finalice:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "Evaluate if Claude should stop: $ARGUMENTS. Check if all tasks are complete."
          }
        ]
      }
    ]
  }
}
CampoRequeridoDescripción
typeDebe ser "prompt"
promptEl texto del prompt a enviar al LLM. Use $ARGUMENTS como marcador de posición para la entrada JSON del hook. Si $ARGUMENTS no está presente, la entrada JSON se agrega al prompt
modelnoModelo a usar para evaluación. Por defecto es un modelo rápido
timeoutnoTiempo de espera en segundos. Predeterminado: 30

Esquema de respuesta

El LLM debe responder con JSON que contenga:
{
  "ok": true | false,
  "reason": "Explanation for the decision"
}
CampoDescripción
oktrue permite la acción, false la previene
reasonRequerido cuando ok es false. Explicación mostrada a Claude

Ejemplo: Hook Stop de múltiples criterios

Este hook Stop usa un prompt detallado para verificar tres condiciones antes de permitir que Claude se detenga. Si "ok" es false, Claude continúa trabajando con la razón proporcionada como su siguiente instrucción. Los hooks SubagentStop usan el mismo formato para evaluar si un subagente debe detenerse:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "prompt",
            "prompt": "You are evaluating whether Claude should stop working. Context: $ARGUMENTS\n\nAnalyze the conversation and determine if:\n1. All user-requested tasks are complete\n2. Any errors need to be addressed\n3. Follow-up work is needed\n\nRespond with JSON: {\"ok\": true} to allow stopping, or {\"ok\": false, \"reason\": \"your explanation\"} to continue working.",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

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.

Cómo funcionan los hooks de agente

Cuando se activa un hook de agente:
  1. Claude Code genera un subagente con su prompt y la entrada JSON del hook
  2. El subagente puede usar herramientas como Read, Grep y Glob para investigar
  3. Después de hasta 50 turnos, el subagente devuelve una decisión estructurada { "ok": true/false }
  4. Claude Code procesa la decisión de la misma manera que un hook de prompt
Los hooks de agente son útiles cuando la verificación requiere inspeccionar archivos reales o salida de prueba, no solo evaluar los datos de entrada del hook solos.

Configuración de hook de agente

Establezca type en "agent" y proporcione una cadena prompt. Los campos de configuración son los mismos que los hooks de prompt, con un tiempo de espera predeterminado más largo:
CampoRequeridoDescripción
typeDebe ser "agent"
promptPrompt que describe qué verificar. Use $ARGUMENTS como marcador de posición para la entrada JSON del hook
modelnoModelo a usar. Por defecto es un modelo rápido
timeoutnoTiempo de espera en segundos. Predeterminado: 60
El esquema de respuesta es el mismo que los hooks de prompt: { "ok": true } para permitir o { "ok": false, "reason": "..." } para bloquear. Este hook Stop verifica que todas las pruebas unitarias pasen antes de permitir que Claude finalice:
{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "agent",
            "prompt": "Verify that all unit tests pass. Run the test suite and check the results. $ARGUMENTS",
            "timeout": 120
          }
        ]
      }
    ]
  }
}

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 durante hasta 120 segundos. Cuando el script finaliza, su salida se entrega en el siguiente turno de conversación:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write",
        "hooks": [
          {
            "type": "command",
            "command": "/path/to/run-tests.sh",
            "async": true,
            "timeout": 120
          }
        ]
      }
    ]
  }
}
El campo timeout establece el tiempo máximo en segundos para el proceso de fondo. Si no se especifica, los hooks asincronos usan el mismo predeterminado de 10 minutos que los hooks sincronos.

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, si el hook produjo una respuesta JSON con un campo systemMessage o additionalContext, ese contenido se entrega a Claude como contexto en el siguiente turno de conversació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.

Ejemplo: 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:
#!/bin/bash
# run-tests-async.sh

# Lee entrada de hook desde stdin
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

# Solo ejecute pruebas para archivos de origen
if [[ "$FILE_PATH" != *.ts && "$FILE_PATH" != *.js ]]; then
  exit 0
fi

# Ejecute pruebas e informe resultados a través de systemMessage
RESULT=$(npm test 2>&1)
EXIT_CODE=$?

if [ $EXIT_CODE -eq 0 ]; then
  echo "{\"systemMessage\": \"Tests passed after editing $FILE_PATH\"}"
else
  echo "{\"systemMessage\": \"Tests failed after editing $FILE_PATH: $RESULT\"}"
fi
Luego agregue esta configuración a .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:
{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/run-tests-async.sh",
            "async": true,
            "timeout": 300
          }
        ]
      }
    ]
  }
}

Limitaciones

Los hooks asincronos tienen varias restricciones en comparación con los hooks sincronos:
  • Solo los hooks type: "command" admiten async. Los hooks basados en prompts no pueden ejecutarse de forma asincrónica.
  • Los hooks asincronos no pueden bloquear llamadas a herramientas o devolver decisiones. En el momento en que se completa el hook, la acción desencadenante ya ha procedido.
  • 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.
  • Cada ejecución crea un proceso de fondo separado. No hay dedu plicación en múltiples activaciones del mismo hook asincrónico.

Consideraciones de seguridad

Descargo de responsabilidad

Los hooks de comando se ejecutan con los permisos completos del usuario del sistema.
Los hooks de comando ejecutan comandos de shell con sus permisos de usuario completos. Pueden modificar, eliminar o acceder a cualquier archivo al que su cuenta de usuario pueda acceder. Revise y pruebe todos los comandos de hook antes de agregarlos a su configuración.

Mejores prácticas de seguridad

Tenga en cuenta estas prácticas al escribir hooks:
  • Validar y desinfectar 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, usando "$CLAUDE_PROJECT_DIR" para la raíz del proyecto
  • Omitir archivos sensibles: evite .env, .git/, claves, etc.

Depurar hooks

Ejecute claude --debug para ver detalles de ejecución de hooks, incluyendo qué hooks coincidieron, sus códigos de salida y salida.
[DEBUG] Executing hooks for PostToolUse:Write
[DEBUG] Found 1 hook commands to execute
[DEBUG] Executing hook command: <Your command> with timeout 600000ms
[DEBUG] Hook command completed with status 0: <Your stdout>
Para obtener detalles de coincidencia de hooks más granulares, establezca 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, bucles infinitos de hooks Stop o errores de configuración, consulte Limitations and troubleshooting en la guía.