Hooks de Claude Code: la guía completa (2026)

Hooks de Claude Code: la guía completa (2026)

Una skill se puede ignorar. Eso no es un defecto de las skills, es todo el diseño: Claude lee la descripción, decide si la tarea actual encaja, y solo carga el cuerpo si cree que sí. La mayoría de las veces ese juicio es correcto. A veces no lo es, y la tarea donde más importa, la revisión de código justo antes de un merge, la entrada de log que debe existir pase lo que pase, es exactamente la tarea donde "probablemente" no es suficiente.

Los hooks son la otra mitad de Claude Code. Un hook es un comando de shell que el harness ejecuta cuando se dispara un evento específico, lo haya previsto o no alguna skill. Ningún juicio del modelo se interpone entre el evento y el comando. Se ejecuta, siempre, en orden, y su código de salida incluso puede detener a Claude en seco. Si alguna vez has querido decir "formatea siempre este archivo después de una edición" o "nunca dejes que Claude toque este directorio", un hook es la herramienta construida para esa frase.

Esta guía cubre qué son los hooks, el esquema de settings.json detrás de ellos, seis recetas que puedes pegar hoy mismo, cómo trabajan juntos hooks y skills, y los modos de fallo que se comen una tarde si no sabes que hay que buscarlos.

Qué es un hook en realidad

Claude Code dispara eventos con nombre durante una sesión: antes de que corra una herramienta, después de que corra una herramienta, cuando Claude termina de responder, cuando se mostraría una notificación. Un hook ata un comando de shell a uno de esos eventos, opcionalmente filtrado a herramientas específicas. El harness ejecuta tu comando, le pasa contexto como JSON por stdin, y luego lee el código de salida para decidir qué pasa después.

Los eventos que más usarás:

  • PreToolUse — se dispara antes de que se ejecute una llamada a herramienta. Un hook aquí puede bloquear la llamada por completo.
  • PostToolUse — se dispara después de que termina una llamada a herramienta. Bueno para formatear, testear o registrar lo que acaba de pasar.
  • Stop — se dispara cuando Claude termina su turno y está a punto de devolverte el control.
  • Notification — se dispara cuando Claude Code mostraría una notificación del sistema (peticiones de permiso, avisos de inactividad).
  • UserPromptSubmit — se dispara cuando envías un mensaje, antes de que Claude lo vea.

Eso es el mecanismo. La razón por la que importa es la garantía que te da, algo que una skill estructuralmente no puede.

El modelo mental: determinista vs discrecional

Esta es la idea que vale la pena recordar aunque no recuerdes nada más de esta guía.

Una skill es discrecional. Claude lee su descripción al principio de una sesión, y después decide, según tu petición, si cargarla y seguirla. Las buenas skills se activan de forma fiable, pero "fiable" sigue siendo una probabilidad, no una garantía. Claude puede malinterpretar un prompt ambiguo, o dos skills pueden tener descripciones solapadas que confundan la coincidencia, un modo de fallo que cubrimos con más profundidad en nuestra guía sobre por qué las skills no se activan.

Un hook es determinista. No le pregunta a Claude si debe correr. No lee una descripción ni juzga relevancia. El harness ve el evento, y el comando se ejecuta, punto. Si el evento es PostToolUse en la herramienta Edit, tu formateador se ejecuta después de cada edición, incluida la que Claude hizo mientras pensaba en otra cosa por completo.

Esa diferencia se traduce directamente en cuándo recurrir a cuál:

Skill Hook
Corre cuando Claude lo juzga relevante Cada vez que se dispara el evento
Se puede saltar Sí, por una mala coincidencia o contexto ocupado No
Mejor para Criterio, estructura, "cómo hacer bien X" Cumplimiento, "X siempre debe pasar"
Modo de fallo No activación silenciosa Código de salida malo silencioso, o bloqueando todo

Si la frase que estás imponiendo empieza con "Claude siempre debería..." o "Claude nunca debe...", quieres un hook. Si empieza con "cuando Claude esté haciendo X, debería abordarlo como..." quieres una skill. Formatear código después de cada edición es un hook; escribir Python idiomático es una skill. Bloquear commits a main es un hook; estructurar un buen mensaje de commit es una skill.

Anatomía de un hook en settings.json

