Tutorial de Hooks de Claude Code: 5 Hooks de Producción Desde Cero
Claude Code ejecuta la acción correcta aproximadamente el 95% de las veces. El 5% restante incluye hacer force-push a main, saltarse el formateador y hacer commit de código que no pasa el linter. Los hooks eliminan ese 5% al agregar puertas determinísticas en 17 puntos del ciclo de vida del flujo de trabajo de Claude. Se ejecutan cada vez, sin excepción, independientemente de la redacción del prompt o el comportamiento del modelo.
Resumen: Los hooks son comandos de shell activados por eventos del ciclo de vida de Claude Code. Los hooks PreToolUse inspeccionan y bloquean acciones (código de salida 2 = bloquear, salida 0 = permitir). Los hooks PostToolUse validan y formatean después del hecho. Configúrelos en .claude/settings.json con un matcher regex y un array anidado de hooks. Este tutorial construye cinco hooks de producción: autoformateador, puerta de seguridad, ejecutor de pruebas, alerta de notificación y verificación de calidad pre-commit.
Puntos Clave
- Desarrolladores independientes: Comience con el autoformateador (Hook 1) y la puerta de seguridad (Hook 2). Estos dos hooks previenen los errores más comunes de Claude Code sin mantenimiento continuo.
- Líderes de equipo: Haga commit de los hooks en
.claude/settings.jsonen su repositorio. Cada miembro del equipo obtiene las mismas puertas de seguridad y verificaciones de calidad automáticamente. - Ingenieros de seguridad: El código de salida 2 bloquea la acción. El código de salida 1 solo registra una advertencia. Cada hook de seguridad PreToolUse debe usar
exit 2, o no proporciona ninguna aplicación.
¿Qué Son los Hooks?
Los hooks son comandos de shell que se ejecutan en eventos específicos del ciclo de vida durante una sesión de Claude Code. Se ejecutan fuera del LLM como scripts simples activados por las acciones de Claude, no como prompts interpretados por el modelo.
Cuatro categorías clave cubren los casos de uso más comunes (Claude Code soporta 17 tipos de eventos en total):
- Eventos de sesión:
SessionStartyStopse activan cuando una sesión comienza o termina. Úselos para configuración, limpieza y notificaciones. - Eventos de herramientas:
PreToolUseyPostToolUsese activan antes y después de que Claude usa una herramienta (escribir un archivo, ejecutar un comando bash o buscar código). Estos son los hooks más poderosos porque pueden inspeccionar y bloquear acciones específicas. - Eventos de notificación:
Notificationse activa cuando Claude genera una notificación. Útil para enrutar alertas a Slack, notificaciones de escritorio o sistemas de registro. - Eventos de subagentes:
SubagentStopse activa cuando un subagente (generado mediante la herramienta Agent) completa su tarea. Los hooks también se activan para las acciones de subagentes, por lo que sus puertas de seguridad se aplican recursivamente.
La semántica del código de salida importa. Salida 0 significa éxito (proceder). Salida 2 significa bloquear la acción. Salida 1 significa un error no bloqueante del hook donde la acción aún procede. Cada hook crítico de seguridad debe usar exit 2 para realmente aplicar su puerta.
Conceptos Básicos de Configuración de Hooks
Los hooks residen en sus archivos de configuración:
- Nivel de proyecto:
.claude/settings.jsonen la raíz de su repositorio (compartido con su equipo) - Nivel de usuario:
~/.claude/settings.json(sus hooks personales, aplicados globalmente)
La estructura JSON:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/path/to/your/script.sh"
}
]
}
],
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/path/to/another-script.sh"
}
]
}
]
}
}
Cada entrada tiene un matcher (regex que coincide con nombres de herramientas como Bash, Write, Edit, Read, Glob, Grep o Agent) y un array de hooks con definiciones de hooks. Cada hook especifica un type ("command" para comandos de shell) y el command a ejecutar. El matcher Write|Edit coincide con ambos tipos de herramientas.
También puede gestionar hooks interactivamente con el comando /hooks dentro de una sesión de Claude Code.
Cuando un hook se activa, Claude Code pasa contexto mediante variables de entorno ($FILE_PATH para operaciones de archivos) y stdin (un objeto JSON que contiene el nombre de la herramienta, parámetros y metadatos de sesión). Su script lee esto para tomar decisiones.
5 Hooks Prácticos
Cada hook a continuación resuelve un problema real que encontré al usar Claude Code como mi herramienta principal de desarrollo. Todos los ejemplos usan el esquema anidado correcto de hooks.
1. Autoformateo al Editar Archivos
Claude escribe código funcionalmente correcto que ocasionalmente rompe las reglas de formato de su proyecto. En lugar de pedirle a Claude que reformatee, ejecute su formateador automáticamente después de cada escritura de archivo.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]]; then black --quiet \"$FILE_PATH\" 2>/dev/null; elif [[ \"$FILE_PATH\" == *.js ]] || [[ \"$FILE_PATH\" == *.ts ]]; then npx prettier --write \"$FILE_PATH\" 2>/dev/null; fi'"
}
]
}
]
}
}
Claude Code establece $FILE_PATH con el archivo modificado. El hook verifica la extensión y ejecuta el formateador apropiado. Los archivos Python reciben black, los archivos JavaScript y TypeScript reciben prettier. El 2>/dev/null suprime la salida ruidosa para que solo vea errores reales.
Para proyectos más grandes, mueva el comando en línea a un script independiente para mejorar la legibilidad.
2. Puerta de Seguridad para Comandos Peligrosos
Los hooks PreToolUse en la herramienta Bash inspeccionan el comando que Claude está por ejecutar y lo bloquean si coincide con un patrón peligroso. Escribí este hook después de que Claude hiciera force-push a main durante una sesión de refactorización.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"rm\\s+-rf\\s+/|git\\s+push\\s+(-f|--force)\\s+(origin\\s+)?main|git\\s+reset\\s+--hard|DROP\\s+TABLE|:(){ :|:& };:\"; then echo \"BLOCKED: Dangerous command detected: $CMD\" >&2; exit 2; fi'"
}
]
}
]
}
}
Cuando este hook sale con código 2, Claude Code cancela el comando pendiente. El mensaje de error se imprime tanto en su terminal como en el contexto de Claude, para que el modelo entienda por qué la acción falló y sugiera una alternativa más segura.
Patrones bloqueados:
- rm -rf / (eliminación recursiva desde la raíz)
- git push --force main y git push -f main (force push a la rama principal)
- git reset --hard (destrucción de trabajo sin commit)
- DROP TABLE (destrucción accidental de base de datos)
- Fork bombs
Personalice esta lista para su entorno. Las bases de datos de producción necesitan patrones de SQL destructivo. Los despliegues basados en CLI necesitan guardias de comandos de despliegue.
3. Ejecutor de Pruebas Después de Cambios
Cuando Claude edita un archivo Python, ejecute automáticamente las pruebas relevantes. Esto detecta regresiones inmediatamente en lugar de descubrirlas tres ediciones de archivo después.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "bash -c 'if [[ \"$FILE_PATH\" == *.py ]] && [[ \"$FILE_PATH\" != *test_* ]]; then TEST_FILE=\"tests/test_$(basename \"$FILE_PATH\")\"; if [[ -f \"$TEST_FILE\" ]]; then python -m pytest \"$TEST_FILE\" -x --tb=short 2>&1 | tail -20; fi; fi'"
}
]
}
]
}
}
El hook verifica si el archivo editado es un archivo fuente Python (no un archivo de pruebas), busca un archivo de pruebas correspondiente usando la convención de nombres con prefijo test_, y lo ejecuta si lo encuentra. La bandera -x se detiene en el primer fallo, y tail -20 mantiene la salida concisa.
Nota: Este hook asume un directorio tests/ plano con la convención de nombres con prefijo test_. Ajuste la construcción de rutas para directorios de pruebas anidados o diferentes convenciones de nombres.
Este hook es particularmente valioso durante sesiones de refactorización donde Claude toca múltiples archivos. El ciclo de retroalimentación inmediata previene la cascada de “arreglar una cosa, romper tres más” que ocurre cuando las pruebas se ejecutan solo al final.
4. Notificación al Finalizar la Sesión
Las sesiones largas de Claude Code pueden tomar minutos. En lugar de observar la terminal, reciba una notificación cuando la sesión termine.
{
"hooks": {
"Stop": [
{
"matcher": "",
"hooks": [
{
"type": "command",
"command": "osascript -e 'display notification \"Claude Code session ended\" with title \"Claude Code\"'"
}
]
}
]
}
}
Este ejemplo de macOS usa osascript para activar una notificación nativa. Para Linux, reemplace la línea de osascript con notify-send "Claude Code" "Session ended". Para notificaciones de Slack, use un webhook:
curl -s -X POST "$SLACK_WEBHOOK_URL" \
-H 'Content-type: application/json' \
-d '{"text": "Claude Code session ended"}'
Uso la variante de Slack para tareas en segundo plano iniciadas con & <task> (el modo en segundo plano de Claude Code). La notificación de escritorio maneja las sesiones interactivas.
5. Verificación de Calidad Antes del Commit
Antes de que Claude ejecute git commit, valide que el código pase el linting. Esto detecta problemas que el formateo por sí solo no detecta: imports no utilizados, variables indefinidas, errores de tipo.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "bash -c 'INPUT=$(cat); CMD=$(echo \"$INPUT\" | jq -r \".tool_input.command\"); if echo \"$CMD\" | grep -qE \"^git\\s+commit\"; then if ! LINT_OUTPUT=$(ruff check . --select E,F,W 2>&1); then echo \"LINT FAILED -- fix before committing:\" >&2; echo \"$LINT_OUTPUT\" >&2; exit 2; fi; fi'"
}
]
}
]
}
}
Este hook se activa solo cuando el comando Bash comienza con git commit. Ejecuta ruff (un linter rápido de Python) con reglas de errores, pyflakes y advertencias. Si existe algún problema, el commit se bloquea (exit 2) y Claude ve la salida del lint, lo que típicamente hace que corrija los problemas y reintente.
Puede apilar múltiples verificaciones de calidad: mypy para verificación de tipos, bandit para escaneo de seguridad, o los scripts de validación personalizados de su proyecto. Los hooks PreToolUse en comandos Bash le dan una puerta programable antes de cualquier acción de shell.
Consejos para Depurar Hooks
Los hooks fallan silenciosamente con más frecuencia de la que esperaría. Cinco técnicas que uso para depurarlos:
- Pruebe los scripts de forma independiente primero. Envíe JSON de ejemplo a su script manualmente:
echo '{"tool_input":{"command":"git commit -m test"}}' | bash your-hook.sh. Si falla fuera de Claude Code, falla dentro de él. - Use stderr para la salida de depuración. Todo lo que su hook escribe en stderr aparece en el contexto de Claude. Escriba
echo "DEBUG: matched $CMD" >&2mientras desarrolla, luego elimínelo una vez que el hook sea sólido. - Cuidado con las fallas de jq. Si su ruta JSON es incorrecta,
jqdevuelvenullsilenciosamente y sus condicionales no coincidirán. Pruebe sus expresionesjqcontra input real de herramientas. - Verifique los códigos de salida. Exit 2 bloquea acciones. Exit 1 solo advierte. Un hook PreToolUse que accidentalmente usa
exit 1no proporciona ninguna aplicación mientras aparenta funcionar. Comience permisivo (exit 0 por defecto) y useexit 2solo para patrones bloqueados específicos. - Mantenga los hooks rápidos. Los hooks se ejecutan de forma sincrónica. Un hook que toma 5 segundos agrega 5 segundos a cada uso de herramienta que coincida. Mantengo todos mis hooks por debajo de 2 segundos, idealmente por debajo de 500 milisegundos.
Próximos Pasos
Estos cinco hooks cubren los fundamentos: formateo, seguridad, pruebas, notificaciones y puertas de calidad. Una vez que se sienta cómodo con estos patrones, puede construir hooks para inyección de contexto (agregar instrucciones específicas del proyecto al inicio de la sesión), guardias de recursión (prevenir bucles infinitos de subagentes) y orquestación de flujos de trabajo (encadenar procesos de múltiples pasos).
Para la arquitectura de hooks, los 17 eventos del ciclo de vida y patrones avanzados, consulte la sección de hooks de mi guía completa: Guía de Claude Code: ¿Cómo Funcionan los Hooks?
También escribí sobre las historias de origen detrás de mis 95 hooks de producción en Hooks de Claude Code: Por Qué Existe Cada Uno de Mis 95 Hooks, que cubre los incidentes que motivaron cada uno.
Preguntas Frecuentes
¿Pueden los hooks bloquear la ejecución de un comando de Claude Code?
Sí. Los hooks PreToolUse bloquean cualquier acción de herramienta al salir con código 2. Claude Code cancela la acción pendiente y muestra la salida stderr del hook al modelo. Exit 1 es un error no bloqueante del hook donde la acción aún procede. Esta distinción es crítica: cada hook de seguridad debe usar exit 2, no exit 1. Claude ve la razón del rechazo y sugiere una alternativa más segura.
¿Dónde coloco los archivos de configuración de hooks?
Las configuraciones de hooks van en .claude/settings.json para hooks a nivel de proyecto (commitidos en su repositorio, compartidos con su equipo) o ~/.claude/settings.json para hooks a nivel de usuario (personales, aplicados a todos los proyectos). Los hooks a nivel de proyecto tienen precedencia cuando ambos existen. Recomiendo usar rutas absolutas para los archivos de scripts para evitar problemas con el directorio de trabajo.
¿Funcionan los hooks con subagentes?
Sí. Los hooks se activan para las acciones de subagentes también. Si Claude genera un subagente mediante la herramienta Agent, sus hooks PreToolUse y PostToolUse se ejecutan para cada herramienta que el subagente usa. Sin este comportamiento, un subagente podría evadir sus puertas de seguridad. El evento SubagentStop le permite ejecutar limpieza o validación cuando un subagente completa su tarea.