Claude Code hooks: guardrails, logging y automatización para tus agentes
Hook PreToolUse para Bash: bloquea rm -rf y loguea todo
set -euo pipefail
Leer el JSON de entrada desde stdin
INPUT=$(cat)
Extraer el comando que Claude quiere ejecutar
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // ""')
Timestamp para el log
TIMESTAMP=$(date -u +"%Y-%m-%dT%H:%M:%SZ")
LOG_FILE="${CLAUDE_PROJECT_DIR:-$HOME}/.claude/bash-audit.log"
Loguear el comando (siempre, antes de cualquier decisión)
echo "[$TIMESTAMP] CMD: $COMMAND" >> "$LOG_FILE"
Patrones peligrosos que bloqueamos sin excepciones
BLOCKED_PATTERNS=(
"rm -rf /"
"rm -rf ~"
"rm -rf *"
"rm -rf ."
":(){ :|:& };:"
"dd if=/dev/zero"
"> /dev/sda"
"mkfs."
)
for PATTERN in "${BLOCKED_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qE "$PATTERN"; then
echo "[$TIMESTAMP] BLOCKED: $COMMAND" >> "$LOG_FILE"
echo "Comando bloqueado por hook de seguridad: patrón destructivo detectado ('$PATTERN')" >&2
exit 2
fi
done
Todo bien — salida silenciosa, flujo normal
exit 0
Ahora la configuración en `.claude/settings.json`:
```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/bash-guard.sh",
"timeout": 10
}
]
}
]
}
}
Dale permisos de ejecución al script:
chmod +x .claude/hooks/bash-guard.sh
A partir de aquí, cada vez que Claude intente ejecutar un comando Bash, el hook se dispara primero. Si detecta un patrón peligroso, Claude recibe el mensaje de error en stderr y no ejecuta nada. Si todo está limpio, el agente continúa sin ninguna interrupción visible.
El archivo bash-audit.log crece con cada comando ejecutado. En una sesión de trabajo normal con un agente activo, ese log te cuenta la historia completa de lo que hizo Claude — sin tener que scrollear el historial de conversación.
Añadir una notificación cuando el agente termina
Si lanzas tareas largas y quieres saber cuándo terminan sin estar mirando la pantalla, el hook Stop es lo que necesitas.
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "${CLAUDE_PROJECT_DIR}/.claude/hooks/notify-done.sh",
"timeout": 5
}
]
}
]
}
}
#!/bin/bash
# .claude/hooks/notify-done.sh
# Notificación de escritorio cuando Claude termina una tarea
# En macOS
if command -v osascript &> /dev/null; then
osascript -e 39;display notification "Claude ha terminado la tarea" with title "Claude Code"39;
fi
# En Linux con notify-send
if command -v notify-send &> /dev/null; then
notify-send "Claude Code" "El agente ha terminado la tarea"
fi
exit 0
El hook Stop no tiene matcher porque no hay herramientas que filtrar — aplica siempre que Claude decide parar. Si necesitas que Claude continúe trabajando hasta que se cumpla alguna condición (por ejemplo, todos los tests en verde), haz que el script devuelva exit 2 y escribe en stdout un JSON con {"hookSpecificOutput": {"additionalContext": "Los tests aún fallan. Corrígelos antes de terminar."}} para que Claude sepa qué debe hacer a continuación. El stderr en Stop hooks no interrumpe el flujo.
Cuándo usar hooks, cuándo CLAUDE.md y cuándo sub-agentes
Esta es la pregunta que más se repite cuando alguien empieza a añadir capas de control a sus agentes.
Usa CLAUDE.md para instrucciones de comportamiento en lenguaje natural: convenciones de código, qué herramientas preferir, cómo formatear los commits. Es lo primero que Claude lee. Es contexto, no control.
Usa hooks cuando necesitas una garantía técnica que no dependa de que Claude interprete bien una instrucción. Un rm -rf bloqueado por un hook es un rm -rf bloqueado, siempre, independientemente de cómo estaba redactado el prompt. Un rm -rf "prohibido" en CLAUDE.md es una sugerencia que Claude puede ignorar bajo presión de contexto.
Usa sub-agentes cuando necesitas razonamiento sobre una situación: revisar si el código generado cumple los requisitos de arquitectura, validar que una migración de base de datos es correcta antes de ejecutarla, resumir los resultados de diez herramientas en paralelo. Los sub-agentes piensan. Los hooks no necesitan pensar — esa es su ventaja.
La regla general: hooks para lo que debe ser determinista, sub-agentes para lo que requiere juicio.
Preguntas frecuentes
¿Los hooks se ejecutan con cada mensaje del usuario o solo cuando Claude usa herramientas?
Depende del tipo de hook. PreToolUse y PostToolUse solo se disparan cuando Claude invoca una herramienta — no con cada mensaje de texto. UserPromptSubmit se dispara con cada mensaje enviado, antes de que Claude lo procese. Stop se dispara cuando Claude decide terminar, no cuando el usuario escribe algo.
¿Puedo tener hooks diferentes para proyectos distintos?
Sí. Los hooks en .claude/settings.json (dentro del proyecto) solo aplican a ese proyecto. Los hooks en ~/.claude/settings.json aplican a todos tus proyectos. Si hay configuraciones en ambos archivos, se combinan. En caso de conflicto en el mismo evento, la configuración más específica (proyecto) tiene precedencia.
¿Un hook puede modificar lo que Claude va a hacer, no solo bloquearlo?
Sí, en PreToolUse. Puedes devolver por stdout un JSON con hookSpecificOutput.updatedInput para reemplazar los argumentos que Claude iba a usar. Por ejemplo, si Claude quiere ejecutar rm -rf build, puedes interceptarlo y devolver rm -rf build/ (con trailing slash) para que solo borre el contenido del directorio, no el directorio en sí. Esta capacidad es poderosa — úsala con cuidado.
¿Hay alguna forma de ver qué hooks están activos en mi sesión?
Sí. Escribe /hooks en el prompt de Claude Code y se abre una vista en el navegador con todos los hooks configurados, organizados por evento, con su matcher y tipo de handler. Es de solo lectura, pero es la forma más rápida de auditar qué está activo.
¿Los hooks se pueden desactivar sin borrarlos?
Sí. Añade "disableAllHooks": true en cualquiera de los archivos de settings. Solo los settings de usuario y proyecto pueden desactivar hooks definidos en esos mismos niveles — los hooks de configuración administrada (managed settings) requieren intervención del administrador.
¿Hay límite en cuántos hooks puedo configurar?
No hay un límite documentado en el número de hooks. Sí hay un timeout por hook (por defecto 600 segundos para comandos, 30 para prompts). Si un hook supera el timeout, se cancela como error no bloqueante (igual que un exit 1) — el flujo continúa pero el hook no tuvo efecto.
Lo que cambia cuando añades hooks a tu workflow
La primera semana que empecé a usar hooks en mis propios agentes, lo que más me sorprendió no fue la seguridad — fue la visibilidad.
El archivo de log de comandos Bash me reveló patrones que no había visto antes. Claude ejecutaba con frecuencia ciertos comandos que yo no esperaba. Algunos eran ineficientes. Uno de ellos era potencialmente problemático en un contexto de CI. Sin el log, nunca me habría enterado.
Los hooks no solo protegen tu sistema. Te dan información real sobre cómo trabaja el agente — y esa información es la que necesitas para mejorar tus prompts, tu CLAUDE.md y tu arquitectura de agentes con el tiempo.
Si estás construyendo algo serio con Claude Code — más de un agente, un workflow automatizado, código que toca producción —, los hooks no son opcionales. Son la diferencia entre un agente que funciona y uno en el que confías.
Si quieres ver cómo encajan los hooks dentro de un sistema de agentes más completo — con sub-agentes, routines y MCP — en el curso Construye con IA cubrimos el stack completo desde la idea hasta el producto, incluyendo cómo estructurar los guardrails de seguridad para workflows que corren sin supervisión constante.
Y si prefieres un entorno donde experimentar con otros developers que están construyendo lo mismo, en Dominicode Labs compartimos proyectos, configuraciones y workflows reales cada semana.
Bezael Pérez — Developer senior, fundador de Dominicode. Lleva 15+ años construyendo software y los últimos años construyendo con IA. Escribe sobre arquitectura de agentes, Angular moderno y cómo pasar de idea a producto sin caos.
