Saltar al contenido principal

Hooks: automatización determinista

Avanzado

Los hooks son comandos de shell que Claude Code ejecuta automáticamente en puntos definidos de su ciclo de vida. Donde los permisos deciden si una acción está permitida, los hooks te dejan a ti ejecutar lógica determinista a su alrededor: formato, validación, registro, controles. Son la manera de hacer que un comportamiento esté garantizado en lugar de quedar en un "por favor, acuérdate de".

What you'll learn
  • Cuándo recurrir a un hook en lugar de a una instrucción o un permiso
  • Cómo se conecta un hook: evento, matcher y el payload JSON en stdin
  • Las dos formas en que un hook bloquea una acción: código de salida 2 frente a JSON en stdout
  • Las buenas prácticas y los errores comunes que separan los hooks rápidos y seguros de los lentos y silenciosos

Cuándo recurrir a un hook

Recurre a un hook cuando quieras que un comportamiento esté garantizado, no simplemente solicitado. Cada tarea común se asigna a un evento del ciclo de vida:

  • Formatear / hacer lint automáticamente tras cada edición de archivo (PostToolUse).
  • Bloquear una acción que infringe una regla antes de que se ejecute (PreToolUse).
  • Notificar o registrar cuando termina una sesión o se completa una tarea (Stop).
  • Inyectar contexto al inicio de la sesión.
Eventos de hook de un vistazo
Pulsa Intro o Espacio para girar la tarjeta. Usa las flechas izquierda y derecha para moverte entre las tarjetas.Término mostrado.
1 / 4

Cómo funcionan

Registras los hooks en settings.json, haciéndolos coincidir con un evento (y a menudo un matcher de herramienta). Cuando el evento se dispara, Claude ejecuta tu comando, pasando un payload JSON por stdin (el nombre de la herramienta, sus entradas, la sesión). El código de salida y la salida de tu comando deciden qué ocurre a continuación.

Guided walkthrough1 of 4
  1. Registra el hook en settings.json bajo el evento del ciclo de vida que te interese; por ejemplo, PostToolUse.
{
"hooks": {
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
]
}
]
}
}

El hook anterior lee la ruta del archivo editado del JSON de stdin (.tool_input.file_path) y lo formatea. No asumas que una variable de entorno contiene la ruta: léela desde stdin. Marcadores de ruta útiles como ${CLAUDE_PROJECT_DIR} están disponibles para localizar scripts.

Cómo bloquea un hook

Dos formas, según el evento:

  • Código de salida 2: el hook hace fallar la acción y lo que haya escrito en stderr se convierte en el mensaje que ve Claude. Sencillo y funciona para los hooks de comando.
  • JSON en stdout (salida 0): devuelve una decisión estructurada. Para PreToolUse, eso es un permissionDecision de deny; para PostToolUse/Stop/etc. es {"decision": "block", "reason": "…"}.

El script de abajo es un hook PreToolUse sobre la herramienta Bash. Léelo de arriba abajo: extrae el comando de stdin y, si parece destructivo, escribe una razón en stderr y sale con 2 para bloquear.

#!/usr/bin/env bash
# PreToolUse hook on the Bash tool: refuse to delete things.
command=$(jq -r '.tool_input.command' < /dev/stdin)
if [[ "$command" == rm\ * || "$command" == *"rm -rf"* ]]; then
echo "Blocked: destructive 'rm' is not allowed by policy." >&2
exit 2
fi
exit 0

El modelo mental

Un hook PreToolUse se ejecuta antes de la acción y puede bloquearla; un hook PostToolUse se ejecuta después de que tenga éxito y reacciona al resultado.

Buenas prácticas

  • Mantén los hooks rápidos e idempotentes: se ejecutan mucho.
  • Falla de forma ruidosa ante problemas reales, pero no bloquees por cuestiones cosméticas.
  • Trata la salida del hook como feedback para Claude: un mensaje claro le ayuda a autocorregirse.
  • Los hooks se ejecutan con los privilegios de tu shell: revisa cualquier hook que no hayas escrito tú (Revisar código de terceros).

Errores comunes

  • Leer la ruta del archivo desde una variable de entorno. La ruta vive en el JSON de stdin (.tool_input.file_path), no en $CLAUDE_FILE_PATH. Pasa stdin a través de jq.
  • Bloqueos silenciosos. Si un hook PreToolUse sale con 2 sin nada en stderr, Claude queda bloqueado pero no sabe por qué y no puede adaptarse. Escribe siempre una razón clara.
  • Hooks lentos. Un hook PostToolUse se ejecuta tras cada edición coincidente. Un linter de 3 segundos hace que toda la sesión se sienta lenta: mantén los hooks rápidos e, idealmente, actúa solo sobre lo que cambió.
  • Matchers demasiado amplios. matcher: ".*" se dispara con cada herramienta. Acótalo con un nombre exacto, una lista Edit|Write o el campo if por manejador (p. ej. "if": "Bash(git push *)").
  • Confiar en hooks que no escribiste. Un hook ejecuta shell arbitrario con tus privilegios. Revisa primero cualquier hook de un plugin o plantilla: consulta Revisar código de terceros.
Watch out
  • Un hook ejecuta shell arbitrario con tus privilegios: nunca conectes un hook de un plugin o plantilla sin leerlo primero.

Hay plantillas listas para copiar y pegar en Recetas de Hooks y settings.json.

Formatear automáticamente los archivos editados (PostToolUse en Edit|Write)

{
"hooks": {
  "PostToolUse": [
    {
      "matcher": "Edit|Write",
      "hooks": [
        { "type": "command", "command": "jq -r '.tool_input.file_path' | xargs npx prettier --write" }
      ]
    }
  ]
}
}

Compruébalo tú mismo

0/3
  1. ¿Dónde encuentra un hook la ruta del archivo que se acaba de editar?
  2. Un hook PreToolUse sale con el código 2. ¿Qué ocurre?
  3. ¿Por qué se considera el matcher ".*" un error común?
Key takeaways
  • Los hooks hacen que un comportamiento esté garantizado, no solicitado: ejecutan lógica determinista alrededor de acciones que los permisos solo permiten o deniegan.
  • Registra un hook en settings.json contra un evento más un matcher; Claude canaliza un payload JSON por stdin y lee tu código de salida y tu salida.
  • Lee la ruta del archivo desde stdin (.tool_input.file_path), no desde una variable de entorno.
  • Bloquea con el código de salida 2 (stderr se convierte en el mensaje) o con JSON estructurado en stdout (salida 0); incluye siempre una razón clara.
  • Mantén los hooks rápidos, idempotentes y con matchers acotados, y revisa cualquier hook que no hayas escrito tú, ya que se ejecuta con los privilegios de tu shell.

Siguiente