Lo esencial
Los skills los llamas tú, cuando los necesitas. Los hooks funcionan sin ti, siempre. Son como las reglas de un contrato de trabajo: el empleado las sigue de forma automática, sin esperar a que se las recuerden cada vez. Un hook se dispara con un evento concreto (antes de una acción, después, ante un error, al terminar) y ejecuta lo que tú le indicaste.
En esta lección está el panorama completo: unos 30 tipos de eventos, 5 tipos de manejadores y el formato exacto de los datos. Empecemos por lo principal.
Conceptos clave
- Hook: una regla automática que se dispara con un evento concreto en Claude Code
- Evento (event): un momento en el ciclo de vida de Claude Code: inicio de sesión, llamada a una herramienta, final, etcétera
- Manejador (handler): qué se ejecuta exactamente con el evento: un script de bash, una solicitud HTTP, una herramienta MCP, un prompt o un agente
- Matcher: un filtro: a qué herramientas o eventos reaccionar exactamente
- settings.json: el archivo de configuración de los hooks (
.claude/settings.jsonpara el proyecto,~/.claude/settings.jsonpara todo) - Exit code: cómo comunica el hook su decisión:
0= todo bien,2= bloquear
Teoría
Skills vs Hooks: cuál es la diferencia
No compiten: son herramientas distintas.
| Skills | Hooks | |
|---|---|---|
| Activación | Tú los llamas de forma explícita | Automática con un evento |
| Alcance | Proyecto o global | Proyecto o global |
| Dónde viven | .claude/skills/<name>/SKILL.md |
.claude/settings.json o ~/.claude/settings.json |
| Para qué | Instrucciones de cómo hacer una tarea | Reglas de seguridad y automatización |
| Analogía | Una receta | Las reglas del contrato de trabajo |
Tipos de eventos (events): cuándo se disparan los hooks
Claude Code admite unos 30 tipos de eventos (la lista exacta crece de versión en versión; consulta la documentación oficial). Para empezar necesitas 6 principales. Los demás son para escenarios avanzados.
Los 6 eventos principales (80% del uso)
| Evento | Cuándo | Para qué |
|---|---|---|
| PreToolUse | ANTES de ejecutar una herramienta | Bloquear acciones peligrosas, revisar condiciones |
| PostToolUse | DESPUÉS de una ejecución exitosa | Registro, auditoría, notificaciones |
| Stop | Claude terminó su respuesta | Aviso de "listo", limpieza, correr pruebas |
| Notification | Claude manda una notificación | Reaccionar a eventos intermedios |
| SessionStart | Inicio o reanudación de la sesión | Cargar contexto, revisar el entorno |
| UserPromptSubmit | El usuario envió una solicitud | Validar, agregar contexto antes de procesar |
Eventos avanzados (cuando los principales te queden chicos)
| Evento | Cuándo | Ejemplo |
|---|---|---|
| SubagentStart | Arranca un subagente | Registrar qué agentes se lanzan |
| SubagentStop | Un subagente terminó | Revisar el resultado del subagente |
| PostToolUseFailure | Una herramienta terminó con error | Mandar una alerta ante el error |
| PostToolBatch | Terminó un lote de llamadas en paralelo | Revisión después de operaciones en lote |
| FileChanged | Un archivo cambió en el disco | Recargar .env cuando cambia |
| ConfigChange | Cambió la configuración | Reaccionar a una actualización de settings |
| PreCompact | Antes de comprimir el contexto | Guardar lo importante antes de la compresión |
| SessionEnd | La sesión termina | Limpieza final, guardar el estado |
| StopFailure | La respuesta se cortó por un error de la API | Alerta por rate limit o error de facturación |
| PermissionRequest | Apareció una ventana de permiso | Aprobar automáticamente ciertas operaciones |
| CwdChanged | Cambió la carpeta de trabajo | Cambiar de entorno |
| Setup | Arranque con --init o --maintenance |
Instalar dependencias al inicializar |
Más a fondo: los 4 eventos principales
⚠️ Importante sobre la vigencia: los primeros materiales sobre Claude Code mencionan "4 tipos de hooks" (Pre-tool / Post-tool / Stop / Need you): es el modelo básico de la documentación vieja. Para octubre de 2026, el ecosistema creció a unos 30 eventos del ciclo de vida (PreToolUse, PostToolUse, UserPromptSubmit, Stop, SessionStart, SubagentStop, Notification, PreCompact, PostCompact y otros) y 5 tipos de manejadores (command, http, mcp_tool, prompt, agent). Estos 4 escenarios básicos siguen cubriendo la mayoría de las tareas. Los demás eventos sirven para afinar encima. Más detalles en la lección Hook-Deny-By-Design: ahí se usan justo los eventos avanzados.
Equivalencias entre el viejo "cuarteto" y los eventos actuales:
| Categoría vieja | Eventos actuales de 2026 |
|---|---|
| Pre-tool | PreToolUse + PreCompact + UserPromptSubmit |
| Post-tool | PostToolUse + PostCompact + SessionStart |
| Stop | Stop + SubagentStop |
| Need you | Notification + UserPromptSubmit |
1. PreToolUse: revisión antes de la acción
Cuándo se dispara: antes de que Claude ejecute cualquier herramienta (escribir un archivo, leerlo, un comando de bash, etc.)
Para qué: bloquear acciones peligrosas, revisar condiciones, proteger archivos sensibles.
Escenarios prácticos:
- No dejar que Claude edite el archivo
.envcon las claves de API - Revisar que el código no tenga secretos escritos directamente
- Bloquear la escritura en la base de datos de producción
- Revisar el presupuesto antes de operaciones caras
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/path/to/check-secrets.sh"
}
]
}
]
}
}Si el script regresa exit code 2 → Claude Code se detiene y no ejecuta la acción. El mensaje de stderr se le pasa a Claude.
2. PostToolUse: acción después de ejecutar
Cuándo se dispara: después de que Claude ejecutó una herramienta con éxito.
Para qué: registrar qué cambió, crear un registro de auditoría, avisar de cambios concretos.
Escenarios prácticos:
- Anotar en un archivo de registro qué archivos cambió Claude y cuándo
- Mandar un aviso a tu chat de trabajo (Slack, Telegram u otro) cuando cambió un archivo crítico
- Actualizar un contador de operaciones para controlar el presupuesto
- Crear un commit de git después de los cambios
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/path/to/audit-log.sh"
}
]
}
]
}
}3. Stop: al terminar la respuesta
Cuándo se dispara: cuando Claude Code termina de responder y cierra la tarea.
Para qué: avisar que el trabajo está listo, limpiar, lanzar el siguiente paso.
Escenarios prácticos:
- Notificación de macOS "Claude terminó la tarea": puedes trabajar en otra cosa mientras tanto
- Mandar el reporte final a tu chat de trabajo
- Correr las pruebas después de que Claude escribió código
- Commit de git automático al terminar
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude terminó la tarea\" with title \"Claude Code\"'"
}
]
}
]
}
}4. Notification: avisos
Cuándo se dispara: cuando Claude Code le manda una notificación al usuario (pero no es Stop).
Para qué: reaccionar a los mensajes intermedios de Claude, no solo al final.
Diferencia con Stop: Stop es el final completo de la tarea. Notification es Claude avisando algo a mitad del proceso.
Valores de matcher: permission_prompt, idle_prompt, auth_success
Escenarios prácticos:
- Registrar todos los mensajes intermedios de Claude
- Avisar cuando Claude encuentra un error y sigue trabajando
- Seguir el avance de tareas largas
5 tipos de manejadores (handlers): CÓMO ejecuta el hook la acción
El evento es el CUÁNDO. El manejador es el CÓMO. Claude Code admite 5 tipos de manejadores:
| Tipo | Qué hace | Cuándo usarlo |
|---|---|---|
command |
Ejecuta un script de bash | El 90% de los casos: revisiones, registros, avisos |
http |
Manda una solicitud HTTP POST | Webhook a Slack, a un mensajero, a un servicio externo |
mcp_tool |
Llama una herramienta de un servidor MCP | Cuando ya tienes conectado un servidor MCP |
prompt |
Manda texto a un modelo rápido | Revisión con IA de la solicitud antes de ejecutarla |
agent |
Lanza un subagente (experimental) | Revisiones complejas que requieren razonar |
Manejador command (script de bash): el principal
El más simple y común. Ejecuta un script de shell.
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-secrets.sh",
"timeout": 30
}Manejador http (webhook): para servicios externos
Manda los datos del hook como una solicitud POST. El cuerpo de la solicitud es el mismo JSON que un hook command recibe por stdin.
{
"type": "http",
"url": "http://localhost:8080/hooks/validate",
"headers": {
"Authorization": "Bearer $MY_TOKEN"
},
"allowedEnvVars": ["MY_TOKEN"],
"timeout": 30
}La respuesta del servidor en formato JSON se procesa igual que el stdout de un hook command.
Manejador prompt: revisión rápida con IA
Manda el texto a un modelo rápido. Sirve para evaluar si una solicitud es segura.
{
"type": "prompt",
"prompt": "¿Este comando de bash es seguro? Comando: $ARGUMENTS\nResponde en JSON: {\"decision\": \"allow\"} o {\"decision\": \"deny\"}",
"timeout": 30
}Manejadores mcp_tool y agent: avanzados
mcp_tool llama una herramienta de un servidor MCP conectado. agent lanza un subagente para revisar (la documentación lo marca como experimental). Los dos son para escenarios complejos, no para empezar.
Matcher: el filtro de "a qué reaccionar"
El matcher define a qué herramientas CONCRETAS reaccionar. Sin matcher, el hook se dispara con TODO.
| Valor de matcher | Qué hace | Ejemplo |
|---|---|---|
"Bash" |
Solo comandos de bash | El hook se dispara con npm test, git push |
"Write|Edit" |
Escritura o edición de archivos | Hook para revisar secretos |
"mcp__memory__.*" |
Todas las herramientas del servidor MCP memory | Auditoría de operaciones MCP |
"*" o vacío |
Todas las herramientas | Registro universal |
El matcher es una expresión regular si tiene caracteres especiales, o una coincidencia exacta si solo tiene letras.
El filtro adicional "if" permite filtrar por argumentos (por ejemplo, Bash(git *) o Edit(*.ts)):
{
"matcher": "Bash",
"hooks": [{
"type": "command",
"if": "Bash(rm *)",
"command": "echo 'rm bloqueado' >&2 && exit 2"
}]
}Aquí el hook se dispara solo para Bash, y solo si el comando empieza con rm. La sintaxis completa de if está en la referencia oficial de hooks.
Estructura de settings.json (formato oficial)
Todos los hooks viven en settings.json. Hay tres niveles de archivos:
| Archivo | Alcance | ¿Se comparte? |
|---|---|---|
~/.claude/settings.json |
Todos los proyectos (global) | No |
.claude/settings.json |
Este proyecto | Sí (se sube a git) |
.claude/settings.local.json |
Este proyecto (local) | No (va en .gitignore) |
Estructura: 3 niveles de anidación
hooks → Evento → [{ matcher, hooks: [{ type, command, ... }] }]Ejemplo completo con 3 hooks:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/check-secrets.sh",
"timeout": 30
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/audit-log.sh"
}
]
}
],
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude terminó\" with title \"Claude Code\"'"
}
]
}
]
}
}Reglas clave:
- Los nombres de los eventos van en CamelCase:
PreToolUse, nopre_tool_use - Cada evento contiene un arreglo de grupos con
matcheryhooks - Cada grupo contiene un arreglo de manejadores
hooks matcherfiltra por herramienta (para Stop/SessionStart no hace falta)- Puedes apagar todos los hooks:
"disableAllHooks": true
Cómo recibe datos el hook (protocolo JSON)
Claude Code le pasa los datos al hook por stdin (en los hooks command) o en el cuerpo del POST (en los hooks http), en formato JSON.
Qué recibe un hook PreToolUse
{
"session_id": "abc123",
"cwd": "/Users/me/project",
"hook_event_name": "PreToolUse",
"tool_name": "Write",
"tool_input": {
"file_path": "/project/config.py",
"content": "API_KEY = 'sk-proj-abc123...'"
}
}Qué recibe un hook PostToolUse
{
"session_id": "abc123",
"cwd": "/Users/me/project",
"hook_event_name": "PostToolUse",
"tool_name": "Bash",
"tool_input": { "command": "npm test" },
"tool_response": "All tests passed"
}Qué recibe un hook SessionStart
{
"session_id": "abc123",
"cwd": "/Users/me/project",
"hook_event_name": "SessionStart",
"source": "startup",
"model": "<identificador del modelo>"
}Cómo le responde el hook a Claude Code (respuesta JSON)
El hook puede regresar un JSON por stdout para controlar el comportamiento:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"permissionDecisionReason": "Comando seguro",
"additionalContext": "Una pista para Claude"
}
}Decisiones para PreToolUse: "allow" (permitir sin preguntar), "deny" (bloquear), "ask" (preguntarle al usuario); en las versiones nuevas también existe "defer".
Si varios hooks dan decisiones distintas, la prioridad es: deny > ask > allow.
Exit codes: cómo comunica el hook su decisión
| Exit code | Resultado |
|---|---|
| 0 | Éxito. Claude Code interpreta stdout como JSON |
| 2 | Bloqueo. El stderr se le pasa a Claude como motivo |
| 1 u otro | Error no crítico: se anota en el registro y el trabajo sigue |
Importante: el exit code 2 (¡no el 1!) bloquea la acción. El exit code 1 es solo un error: el hook "se rompió", pero Claude sigue trabajando.
Cómo agregar un hook: dos formas
Forma 1: pedírselo a Claude Code (recomendado para empezar)
Quiero hacer un hook: cuando Claude termine una respuesta, que me mande una notificación de macOS
Claude Code te hará preguntas para aclarar, creará el script de bash y agregará la entrada en settings.json.
Forma 2: con /hooks en la terminal
claude
# Dentro de Claude Code:
/hooks
# Abre la lista de hooks configurados (solo para ver)
# Para editarlos, cambia settings.json directamenteMuestra los hooks actuales: el tipo de manejador ([command], [http], [prompt]), el origen ([User], [Project], [Local]) y el matcher.
Hooks globales vs de proyecto
Los hooks de seguridad (secretos, presupuesto) ponlos de forma global (~/.claude/settings.json). Te protegen en todos tus proyectos.
Los hooks propios de un proyecto (linter, pruebas, despliegue) ponlos en el proyecto (.claude/settings.json). Puedes subirlos a git y compartirlos con tu equipo.
~/.claude/settings.json ← Seguridad (todos los proyectos)
└── PreToolUse: no-secrets
└── PreToolUse: budget-check
.claude/settings.json ← De proyecto (este proyecto)
└── PostToolUse: run-linter
└── Stop: run-testsTodos los niveles se combinan. Los hooks globales + de proyecto + locales trabajan juntos.
Variables de entorno en los hooks
Dentro de un hook command tienes disponibles:
| Variable | Qué contiene |
|---|---|
$CLAUDE_PROJECT_DIR |
La raíz del proyecto (¡ponla entre comillas!) |
$CLAUDE_ENV_FILE |
La ruta para guardar variables de entorno durante toda la sesión |
Ejemplo de uso:
#!/bin/bash
# Ejecutar un script desde la carpeta del proyecto
"$CLAUDE_PROJECT_DIR"/.claude/hooks/my-check.shPráctica
Tarea: conocer la estructura de settings.json
- Abre o crea el archivo
.claude/settings.json - Pídele a Claude Code:
Muéstrame la configuración actual de hooks - Pídele que cree el hook más simple:
Crea un hook: cuando Claude termine una respuesta, muestra la notificación "Listo", y presiona yes - Comprueba que settings.json se actualizó: mira la entrada nueva en la sección
Stop(¡CamelCase!) - Pruébalo: hazle a Claude cualquier pregunta sencilla; debe aparecer la notificación
- Escribe
/hooksen Claude Code y comprueba que el hook aparece en la lista
Objetivo: entender que settings.json es el punto único de configuración de todos los hooks, y que los hooks funcionan solos, sin que intervengas
Herramientas y recursos
.claude/settings.json: archivo de hooks del proyecto (se sube a git)~/.claude/settings.json: archivo global de hooks (todos los proyectos)/hooks: comando para ver los hooks configurados en Claude Codejq: herramienta para procesar JSON en scripts de bash (la necesitas para los hooks command)osascript: comando de macOS para mandar notificaciones nativas- Documentación oficial (referencia vigente): https://code.claude.com/docs/en/hooks — todos los eventos del ciclo de vida, formatos JSON, exit codes
- Eventos avanzados: la lección Hook-Deny-By-Design, con práctica de SubagentStop, PreCompact y PermissionRequest
Fuentes
- https://code.claude.com/docs/en/hooks — referencia oficial (revisada en octubre de 2026)
- La lección Hook-Deny-By-Design: eventos avanzados en la práctica
Conclusiones clave
Hooks ≠ Skills. Los skills los llamas tú. Los hooks funcionan solos con un evento: los configuras una vez.
Hay unos 30 tipos de eventos, pero empieza con 4-6 principales: PreToolUse, PostToolUse, Stop, Notification, SessionStart, UserPromptSubmit.
5 tipos de manejadores: command (bash), http (webhook), mcp_tool, prompt (revisión con IA), agent (subagente). Para empezar, con command basta.
El matcher filtra por herramienta:
"Write|Edit"solo operaciones con archivos,"Bash"solo comandos.
Exit code 2 = bloqueo, exit code 0 = permitir. ¡No el 1: el que bloquea es el 2!
Los hooks viven en settings.json en tres niveles: global, de proyecto y local. Todos se combinan.
Qué sigue
La marca se guarda solo en este navegador y no se envía a ningún sitio. Mi progreso