Los hooks viven bajo la clave hooks en .claude/settings.json (a nivel de proyecto) o ~/.claude/settings.json (a nivel de usuario), un archivo distinto de CLAUDE.md y que vale la pena no confundir: CLAUDE.md es prosa que Claude lee, settings.json es configuración que el harness ejecuta. Si todavía no has configurado ninguno de los dos, nuestra guía de CLAUDE.md y nuestro recorrido completo de configuración cubren el resto de la pila en la que vive este archivo. Aquí va un ejemplo mínimo pero completo de hooks, anotado:

{
  "hooks": {
    // The event name — PreToolUse, PostToolUse, Stop, Notification, etc.
    "PostToolUse": [
      {
        // matcher filters which tool calls trigger this hook.
        // Omit it (or use "*") to match every tool.
        "matcher": "Edit|Write",
        "hooks": [
          {
            // "command" is currently the only hook type.
            "type": "command",
            // The shell command to run. Receives event JSON on stdin.
            "command": "npx prettier --write \"$(echo $CLAUDE_TOOL_INPUT | jq -r .file_path)\"",
            // Optional: kill the command if it hangs.
            "timeout": 15
          }
        ]
      }
    ]
  }
}

Algunas cosas que vale la pena señalar porque hacen tropezar a la gente:

El campo matcher opera sobre el nombre de la herramienta, no sobre rutas de archivo o contenido. "Edit|Write" coincide con las herramientas Edit y Write; "Bash" coincide con llamadas de shell. Si necesitas filtrar por ruta de archivo o contenido de comando, hazlo dentro de tu script leyendo el payload JSON, no en el matcher.

Cada clave de evento contiene un array de bloques de matcher, y cada bloque de matcher contiene un array de comandos de hook, así que puedes adjuntar varios comandos a un matcher, o un comando a varios matchers, sin duplicar configuración.

El comando recibe el payload del evento como JSON por stdin: nombre de la herramienta, input de la herramienta, y para PostToolUse, el resultado de la herramienta. Un hook que actúa sobre el archivo específico que se está editando lee ese JSON en vez de asumir que el directorio de trabajo de la shell cuenta toda la historia.

Los códigos de salida tienen significado. Salida 0 significa "bien, continúa". Una salida distinta de cero en un hook de PreToolUse bloquea la llamada a la herramienta y devuelve stderr a Claude como razón. Una salida distinta de cero en PostToolUse solo se registra; la herramienta ya se ejecutó, así que no queda nada que bloquear.

Seis recetas que puedes usar hoy

Son deliberadamente estrechas. Copia el bloque, ajusta el comando, y confirma que hace lo que esperas en un archivo desechable antes de confiar en él para trabajo real.

1. Autoformatear después de cada edición

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npx prettier --write . --ignore-unknown"
          }
        ]
      }
    ]
  }
}

Ejecuta Prettier después de cualquier edición o escritura. Para repos grandes, cambia el . genérico por una ruta derivada del JSON del hook para formatear solo el archivo tocado.

2. Bloquear ediciones a rutas protegidas

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "python3 .claude/hooks/guard_paths.py"
          }
        ]
      }
    ]
  }
}

guard_paths.py lee la ruta del archivo desde el JSON de stdin, la comprueba contra una lista negra (migrations/, .env, infra/prod/), y sale con 1 y un mensaje en stderr si coincide. Es lo más parecido a un límite de permisos duro que tiene Claude Code.

3. Ejecutar tests después de cambios en el código fuente

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "cd \"$CLAUDE_PROJECT_DIR\" && npm test -- --onlyChanged --silent"
          }
        ]
      }
    ]
  }
}

Le da a Claude una señal inmediata cuando una edición rompe un test, en vez de esperar a que lo notes en la revisión. Mantén el comando de test estrecho (--onlyChanged, un subconjunto rápido) o esto se convierte en la receta seis de la sección de "cuándo no" más abajo.

4. Notificación de escritorio cuando Claude termina

{
  "hooks": {
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude finished\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}

Específico de macOS (cambia por notify-send en Linux). Útil en cuanto empiezas a correr turnos autónomos más largos y dejas de vigilar la terminal todo el tiempo.

5. Registrar cada comando de bash

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "jq -r '.tool_input.command' >> \"$CLAUDE_PROJECT_DIR/.claude/bash-history.log\""
          }
        ]
      }
    ]
  }
}

