Hooks de Claude Code: por qué existe cada uno de mis 95 hooks
Construí 95 hooks para Claude Code. Cada uno existe porque algo salió mal primero. El git-safety-guardian existe porque Claude hizo force-push a main. El recursion-guard existe porque un subagente generó hijos infinitos. El blog-quality-gate existe porque publiqué un artículo con 7 frases en voz pasiva y una nota al pie huérfana.1
Los hooks de Claude Code son comandos de shell que se ejecutan en puntos específicos del ciclo de vida (inicio de sesión, antes/después del uso de herramientas, envío de prompt y finalización de respuesta), proporcionando barreras de protección deterministas sobre el comportamiento probabilístico del LLM. Los hooks reciben JSON por stdin y devuelven decisiones (bloquear, permitir o modificar) por stdout. Aplican políticas como bloquear force-pushes a main, prevenir generaciones recursivas de agentes, inyectar contexto por sesión y controlar la calidad de salida que el LLM por sí solo no puede garantizar.
TL;DR
Los hooks de Claude Code ejecutan comandos de shell en puntos específicos del ciclo de vida durante el desarrollo asistido por IA. Los hooks proporcionan garantías deterministas (bloqueando comandos de git peligrosos, inyectando contexto, aplicando calidad) sobre un sistema probabilístico (el LLM). Tras construir 95 hooks en mi infraestructura, descubrí que los mejores hooks surgen de incidentes, no de la planificación. Este artículo cubre la arquitectura, las historias de origen detrás de mis hooks más críticos y los patrones que aprendí tras 9 meses de desarrollo de hooks.
La arquitectura
Claude Code expone 26 eventos del ciclo de vida donde los hooks pueden interceptar, modificar o bloquear el comportamiento (a partir de v2.1.116, abril de 2026).2 Mis hooks se dirigen a seis de los más comunes:
Eventos de sesión
| Evento | Cuándo se dispara | Mis hooks |
|---|---|---|
| SessionStart | Comienza una nueva sesión | session-start.sh: inyecta la fecha, valida venv, inicializa el estado de recursión |
| SessionEnd | La sesión termina | Limpia archivos temporales |
Eventos de ejecución de herramientas
| Evento | Cuándo se dispara | Mis hooks |
|---|---|---|
| PreToolUse | Antes de que se ejecute cualquier herramienta | git-safety-guardian.sh, recursion-guard.sh, credentials-check.sh |
| PostToolUse | Después de que la herramienta termina | post-deliberation.sh, log-bash.sh |
Eventos de respuesta
| Evento | Cuándo se dispara | Mis hooks |
|---|---|---|
| UserPromptSubmit | El usuario envía un prompt | Inyectores de contexto (fecha, rama, info del modelo) |
| Stop | Claude termina de responder | deliberation-pride-check.sh, reviewer-stop-gate.sh |
Cada hook recibe JSON por stdin y se comunica a través de stdout:
{"decision": "block", "reason": "Force push to main is prohibited"}
O permite silenciosamente saliendo con código 0.3
Historias de origen: los hooks que más importan
Hook 1: git-safety-guardian.sh (PreToolUse:Bash)
El incidente: durante una sesión temprana de Claude Code, le pedí al agente que “limpiara el historial de git”. El agente ejecutó git push --force origin main. El force push sobrescribió tres días de commits en una rama compartida. Recuperé desde una copia de seguridad local, pero el proceso de recuperación de 4 horas me convenció de que el juicio probabilístico nunca debe controlar operaciones destructivas de git.
El hook:
#!/bin/bash
INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
# Only check git commands
echo "$COMMAND" | grep -qE '\bgit\b' || exit 0
# Block force push to main/master
if echo "$COMMAND" | grep -qiE 'git\s+push\s+.*--force.*\b(main|master)\b'; then
cat << EOF
{"decision": "block", "reason": "Force push to main/master blocked by safety hook"}
EOF
fi
La lección: el hook no intenta entender la intención. Hace pattern-matching sobre la cadena del comando. Simple, determinista, imposible de eludir mediante prompting ingenioso. El agente todavía puede hacer force-push a ramas de funciones (a veces legítimo), pero main/master están permanentemente protegidas.4
Salvamentos totales: 8 intentos de force-push interceptados en 9 meses.
Hook 2: recursion-guard.sh (PreToolUse:Task)
El incidente: mientras construía el sistema de deliberación, ejecuté una sesión que generó 3 subagentes de exploración. Cada subagente, al carecer de límites de generación, generó sus propios subagentes. La recursión consumió tokens de API a 10 veces la tasa normal. Maté la sesión manualmente tras notar la quema acelerada de tokens.
El hook:
#!/bin/bash
CONFIG_FILE="${HOME}/.claude/configs/recursion-limits.json"
STATE_FILE="${HOME}/.claude/state/recursion-depth.json"
MAX_DEPTH=2
MAX_CHILDREN=5
DELIB_SPAWN_BUDGET=2
DELIB_MAX_AGENTS=12
# Validate integers safely (((VAR++)) crashes with set -e when VAR=0)
is_positive_int() {
[[ "$1" =~ ^[0-9]+$ ]] && [[ "$1" -gt 0 ]]
}
La decisión de diseño clave: los agentes heredan un presupuesto de generación de su padre en lugar de incrementar la profundidad. Un agente raíz con budget=12 puede distribuir ese presupuesto a través de cualquier forma de árbol. Los límites basados en profundidad son demasiado rígidos (impiden cadenas profundas pero estrechas que son perfectamente seguras).5
Bloqueos totales: 23 intentos de generación descontrolada.
Hook 3: blog-quality-gate.sh (Stop)
El incidente: publiqué un artículo de blog con 7 frases en voz pasiva, una nota al pie referenciada en el texto pero ausente de la sección de referencias, y “was implemented by the team” como frase de apertura. El artículo lucía pulido en mi editor pero no superaba controles básicos de calidad que cualquier revisor humano captaría.
El hook: ejecuta mi linter de blog de 12 módulos sobre cualquier archivo de contenido de blog modificado. Revisa voz pasiva, notas al pie huérfanas, meta descripciones faltantes, bloques de código sin etiquetar e integridad de citas. Cada hallazgo es específico: “Línea 47: voz pasiva detectada en ‘was implemented by the team.’ Sugerencia: ‘the team implemented.’”
El paralelo con la retroalimentación humana: el hook critica el trabajo, no al operador. Dice “la línea 47 tiene voz pasiva”, no “escribes mal”. El mismo principio que hace constructiva la retroalimentación humana hace útil la retroalimentación automatizada.
El patrón detrás de 95 hooks
La arquitectura impulsada por configuración
Mis hooks evolucionaron de valores codificados a comportamiento impulsado por configuración:
~/.claude/configs/
├── recursion-limits.json # Depth, spawn budgets, timeouts
├── deliberation-config.json # Consensus thresholds per task type
├── consensus-profiles.json # security=85%, docs=50%
├── circuit-breaker.json # Failure mode configurations
└── file-scope-rules.json # Path-scoped hook application
Mover los umbrales a configuraciones JSON significó que podía ajustar el comportamiento sin editar scripts de bash. Cuando necesité consenso relacionado con seguridad al 85% pero documentación al 50%, el cambio de configuración tomó 30 segundos. Un cambio de código habría requerido editar, probar y volver a desplegar.6
El patrón de capas del ciclo de vida
Mis 95 hooks forman una red de seguridad con cuatro capas:
Capa 1: Prevención (PreToolUse): detener cosas malas antes de que sucedan. git-safety-guardian, credentials-check, recursion-guard.
Capa 2: Contexto (UserPromptSubmit, SessionStart): inyectar la información que el agente necesita. Fecha, rama, contexto del proyecto, entradas de memoria, principios de filosofía.
Capa 3: Validación (PostToolUse): verificar que las acciones completadas cumplan con los estándares. Verificación de consenso post-deliberación, registro de salida.
Capa 4: Calidad (Stop): controlar la salida final. Pride check, quality gate, reviewer stop gate. Esta capa implementa la monitorización metacognitiva donde el agente evalúa la calidad de su propio razonamiento, no solo su salida.
Cada capa es independiente. Si un hook de PreToolUse falla silenciosamente, el hook Stop aún captura problemas de calidad. Defensa en profundidad, aplicada al comportamiento del agente de IA.
Configuración
Los hooks viven en .claude/settings.json:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"command": "~/.claude/hooks/git-safety-guardian.sh",
"timeout": 5000
}
],
"PreToolUse:Task": [
{
"command": "~/.claude/hooks/recursion-guard.sh"
}
]
}
}
El campo matcher filtra qué herramientas activan el hook. PreToolUse:Task es una abreviación para un matcher que solo se dispara en invocaciones de la herramienta Task. Los hooks asíncronos (async: true) se ejecutan en segundo plano sin bloquear.7
Jerarquía de ámbito
- Nivel de usuario (
~/.claude/settings.json): se aplica a todos los proyectos. Mis 95 hooks viven aquí. - Nivel de proyecto (
.claude/settings.json): añade hooks específicos del proyecto. - Frontmatter de Skill/Subagente: limitado al ciclo de vida de un componente específico.8
Qué haría diferente
Empieza con 3 hooks, no 25. Mi primer mes produjo 25 hooks, muchos de los cuales añadían contexto que el agente ya tenía. La sobrecarga de cargar 25 hooks en cada llamada a herramienta era medible. Finalmente los reduje a los hooks que producían valor real (prevenían incidentes reales, capturaban problemas de calidad reales).
Impulsado por configuración desde el día uno. Pasé dos semanas refactorizando umbrales codificados en configuraciones JSON. Esa refactorización habría sido gratuita si hubiera empezado con configuraciones.
Infraestructura de pruebas temprano. Los primeros 20 hooks no tenían pruebas. Cuando añadí el arnés de pruebas (48 pruebas de integración bash), encontré 3 hooks que fallaban silenciosamente en casos extremos. Las pruebas deberían haber salido con el hook #1.
Puntos clave
Para desarrolladores que comienzan con hooks: - Empieza con tres hooks: seguridad de git (PreToolUse:Bash), inyección de contexto (UserPromptSubmit) y control de calidad (Stop); añade más solo cuando tengas incidentes que los justifiquen - Usa el marco de tiempo de decisión: la arquitectura de hooks es irreversible (95 hooks dependen de ella), así que invierte en el modelo del ciclo de vida antes de escribir hooks
Para equipos que estandarizan hooks: - Estandariza los hooks a nivel de usuario para que cada miembro del equipo obtenga las mismas barreras de seguridad - Rastrea las métricas de hooks (operaciones bloqueadas, incidentes interceptados) para justificar el costo de mantenimiento - Revisa los registros de hooks mensualmente para identificar nuevos patrones que valga la pena automatizar
Preguntas frecuentes
¿Qué son los hooks de Claude Code?
Los hooks son scripts de shell que se ejecutan en puntos específicos del ciclo de vida durante las sesiones de Claude Code. Se disparan deterministamente en momentos específicos: antes de que se ejecute una herramienta (PreToolUse), después de que se complete (PostToolUse), cuando envías un prompt (UserPromptSubmit), cuando comienza una sesión (SessionStart) y cuando Claude termina de responder (Stop). Los hooks reciben JSON por stdin con contexto completo (nombre de la herramienta, entrada, ID de sesión) y pueden bloquear operaciones, inyectar contexto o aplicar controles de calidad. Proporcionan garantías deterministas sobre un LLM probabilístico.
¿Cuántos hooks puede ejecutar Claude Code y hay un costo de rendimiento?
No hay un límite estricto en el número de hooks. Ejecuto 95 hooks en 6 de los 26 eventos del ciclo de vida disponibles (a partir de v2.1.116, abril de 2026), con aproximadamente 200ms de sobrecarga total por evento. El límite práctico es la latencia: cada hook añade tiempo de ejecución antes o después de las llamadas a herramientas. La optimización clave es usar dispatchers (uno por evento, que ejecutan hooks secuencialmente desde stdin cacheado) en lugar de hooks independientes que cada uno lee stdin por separado. Las escrituras concurrentes desde hooks independientes causaron corrupción de JSON en mi configuración inicial; los dispatchers eliminaron ese modo de fallo por completo.
¿En qué eventos se disparan los hooks de Claude Code?
Claude Code expone 26 eventos del ciclo de vida para hooks a partir de v2.1.116 (abril de 2026). Los seis más comunes a los que se dirigen mis hooks son: SessionStart (comienza una nueva sesión), SessionEnd (la sesión termina), PreToolUse (antes de que se ejecute cualquier herramienta), PostToolUse (después de que la herramienta se complete), UserPromptSubmit (el usuario envía un prompt) y Stop (Claude termina de responder). Otros incluyen SubagentStart, PermissionRequest, PermissionDenied, TaskCreated, CwdChanged, FileChanged, PreCompact y más. PreToolUse y PostToolUse pueden acotarse aún más con matchers como PreToolUse:Bash o PreToolUse:Task para dispararse solo en tipos específicos de herramientas. Cada evento recibe JSON por stdin con el contexto relevante.
¿Los hooks de Claude Code pueden bloquear llamadas a herramientas?
Sí. Un hook que produce {"decision": "block", "reason": "explanation"} por stdout impide que la llamada a la herramienta se ejecute. Este es el mecanismo central para los hooks de seguridad: mi git-safety-guardian bloquea los force pushes a main, mi credentials-check bloquea lecturas de archivos .env y mi recursion-guard bloquea la generación excesiva de agentes. El bloqueo es determinista y no puede eludirse mediante prompting. Los hooks que salen silenciosamente con código 0 permiten que la operación proceda.
¿Cuál es la diferencia entre los hooks PreToolUse y PostToolUse?
PreToolUse se dispara antes de que se ejecute una herramienta y puede bloquear la operación por completo. Úsalo para puertas de seguridad: bloquear comandos peligrosos, verificar credenciales, aplicar presupuestos de generación. PostToolUse se dispara después de que una herramienta se complete y puede proporcionar retroalimentación o desencadenar acciones de seguimiento. Úsalo para validación: verificar la calidad de la salida, registrar actividad, verificar consenso. Juntos forman la primera y tercera capas de una red de seguridad de cuatro capas: prevención (PreToolUse), inyección de contexto (UserPromptSubmit), validación (PostToolUse) y control de calidad (Stop).
Referencias
-
Infraestructura de hooks del autor. 95 hooks en 6 de los 26 eventos del ciclo de vida disponibles (v2.1.116, abril de 2026), desarrollados durante 9 meses (2025-2026). ↩
-
Anthropic, “Claude Code Hooks Reference,” 2026. 26 tipos de eventos del ciclo de vida a partir de v2.1.116 (abril de 2026). ↩
-
Anthropic, “Claude Code Documentation,” 2025. Formato JSON de entrada/salida de hooks. ↩
-
git-safety-guardian.sh del autor. 8 intentos de force-push interceptados rastreados en
~/.claude/state/. ↩ -
recursion-guard.sh del autor. Modelo de herencia de presupuesto documentado en
~/.claude/configs/recursion-limits.json. ↩ -
Arquitectura impulsada por configuración del autor. 14 archivos de configuración JSON que codifican todos los umbrales y reglas de los hooks. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Configuración de hooks y ejecución asíncrona. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Jerarquía de ámbito de hooks. ↩