El martes pasado casi empujo una API key a un repositorio público. Un Claude Code hook lo atrapó medio segundo antes del commit. Ese fue el momento en que pasé de "los hooks son algo opcional" a "no voy a correr un agente sin ellos".
Los Claude Code hooks son la función más infravalorada de toda la herramienta, y casi nadie en mi timeline los usa. Voy a cambiar eso. Te mostraré los hooks exactos que ejecuto trabajando solo, la config que los impulsa, y el que genuinamente salvó mi repositorio.
Qué es un hook en realidad
Un hook es un comando de shell que Claude Code ejecuta automáticamente cuando algo ocurre. Antes de que una herramienta corra. Después de que se edite un archivo. Cuando el agente termina. No le estás pidiendo a Claude que recuerde hacer algo — el harness lo hace, de forma determinista, siempre. Esa distinción importa. Un agente que "normalmente recuerda" ejecutar el formateador es un agente que olvidará en el peor momento posible.
Los hooks viven en tu settings.json. Así es su estructura:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "prettier --write \"$CLAUDE_FILE_PATHS\"" }
]
}
]
}
}
Ese ejecuta Prettier sobre cualquier archivo que Claude edite, en el instante en que lo edita. Ya nunca pienso en el formato. El diff siempre está limpio. No más "ups, 400 cambios de espacios en blanco" contaminando mis commits.
El hook que salvó mi repositorio
Este es el de la API key. Un hook PreToolUse sobre Bash que escanea en busca de git commit o git push y hace grep al diff staged buscando cualquier cosa que parezca un secreto.
{
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "~/.claude/scripts/secret-scan.sh" }
]
}
]
}
El script hace grep a los cambios staged buscando patrones como sk-, AKIA, y algunos prefijos de mis propios proveedores. Si hay coincidencia, el script sale con código no cero, y una salida no cero de un hook PreToolUse bloquea la acción. Claude literalmente no puede ejecutar el comando. Se le informa el motivo y lo esquiva.
El martes pasado lo detectó. Había dejado que un agente refactorizara una config y terminó incluyendo en línea una key de un env file que no debería haber leído. Sin el hook, esa key está en GitHub, estoy rotando credenciales a medianoche, y quizás alguien está minando crypto a mi costa. Con el hook, recibí un mensaje de bloqueo de una línea y seguí adelante. Doce líneas de bash. El seguro más barato que tengo.
Mis hooks del día a día
Más allá de los dos anteriores, esto es lo que realmente gana su lugar.
Un hook PostToolUse que ejecuta mi verificador de tipos después de ediciones a archivos .ts y devuelve los errores directamente al agente. Claude ve sus propios errores de tipos sin que yo los pegue. Se autocorrige en el mismo turno. Solo ese bucle de retroalimentación ya vale la configuración. El agente corrige el error antes de que yo siquiera lo lea.
Un hook Stop que dispara una notificación de escritorio cuando Claude termina una tarea larga. Ejecuto agentes en grandes refactorizaciones y me voy a hacer el almuerzo. La notificación significa que no estoy vigilando una terminal, mirándola cada noventa segundos como un tamagotchi.
{
"Stop": [
{ "hooks": [{ "type": "command", "command": "osascript -e 'display notification \"Claude is done\" with title \"CC\"'" }] }
]
}
Pequeño. Cambia completamente cómo trabajo. Lanzo una tarea y me voy con verdadera confianza de que voy a recibir un aviso.
El que eliminé
Probé un hook que ejecutaba mi suite de tests completa después de cada edición. Sonaba responsable. Era miserable. La suite toma 40 segundos, así que cada edición pequeña se detenía 40 segundos, y el agente se quedaba ahí inactivo quemando mi tarde. Lección: los hooks deben ser rápidos o asíncronos. Un hook síncrono lento convierte tu agente ágil en melaza.
Ahora los tests corren solo en un hook Stop, una vez, al final. Los chequeos rápidos (lint, tipos, escaneo de secretos) corren en línea. Los chequeos lentos corren en el límite. Ajusta el costo del hook a la frecuencia con que se dispara. Si se dispara en cada edición, más vale que retorne en menos de un segundo.
Dónde ponerlos
Los hooks específicos de proyecto van en .claude/settings.json en el repositorio para que se incluyan con el código y mis colaboradores también los obtengan. Los personales, como la notificación de escritorio, van en ~/.claude/settings.json para que me sigan en todos los proyectos. Mantén el escáner de secretos global. Lo quieres en todos lados, sin excepciones, sin "lo agregaré después".
Un gotcha que me costó una hora: los comandos de hooks corren en un shell no interactivo. Si tu comando depende de algo de tu .zshrc, no estará ahí. Lo aprendí cuando prettier funcionaba en mi terminal y silenciosamente no hacía nada en el hook porque el PATH era diferente. Usa rutas absolutas o npx.
Empieza con uno
No construyas todos estos hoy. Agrega el escáner de secretos. Solo ese. Son doce líneas y es la diferencia entre un martes tranquilo y un simulacro de incendio con rotación de credenciales.
Luego, cuando confíes en él, agrega el formateador. Luego la notificación. En una semana tendrás un agente que formatea limpio, bloquea secretos, corrige sus propios errores de tipos, y te da un toque en el hombro cuando termina — y habrás dejado de hacer esas cuatro cosas a mano.
Esa es la hora al día. Nunca fue una tarea grande. Eran cuarenta pequeñas, y ahora un script de shell las hace mientras me sirvo el café.