Un registro de auditoría que no depende de que recuerdes revisar la transcripción. En una máquina compartida o un repo con un requisito de cumplimiento, esto es casi obligatorio.

6. Puerta de lint antes de commit

{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": ".claude/hooks/block_bad_commit.sh"
          }
        ]
      }
    ]
  }
}

block_bad_commit.sh lee stdin, comprueba si el comando es un git commit, y si lo es ejecuta primero tu linter, saliendo con código distinto de cero si falla. Eso convierte "por favor haz lint antes de hacer commit" de una petición que Claude podría olvidar en una regla que no puede saltarse.

PACK INICIAL GRATIS

¿Configurando hooks junto a tus primeras skills? Te enviamos nuestros 3 skills mejor puntuados más la checklist de instalación que usamos antes de cada test de SkillProof. Gratis.

Consigue el pack inicial gratis

Hooks y skills juntos

No son herramientas que compiten; las mejores configuraciones usan ambas para lo que cada una hace bien. Un ejemplo trabajado: un equipo al que probamos quería cada commit escrito en su estilo de casa, modo imperativo, un prefijo acotado, un cuerpo que explica el porqué, y también querían que se bloquearan los commits si el diff tocaba una migración de base de datos sin un archivo de rollback correspondiente.

La parte del estilo es criterio. Qué cuenta como un buen "porqué" varía según el cambio, y no hay un script de shell que escriba buena prosa de forma fiable. Ese es el trabajo de una skill: algo como Git Workflow Coach cargado cada vez que Claude está a punto de hacer un commit, enseñando la estructura y dando ejemplos de mensajes de commit buenos frente a perezosos. Claude lo lee, aplica criterio, y escribe un mensaje que encaja con el patrón sin ser un relleno de plantilla. Si el equipo también aplica un red-green-refactor estricto, Test-Driven Development es el mismo tipo de adición de criterio-no-ley: da forma a cómo Claude aborda el trabajo, algo que un hook no puede hacer.

La regla de la migración no es criterio, es ley: o existe el archivo de rollback o no existe, y el equipo no quería que "Claude decidió que este no lo necesitaba" fuera una opción. Ese es el hook de PreToolUse de la receta seis, adaptado para comprobar el archivo emparejado en vez de ejecutar un linter, bloqueando la llamada git commit por completo si falta.

Ejecútalos juntos y obtienes un buen mensaje de commit que además tiene garantizado pasar el chequeo de migración, porque la skill maneja la parte que necesita un cerebro y el hook maneja la parte que necesita un muro. Ninguno reemplaza al otro. La skill no puede garantizar cumplimiento, y un hook que escribiera "fix: varios cambios" en cada commit sería inútil. Nuestra página de mejores skills de programación clasifica el lado de criterio de este emparejamiento por puntuación probada, si estás eligiendo una primera skill para correr junto a tus hooks.

Depurar hooks

Los hooks fallan en silencio más a menudo de lo que fallan de forma ruidosa. Lo que suele romperse:

Comillas. Los comandos de hook son cadenas de shell dentro de cadenas JSON, así que una " sin escapar rompe el parseo del JSON antes de que tu comando se ejecute siquiera. En caso de duda, pon la lógica real en un archivo de script y haz que el comando del hook solo lo invoque (bash .claude/hooks/my-hook.sh) en vez de meter en línea un one-liner complejo.

Códigos de salida que no significan lo que crees. Un script de hook que choca con un error no relacionado (dependencia faltante, permiso denegado) sale con código distinto de cero igual que uno que deliberadamente quiere bloquear. Si un hook de PreToolUse empieza a bloquear cada llamada a herramienta y no lo escribiste para que fuera tan estricto, comprueba si el script realmente está fallando en vez de juzgando.

Suposiciones de PATH. Los hooks corren en un entorno de shell que puede no coincidir con tu terminal interactiva. Un comando que funciona bien cuando lo escribes tú mismo puede fallar dentro de un hook porque nvm, un virtualenv, o una herramienta instalada vía un plugin de shell no está en el PATH en ese contexto. Usa rutas absolutas a los binarios, o carga el entorno correcto al principio del script.

