Claude Code Hooks: 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 oraciones en voz pasiva y una nota al pie colgante.1
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 (bloquear comandos git peligrosos, inyectar contexto, imponer calidad) sobre un sistema probabilístico (el LLM). Después de construir 95 hooks a lo largo de mi infraestructura, he descubierto 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 he aprendido en 9 meses de desarrollo de hooks.
La arquitectura
Claude Code expone eventos del ciclo de vida donde los hooks pueden interceptar, modificar o bloquear comportamiento:2
Eventos de sesión
| Evento | Cuándo se dispara | Mis hooks |
|---|---|---|
| SessionStart | Comienza una nueva sesión | session-start.sh — inyecta fecha, valida venv, inicializa estado de recursión |
| SessionEnd | La sesión termina | Limpieza de archivos temporales |
Eventos de ejecución de herramientas
| Evento | Cuándo se dispara | Mis hooks |
|---|---|---|
| PreToolUse | Antes de que cualquier herramienta se ejecute | git-safety-guardian.sh, recursion-guard.sh, credentials-check.sh |
| PostToolUse | Después de que la herramienta se completa | 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, información del modelo) |
| Stop | Claude termina de responder | deliberation-pride-check.sh, reviewer-stop-gate.sh |
Cada hook recibe JSON en stdin y se comunica a través de stdout:
{"decision": "block", "reason": "Force push to main is prohibited"}
O permite silenciosamente al salir 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é los datos desde un respaldo local, pero el proceso de recuperación de 4 horas me convenció de que el juicio probabilístico nunca debería 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 coincidencia de patrones sobre la cadena del comando. Simple, determinista, imposible de eludir mediante prompts ingeniosos. El agente aún puede hacer force-push a ramas de funciones (a veces legítimo), pero main/master están permanentemente protegidas.4
Intercepciones en su vida útil: 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, sin límites de generación, creó sus propios subagentes. La recursión consumió tokens de API a 10 veces la tasa normal. Detuve la sesión manualmente después de 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 en cualquier forma de árbol. Los límites basados en profundidad son demasiado rígidos (impiden cadenas profundas pero angostas que son perfectamente seguras).5
Bloqueos en su vida útil: 23 intentos de generación descontrolada.
Hook 3: blog-quality-gate.sh (Stop)
El incidente: Publiqué un artículo de blog con 7 oraciones en voz pasiva, una nota al pie referenciada en el texto pero ausente de la sección de referencias, y “fue implementado por el equipo” como línea de apertura. El artículo se veía pulido en mi editor, pero falló verificaciones básicas de calidad que cualquier revisor humano detectaría.
El hook: Ejecuta mi linter de blog de 12 módulos sobre cualquier archivo de contenido de blog modificado. Verifica voz pasiva, notas al pie colgantes, 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 ‘fue implementado por el equipo.’ Sugerencia: ‘el equipo implementó.’”
El paralelo con la retroalimentación humana: El hook critica el trabajo, no al operador. Dice “la línea 47 tiene voz pasiva,” no “usted escribe 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 basada en configuración
Mis hooks evolucionaron de valores codificados a comportamiento basado en 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 de seguridad al 85% pero documentación al 50%, el cambio de configuración tomó 30 segundos. Un cambio de código habría requerido edición, pruebas y redespliegue.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 las cosas malas antes de que ocurran. git-safety-guardian, credentials-check, recursion-guard.
Capa 2: Contexto (UserPromptSubmit, SessionStart) — Inyectar 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 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.
Cada capa es independiente. Si un hook PreToolUse falla silenciosamente, el hook Stop aún detecta problemas de calidad. Defensa en profundidad, aplicada al comportamiento de agentes de IA.
Configuración
Los hooks residen 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 abreviatura 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 alcance
- Nivel de usuario (
~/.claude/settings.json) — se aplica a todos los proyectos. Mis 95 hooks residen aquí. - Nivel de proyecto (
.claude/settings.json) — agrega hooks específicos del proyecto. - Frontmatter de skill/subagente — alcance limitado al ciclo de vida de un componente específico.8
Qué haría diferente
Comenzar con 3 hooks, no 25. Mi primer mes produjo 25 hooks, muchos de los cuales agregaban contexto que el agente ya tenía. La sobrecarga de cargar 25 hooks en cada llamada de herramienta era medible. Eventualmente podé hasta los hooks que producían valor real (prevenían incidentes reales, detectaban problemas de calidad reales).
Basado en configuración desde el primer día. Pasé dos semanas refactorizando umbrales codificados en configuraciones JSON. Esa refactorización habría sido gratuita si hubiera comenzado con configuraciones.
Infraestructura de pruebas temprano. Los primeros 20 hooks no tenían pruebas. Cuando agregué el arnés de pruebas (48 pruebas de integración en bash), encontré 3 hooks que fallaban silenciosamente en casos límite. Las pruebas deberían haber sido parte del hook #1.
Conclusiones clave
Para desarrolladores que comienzan con hooks: - Comience con tres hooks: seguridad de git (PreToolUse:Bash), inyección de contexto (UserPromptSubmit) y compuerta de calidad (Stop); agregue más solo cuando tenga incidentes que los justifiquen - Use el marco de temporalidad de decisiones: la arquitectura de hooks es irreversible (95 hooks dependen de ella), así que invierta en el modelo de ciclo de vida antes de escribir hooks
Para equipos que estandarizan hooks: - Estandarice los hooks a nivel de usuario para que cada miembro del equipo reciba las mismas barandas de seguridad - Registre métricas de hooks (operaciones bloqueadas, incidentes interceptados) para justificar el costo de mantenimiento - Revise los registros de hooks mensualmente para identificar nuevos patrones que valga la pena automatizar
Referencias
-
Infraestructura de hooks del autor. 95 hooks en 6 eventos del ciclo de vida, desarrollados durante 9 meses (2025-2026). ↩
-
Anthropic, “Claude Code Documentation,” 2025. Eventos del ciclo de vida de hooks. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Formato JSON de entrada/salida de hooks. ↩
-
git-safety-guardian.sh del autor. 8 intentos de force-push interceptados registrados en
~/.claude/state/. ↩ -
recursion-guard.sh del autor. Modelo de herencia de presupuesto documentado en
~/.claude/configs/recursion-limits.json. ↩ -
Arquitectura basada en configuración del autor. 14 archivos de configuración JSON que codifican todos los umbrales y reglas de hooks. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Configuración de hooks y ejecución asíncrona. ↩
-
Anthropic, “Claude Code Documentation,” 2025. Jerarquía de alcance de hooks. ↩