Hooks en Claude Code: automatiza y aplica reglas

Los hooks de Claude Code son comandos de shell que se ejecutan automáticamente en puntos concretos de su ciclo de vida. Su gran diferencia con el CLAUDE.md es que el hook siempre se ejecuta: no depende de que el modelo decida hacerlo. Por eso son la herramienta para forzar reglas (formatear, bloquear, validar), no solo sugerirlas.
En este artículo verás qué son, qué eventos existen y cómo configurarlos, con ejemplos aplicados a un proyecto Laravel (formatear con Pint, proteger tu .env).
Por qué un hook y no una instrucción
El CLAUDE.md es guía: Claude lo lee y trata de seguirlo, pero no hay garantía. Un hook es determinista: corre como un comando del sistema en el evento que definas, decida lo que decida el modelo. Si algo tiene que pasar (formatear tras cada edición, no tocar ciertos archivos), va en un hook.
Los eventos principales
Claude Code expone muchos eventos de ciclo de vida (más de treinta). Estos son los que más vas a usar:
PreToolUse: antes de que Claude use una herramienta (puedes bloquear la acción).PostToolUse: después de usar una herramienta (ideal para formatear o lintar tras editar).UserPromptSubmit: cuando envías un prompt.Notification: cuando Claude espera tu input o permiso.SessionStart: al iniciar la sesión (inyectar contexto).Stop/SubagentStop: al terminar la conversación o un subagente.
Cómo se configuran
Los hooks viven en un archivo de settings: ~/.claude/settings.json (global), .claude/settings.json (del proyecto) o .claude/settings.local.json (local, sin commitear). La estructura es: evento → lista de entradas con un matcher (el patrón que decide qué herramienta dispara el hook: una cadena exacta, una lista separada por |, o una expresión regular) y los hooks a ejecutar.
Ejemplo genérico: formatear con Prettier cada vez que Claude escribe o edita un archivo. El matcher Write|Edit calza con ambas herramientas.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "npx prettier --write \"$(jq -r '.tool_input.file_path')\"" }
]
}
]
}
}El hook recibe la información del evento como JSON por stdin: la ruta del archivo recién editado viene en tool_input.file_path, que extraes con jq. Por eso el comando de arriba lee esa ruta con jq -r '.tool_input.file_path' y se la pasa a Prettier. (No hay una variable de entorno con las rutas; en un hook de tipo command el dato siempre llega por stdin.)
Hooks para un proyecto Laravel
Aquí es donde los hooks se vuelven tu control de calidad automático. Tres ejemplos concretos.
1. Formatear con Pint tras cada edición. En vez de recordarle a Claude que respete el estilo, lo fuerzas: cada Write|Edit pasa por Pint.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "./vendor/bin/pint \"$(jq -r '.tool_input.file_path')\"" }
]
}
]
}
}2. Proteger archivos sensibles. Un hook en PreToolUse puede denegar una edición antes de que ocurra. Aquí bloqueamos cualquier cambio a tu .env o a las migraciones ya versionadas. Apunta el hook a un script:
{
"hooks": {
"PreToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": ".claude/hooks/proteger-laravel.sh" }
]
}
]
}
}Y el script lee la ruta del archivo y decide. Para bloquear, imprime un JSON con permissionDecision: "deny":
#!/usr/bin/env bash
ruta=$(jq -r '.tool_input.file_path')
if [[ "$ruta" == *".env"* || "$ruta" == *"/database/migrations/"* ]]; then
echo '{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"deny","permissionDecisionReason":"Archivo protegido: edítalo tú a mano."}}'
fi3. No cerrar una tarea con los tests en rojo. Un hook en Stop puede correr php artisan test (o PHPStan/Larastan con ./vendor/bin/phpstan) al terminar, para que el agente no dé por hecho un trabajo que rompe la suite.
Puedes abrir /hooks para ver los eventos y los hooks configurados (es de solo lectura: para editarlos, tocas el JSON o le pides a Claude que lo haga). Este flujo encaja con el resto del cluster de IA para desarrolladores Laravel y con usar Claude Code en un proyecto Laravel real.
Hook, CLAUDE.md, skill o subagente
- Hook: enforcement determinista en un evento fijo.
- CLAUDE.md: guía persistente que el modelo intenta seguir.
- Skill: procedimiento que se carga bajo demanda.
- Subagente: tarea en un contexto aislado.
Para ver cómo se combinan todos, revisa la guía de Claude Code.
Preguntas frecuentes
¿Qué son los hooks en Claude Code?
Comandos de shell que Claude Code ejecuta automáticamente en eventos de su ciclo de vida (antes/después de usar una herramienta, al notificar, al iniciar sesión, etc.) para automatizar tareas y aplicar reglas de forma determinista.
¿Dónde se configuran los hooks?
En un archivo de settings (~/.claude/settings.json, .claude/settings.json del proyecto o .claude/settings.local.json), dentro de un bloque hooks, agrupados por evento, con un matcher y los comandos a ejecutar.
¿Un hook puede impedir una acción de Claude?
Sí. Un hook en PreToolUse puede denegar el uso de una herramienta antes de que ocurra: imprime un JSON con permissionDecision: "deny" (o termina con código de salida 2). Es la forma de proteger archivos como tu .env.
¿Cómo formateo con Pint automáticamente?
Con un hook PostToolUse (matcher Write|Edit) que corra Pint sobre el archivo recién editado. La ruta llega en el JSON del evento por stdin, así que la extraes con jq: ./vendor/bin/pint "$(jq -r '.tool_input.file_path')".
¿Cuándo uso un hook en vez del CLAUDE.md?
Cuando algo debe ocurrir siempre en un momento exacto. El CLAUDE.md es guía (puede no cumplirse); el hook es enforcement.