Le dices a Claude Code, en tu CLAUDE.md, que nunca toque la carpeta migrations/ y que siempre pase el formateador antes de terminar. Funciona nueve veces de diez. La décima, en una sesión larga con mucho contexto, la instrucción se diluye y el cambio llega sin formatear, o peor, con una migración editada a mano. El problema no es el modelo: es que una instrucción en lenguaje natural es una petición, no una garantía.
Los hooks resuelven justo eso. Son comandos que defines tú y que Claude Code ejecuta en momentos concretos de su trabajo, sin preguntarle al modelo si le apetece. Si una regla no admite excepciones, no es una instrucción: es un hook.
Qué es un hook y cuándo se dispara
Un hook es un comando de shell asociado a un evento del ciclo de trabajo del agente. Cuando el evento ocurre, Claude Code ejecuta tu comando y le pasa por la entrada estándar un JSON con el contexto: qué herramienta se va a usar, con qué parámetros, en qué carpeta, etc. Los eventos que más se usan:
- PreToolUse: justo antes de que Claude use una herramienta (ejecutar un comando, editar un archivo…). Puede bloquear la acción.
- PostToolUse: justo después de que la herramienta termine bien. Ideal para formatear, ejecutar linters o tests rápidos.
- UserPromptSubmit: cuando envías un prompt, antes de que Claude lo procese. Sirve para añadir contexto o validar.
- Stop: cuando Claude termina de responder. Útil para notificaciones o comprobaciones finales.
- SessionStart: al abrir o reanudar una sesión, para cargar contexto del proyecto.
La lista completa y los detalles de cada evento cambian con las versiones, así que conviene consultar la referencia oficial cuando vayas a configurar algo concreto.
Dónde se configuran
Los hooks viven en los archivos de configuración de Claude Code, bajo la clave hooks:
~/.claude/settings.json: tus hooks personales, en todos los proyectos..claude/settings.json: los del proyecto, que se versionan y comparte todo el equipo..claude/settings.local.json: los tuyos para ese proyecto, sin subirlos al repositorio.
La estructura básica es siempre la misma: evento → lista de bloques con un matcher (a qué herramienta se aplica) → lista de comandos.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "npx prettier --write \"$(jq -r '.tool_input.file_path')\"" }
]
}
]
}
}
Este primer ejemplo formatea con Prettier cada archivo que Claude edita o crea. El comando recibe el JSON por la entrada estándar y jq extrae la ruta del archivo. Así el formato queda siempre limpio, sin gastar una sola instrucción en pedírselo al modelo.
Ejemplo 1: bloquear comandos peligrosos
El caso estrella del evento PreToolUse. El hook recibe el comando que Claude quiere ejecutar y decide si lo deja pasar. La convención es sencilla: código de salida 0 deja continuar; código 2 bloquea la acción y el mensaje que escribas por la salida de error se devuelve a Claude, que lo lee y busca otra vía.
#!/usr/bin/env bash
# .claude/hooks/bloquear-peligrosos.sh
cmd=$(jq -r '.tool_input.command // empty')
if echo "$cmd" | grep -Eq 'rm -rf (/|~|\$HOME)|git push (-f|--force)|DROP TABLE'; then
echo "Bloqueado por política del proyecto: comando destructivo. Propón una alternativa más segura." >&2
exit 2
fi
exit 0
Y su registro en .claude/settings.json, limitado a la herramienta de shell:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/bloquear-peligrosos.sh" }
]
}
]
}
}
Fíjate en el mensaje de error: está escrito para Claude, no para ti. Un buen mensaje de bloqueo dice qué regla se ha incumplido y qué hacer en su lugar. Con eso, el agente rectifica en el mismo turno en vez de quedarse atascado o reintentar lo mismo.
Ejemplo 2: proteger archivos que no deben tocarse
Misma idea, aplicada a las herramientas de edición. Este hook impide modificar migraciones ya aplicadas y archivos de entorno:
#!/usr/bin/env bash
# .claude/hooks/proteger-archivos.sh
ruta=$(jq -r '.tool_input.file_path // empty')
case "$ruta" in
*/migrations/*|*.env|*.env.*)
echo "Archivo protegido ($ruta). Las migraciones se crean nuevas, nunca se editan; los .env los gestiona una persona." >&2
exit 2
;;
esac
exit 0
Se registra con "matcher": "Edit|Write" en PreToolUse. La regla que antes vivía como una frase en CLAUDE.md pasa a ser imposible de incumplir.
Ejemplo 3: avisarte cuando Claude termina
Con tareas largas, nadie quiere mirar la terminal. Un hook en Stop te avisa cuando el agente ha acabado. En macOS, por ejemplo:
{
"hooks": {
"Stop": [
{
"hooks": [
{ "type": "command", "command": "osascript -e 'display notification \"Claude ha terminado\" with title \"Claude Code\"'" }
]
}
]
}
}
Es el hook más pequeño y probablemente el que más se agradece: lanzas la tarea, te pones con otra cosa y llega el aviso.
Formatear al editar (PostToolUse): una tarea mecánica que no quieres pedir cada vez.
Bloquear comandos y archivos (PreToolUse): una barrera que evita el daño antes de que ocurra.
Notificar al terminar (Stop): una comodidad que te devuelve tiempo.
La mayoría de los equipos empieza por uno de estos tres y va añadiendo según aparecen los sustos.
Cómo crear tu primer hook, paso a paso
- Elige una regla que hayas tenido que repetirle a Claude más de dos veces. Esa es la candidata ideal.
- Decide el evento: ¿hay que impedir algo antes (PreToolUse), reaccionar después (PostToolUse) o avisar al final (Stop)?
- Escribe el comando como un script aparte en `.claude/hooks/` y pruébalo a mano, pasándole un JSON de ejemplo por la entrada estándar.
- Regístralo en `.claude/settings.local.json` con el `matcher` más estrecho posible (solo `Bash`, solo `Edit|Write`…).
- Observa una sesión real y revisa que se dispara cuando debe y que el mensaje de bloqueo ayuda a Claude a rectificar.
- Cuando funcione, súbelo a `.claude/settings.json` para que lo use todo el equipo y documenta en el README qué hace cada hook.
Los fallos más habituales
- El
matcheres demasiado amplio. Un hook que se ejecuta con cada herramienta ralentiza todo. Limítalo a lo que necesitas. - Hooks lentos. Se ejecutan en el camino crítico: si tardan segundos, cada acción de Claude los espera. Deja los tests largos para cuando termine, no para cada edición.
- Mensajes de bloqueo crípticos. Si solo dices «prohibido», Claude insistirá o se quedará bloqueado. Explica la regla y la alternativa.
- Scripts sin permiso de ejecución. Un
chmod +xolvidado produce errores que parecen un fallo del hook y son del sistema. - Dependencias que no existen en otra máquina. Si tu hook usa
jqoprettier, el resto del equipo también los necesita. Anótalo. - Olvidar que se ejecutan con tus permisos. Un hook es código que corre automáticamente en tu máquina. Trátalo como cualquier script que se ejecuta solo.
Pídele a Claude que te ayude a diseñar los tuyos
Nadie conoce tus sustos mejor que tú, pero Claude puede ayudarte a convertirlos en hooks bien hechos. Un buen punto de partida:
Conclusión
Los hooks no hacen a Claude más listo; hacen tu flujo de trabajo más fiable. Convierten las reglas que antes dependían de la memoria del modelo en comprobaciones que se ejecutan siempre. Empieza pequeño: un hook de formato, uno de protección de archivos y un aviso al terminar. Lo que ganas no es velocidad, sino algo más valioso: dejar de preocuparte por lo que el agente podría olvidar.
Si quieres seguir profundizando en cómo estructurar el trabajo con Claude, te recomendamos cómo elegir entre skills, MCP y subagentes y cómo crear tu primera skill.