Suposiciones silenciosas de stdin. Si tu script espera JSON por stdin y no lo recibe, porque lo probaste ejecutándolo directamente en vez de canalizando un payload de ejemplo, se comportará distinto bajo el harness de como lo hizo en tu terminal.

Timeouts. Un hook sin timeout que se cuelga colgará todo el turno. Configura un timeout explícito en cualquier cosa que toque la red o un subproceso lento.

Cuándo no usar hooks

Los hooks son baratos de escribir y fáciles de usar en exceso. El modo de fallo no es un hook haciendo lo incorrecto, es un hook haciendo lo correcto con demasiada frecuencia. Un hook de PostToolUse que ejecuta toda tu suite de tests después de cada edición individual convierte un cambio de cinco segundos en una espera de dos minutos, repetida por cada edición de una sesión que hace veinte de ellas.

La regla general: si el comando de un hook tarda más de un segundo o dos, acota el matcher, acota lo que comprueba, o muévelo a un evento menos frecuente. Test en cada edición se convierte en test en cada escritura se convierte en test antes de commit a medida que el chequeo se vuelve más caro. Ajusta el coste del hook a la frecuencia con la que se dispara su evento, y considera si una skill, que solo carga contexto y no ejecuta un proceso, encaja mejor para cualquier cosa que no sea estrictamente cumplimiento.

También vale la pena no recurrir a un hook para arreglar el problema de activación de una skill. Si una skill no se activa cuando debería, el arreglo es una mejor descripción, no un hook, ya que los hooks ejecutan comandos de shell y no pueden cargar contenido de una skill. Para ese modo de fallo, mira por qué las skills no se activan.

PACK SKILLPROOF

Emparejar hooks con las skills correctas es la mayor parte de una buena configuración de Claude Code. El Developer Toolkit empaqueta nuestros skills de programación mejor puntuados, pre-comprobados por conflictos de activación, así que la mitad de skill de este emparejamiento ya está hecha por ti.

Consigue el Developer Toolkit — $10

Preguntas frecuentes

¿Los hooks ralentizan cada sesión de Claude Code?

Solo los eventos a los que los adjuntas, y solo lo que tarde tu comando. Un hook en PostToolUse para Edit corre una vez por edición; un formateador rápido es imperceptible, una suite de tests completa se nota en cada edición, que es el caso que se cubre arriba bajo cuándo no usar hooks.

¿Un hook puede detener a Claude de hacer algo por completo?

Sí, para eso están los hooks de PreToolUse. Sal con código distinto de cero y la llamada a la herramienta se bloquea antes de ejecutarse, con stderr normalmente devuelto a Claude como la razón. Ese es el mecanismo detrás de la receta dos (rutas protegidas) y la receta seis (puerta de lint).

¿Dónde pongo mi configuración de hooks, proyecto o usuario?

A nivel de proyecto (.claude/settings.json, comiteado) si debería aplicarse a todos en esa base de código: formateo, rutas protegidas, chequeos de migración. A nivel de usuario (~/.claude/settings.json) para una preferencia personal, como la notificación de escritorio de la receta cuatro.

¿Cuál es la diferencia entre un hook y una skill que dice "siempre formatea el código"?

El hook de verdad siempre se ejecuta. Una skill que le dice a Claude que siempre formatee el código sigue siendo una instrucción que Claude lee y decide seguir; es un empujón fuerte, no una garantía, y compite con otras cosas en el contexto por atención en un turno dado. Si "siempre" es un requisito en vez de una preferencia, usa un hook.

Mi hook no se ejecuta en absoluto. ¿Qué es lo primero que debo comprobar?

Confirma que el archivo de settings es JSON válido (una coma sobrante o una comilla sin escapar puede desactivar en silencio todo el bloque de hooks) y que el nombre del evento y el matcher están escritos exactamente como se espera; ambos distinguen mayúsculas de minúsculas. Después de eso, comprueba si editaste la configuración de proyecto cuando la sesión está leyendo la configuración de usuario, o viceversa.

★ 9.6/10 × 3

El pack de inicio gratis

Los 3 skills con nuestras mejores puntuaciones de test más la checklist de instalación: el setup que pondríamos en una máquina recién estrenada. Gratis, por email.

Un email con el pack + un breve resumen semanal con nuevos resultados de test. Date de baja cuando quieras.