Hooks de Claude Code — ejecutar comandos de shell automáticamente en eventos de sesión y herramientas
Los hooks en settings.json insertan comandos de shell al inicio de sesión, antes y después del uso de herramientas, y al detenerse. Cinco ejemplos: bloqueo de seguridad, autoformato, logging, alertas externas.
Los hooks de Claude Code — definidos bajo la clave hooks en settings.json — permiten ejecutar comandos de shell automáticamente al inicio de la sesión, antes y después de llamadas a herramientas, y al finalizar la sesión. Intercepta lo que el agente de IA está a punto de hacer, corrígelo después, limpia al final. Un potente punto de extensión.
Esta guía está orientada a Claude Code 1.x / la especificación oficial de hooks. Se basa en la configuración de Claude Code con una capa adicional de automatización y seguridad.
TL;DR
| Evento hook | Se dispara | Uso típico |
|---|---|---|
SessionStart | Justo después de que comienza la sesión | Aviso de sincronización de memoria, validación de entorno |
UserPromptSubmit | Justo después de que el usuario envía un prompt | Reconocimiento de slash commands, logging, posprocesado |
PreToolUse | Justo antes de una llamada a herramienta | Bloquear comandos peligrosos, proteger archivos secretos |
PostToolUse | Justo después de una llamada a herramienta | Autoformato (Prettier), lint, logging de efectos secundarios |
Stop | Al finalizar la sesión | Resumen de cambios, copia de seguridad, notificación |
Reglas clave:
- Código de salida 2 bloquea la llamada a la herramienta (PreToolUse).
- El JSON llega al stdin del hook: parsearlo con
jq. - Los hooks son comandos de shell: tener en cuenta las diferencias de SO (macOS/Linux/Windows WSL).
Requisitos previos
- Claude Code 1.x instalado — Configuración de Claude Code
jqinstalado (brew install jq/apt install jq/winget install jqlang.jq)- Acceso de edición a
settings.json(normalmente~/.claude/settings.json)
Descargar la línea base
La línea base recomendada por devAlice: cinco hooks + lista de denegación/permiso. Descargarla y personalizar.
settings.json# 1. Descargar
curl -fsSL https://devalice.jaceclub.com/assets/ai-agents/claude-code-hooks/settings.example.json -o settings.json
# 2. Verificar SHA-256
shasum -a 256 settings.json
# Esperado: 30c1d4117a935cac03b601c71125c11b71733c8c59517ab129fd8913cfb8a69e
# 3. Inspeccionar (especialmente los hooks PreToolUse)
less settings.json
# 4. Copiar a su ubicación (macOS/Linux)
mkdir -p ~/.claude
cp settings.json ~/.claude/settings.json⚠️ Advertencia: los hooks ejecutan comandos de shell arbitrarios. Inspecciona siempre los hooks de otros antes de aplicarlos. Este ejemplo es una línea base segura de cinco hooks, pero revísalo y adáptalo a tu entorno.
1. Estructura de hooks (5 min)
La clave hooks en ~/.claude/settings.json:
{
"hooks": {
"NombreDelEvento": [
{
"matcher": "regex o nombre de herramienta", // opcional
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'comando de shell real'"
}
]
}
]
}
}Reglas de coincidencia:
- Sin
matcher: se dispara en cada evento matcher= nombre de herramienta (Bash,Write,Edit,Read…): solo esa herramientamatcher= regex (^/(?:ej|euijinpro)\\b): paraUserPromptSubmit, coincide con el texto del prompt
JSON que llega al stdin:
{
"session_id": "...",
"transcript_path": "/ruta/a/transcript.jsonl",
"cwd": "/Users/...",
"tool_name": "Bash",
"tool_input": { "command": "..." },
"tool_response": { /* Solo PostToolUse */ }
}Extraer con jq -r .tool_input.command.
2. SessionStart — Cargar contexto al inicio
El hook más simple. Imprimir una línea cuando comienza la sesión:
"SessionStart": [
{
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'printf \"[inicio de sesión] %s\\nmemory sync: latest (skip)\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
}
]
}
]El stdout se expone como mensaje de sistema. Úsalo para:
- Activar la carga automática de archivos de memoria
- Inyectar contexto específico del proyecto
- Mostrar el estado de git al comienzo de la sesión
# Ejemplo más rico
command='/bin/bash -lc "echo [inicio de sesión]; git -C \"$PWD\" status -s; git -C \"$PWD\" log --oneline -3"'3. PreToolUse — Bloquear comandos peligrosos (el hook de mayor valor)
exit 2 bloquea. Aunque el agente de IA intente un comando de shell peligroso, lo detienes.
3.1 Bloquear patrones peligrosos
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'cmd=$(jq -r .tool_input.command); case \"$cmd\" in *\"rm -rf /\"*|*\"sudo rm\"*|*\":(){\"*) echo \"comando peligroso bloqueado: $cmd\" >&2; exit 2;; esac'"
}
]
}
]Patrones bloqueados:
rm -rf /: destrucción del sistemasudo rm: eliminación amplia (permitir vía lista de permitidos si lo necesitas):(){: fork bomb (:(){ :|: & };:)
3.2 Bloquear ediciones de archivos secretos
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in */.env|*/credentials.json|*/.aws/credentials) echo \"edición de archivo secreto bloqueada: $path\" >&2; exit 2;; esac'"
}
]
}Previene ediciones accidentales a .env, credentials.json, ~/.aws/credentials, etc.
3.3 Proteger directorios específicos
command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); case \"\$path\" in /etc/*|/System/*) echo \"ruta de sistema bloqueada: \$path\" >&2; exit 2;; esac"'4. PostToolUse — Autoformato, lint y registro
Limpieza después de la llamada a la herramienta.
4.1 Autoformato con Prettier
"PostToolUse": [
{
"matcher": "Edit|Write|MultiEdit",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in *.ts|*.tsx|*.js|*.jsx) prettier --write \"$path\" 2>/dev/null || true ;; esac'"
}
]
}
]Después de un Write/Edit del agente de IA, Prettier limpia automáticamente. Añade ~0,5 s por edición.
4.2 Registrar archivos modificados
command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); echo \"[cambiado] \$(date +%T) \$path\" >> ~/.claude/change.log"'Registro ordenado por tiempo de cada cambio de archivo en la sesión.
4.3 Type-check y lint (mover trabajo lento al hook Stop)
tsc --noEmit en cada edición es costoso. Mejor colocarlo en el hook Stop (§6).
5. UserPromptSubmit — Detección de slash commands y posprocesado
"UserPromptSubmit": [
{
"matcher": "^/(?:ej|euijinpro)\\b",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'echo \"[ej skill] prompt marcado para logging en euijin.pro\" >&2'"
}
]
}
]- Detectar slash commands y activar sistemas externos (Slack, logging separado)
- Etiquetar automáticamente palabras clave específicas (
urgente,bug) - Verificar el umbral del presupuesto de tokens
6. Stop — Limpieza al final de sesión
"Stop": [
{
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'printf \"\\n[fin de sesión] %s\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
}
]
}
]Usos:
- Resumen del estado de git de los archivos modificados
- Copia de seguridad (
rsyncogit stash) - Notificación externa al cerrar la sesión
- Una ronda de type-check y build
command='/bin/bash -lc "if git diff --quiet; then echo \"[limpio]\"; else echo \"[cambios]\"; git diff --stat; fi"'7. Permisos: deny/allow
Separado de los hooks, pero la clave de seguridad más importante de settings.json.
"permissions": {
"deny": [
"Bash(rm -rf /:*)",
"Bash(sudo rm:*)",
"Read(.env)",
"Read(.env.local)",
"Read(.aws/credentials)",
"Read(*/credentials.json)"
],
"allow": [
"Bash(git status:*)",
"Bash(git log:*)",
"Bash(git diff:*)",
"Bash(pnpm:*)",
"Bash(npm:*)",
"Bash(node:*)"
]
}denygana incondicionalmente: complementa los bloqueos de hooksallowaprueba automáticamente sin confirmación del usuario (comandos seguros de uso frecuente)- Todo lo demás pregunta al usuario cada vez
Los hooks de esta guía se solapan con deny: defensa en profundidad.
8. Ubicaciones y prioridad de settings.json
| Ubicación | Ámbito | Notas |
|---|---|---|
~/.claude/settings.json | Global de usuario | Todos los proyectos |
<proyecto>/.claude/settings.json | Proyecto | Se combina con el usuario al entrar al proyecto |
<proyecto>/.claude/settings.local.json | Proyecto + local de usuario | Se recomienda añadir a .gitignore |
Orden de combinación: usuario → proyecto → proyecto.local. El posterior sobreescribe al anterior.
Los hooks se acumulan en lugar de fusionarse: 5 hooks de usuario + 3 hooks de proyecto = los 8 se ejecutan.
9. Depuración: cuando el hook no se dispara
9.1 Inspeccionar el payload de stdin
Hook de depuración temporal:
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "/bin/bash -lc 'cat > /tmp/claude-hook-debug.json'"
}
]
}→ cat /tmp/claude-hook-debug.json | jq .
9.2 Probar códigos de salida manualmente
command='/bin/bash -lc "echo dbg >&2; exit 2"'→ Verificar que el bloqueo en PreToolUse funciona.
9.3 Confirmar que el hook se dispara
command='/bin/bash -lc "echo \"[hook disparado $$ $(date +%T)]\" >> ~/.claude/hook-trace.log"'Verificar el log después de una sesión.
9.4 Errores de sintaxis JSON
settings.json es implacable: las comas finales y los escapes omitidos son errores frecuentes. Validar con jq . ~/.claude/settings.json.
10. Solución de problemas
"command not found: jq"
brew install jq (macOS) / sudo apt install jq (Linux) / winget install jqlang.jq (Windows).
El hook se dispara pero no hace nada
commanddebe ser una sola línea. Para varias líneas, usar\\no llamar a un script separado.- Revisar los escapes de shell:
\"requiere doble escape dentro de JSON.
PreToolUse: exit 2 ignorado
- Confirmar que
exit 2realmente es la última instrucción del comando de shell. - Un entorno con
set -epuede hacer quecase ;;salga con 0: usarexit 2directamente dentro de la rama coincidente.
Prettier no se ejecuta
- Confirmar que Prettier está instalado en la raíz del proyecto (
pnpm prettier --version). - El PATH del hook puede no incluir
node_modules/.bin: usarnpx prettiero una ruta absoluta.
Los cambios en settings.json no se aplican
- Se requiere reiniciar la sesión. O
/config reloadsi está soportado. - Podría estar siendo ignorado silenciosamente por un error de sintaxis JSON. Validar con
jq ..
Pasos siguientes
- Configuración de Claude Code — requisito previo
- Agent runner — ejecución autónoma + combinación de hooks
- Servidores MCP — diferencias de rol respecto a hooks
- Flujo de trabajo multi-agente — integración de subagentes
Referencias
Registro de cambios
- 2026-05-12 — Borrador inicial (devAlice M2 seed expansion)