PTENES
4.2 ~50 min · Intermedio

🪝 Hooks: automatizar el harness

Configura hooks de eventos para aplicar políticas, automatizar lint e inyectar contexto, sin tocar el código del agente.

Inicio de sesión inyecta contexto PreToolUse valida/bloquea ⚙️ Herramienta Bash · Read · Write ejecuta aquí PostToolUse lint · auditoría Stop cleanup hook.sh lint.sh Ciclo de vida del harness: los hooks interceptan en 4 puntos

🪝 Qué son los hooks

Los hooks son scripts externos ejecutados por el harness en momentos específicos del ciclo de vida del agente. Reciben metadatos del evento vía stdin y pueden influir en el comportamiento del harness mediante el exit code y el output.

La gran ventaja: agregas comportamiento sin modificar el prompt ni el agente. Las políticas quedan en scripts versionados en el repositorio y se aplican automáticamente a todo el equipo.

🔔 SessionStart

Se activa al iniciar una nueva sesión. El stdout se inyecta como mensaje inicial en el contexto. Ideal para cargar el estado del proyecto, la rama actual y las tareas pendientes.

🔍 PreToolUse

Se activa antes de cada herramienta. Exit 0 = permite. Un Exit distinto de cero = bloquea la herramienta. El mensaje de error en stderr se devuelve al agente.

📋 PostToolUse

Se activa después de que cada herramienta devuelve un resultado. El stdout se inyecta en el contexto. Ideal para ejecutar lint automático, auditorías y métricas de uso.

🛑 Detener

Se activa cuando el agente termina. Ideal para tareas de limpieza, notificaciones de finalización, informes de sesión y persistencia de memoria.

⚙️ Configurar en settings.json

Los hooks se configuran en la clave hooks de settings.json. Cada entrada define: el evento, un matcher de herramienta (opcional) y el comando de shell que se ejecutará.

// .claude/settings.json — ejemplo con 4 hooks
{
  "hooks": {
    "SessionStart": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/session-start.sh"
          }
        ]
      }
    ],
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/block-dangerous.sh"
          }
        ]
      }
    ],
    "PostToolUse": [
      {
        "matcher": "Write|Edit",
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/lint.sh"
          }
        ]
      }
    ],
    "Stop": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "bash .claude/hooks/session-end.sh"
          }
        ]
      }
    ]
  }
}

💡 Global vs local

~/.claude/settings.json

Se aplica a todos los proyectos. Útil para políticas personales: bloqueo de comandos destructivos, preferencias de lint.

.claude/settings.json

Específico del proyecto. Se registra en el repositorio con un commit — todo el equipo hereda las políticas automáticamente.

🛠️ Casos de uso prácticos

🧹

Lint automático después de cada guardado

PostToolUse en Write/Edit ejecuta eslint/prettier. El resultado se inyecta en el contexto: el agente ve los errores y los corrige automáticamente.

#!/bin/bash # .claude/hooks/lint.sh
FILE=$(cat /dev/stdin | jq -r '.tool_input.file_path // empty')
if [[ "$FILE" == *.ts || "$FILE" == *.tsx ]]; then
  npx eslint --fix "$FILE" 2>&1
fi
🔒

Bloquear comandos peligrosos

PreToolUse en Bash inspecciona el comando. Si detecta patrones prohibidos, devuelve exit 1 con un mensaje que explica el bloqueo.

#!/bin/bash # .claude/hooks/block-dangerous.sh
CMD=$(cat /dev/stdin | jq -r '.tool_input.command // empty')
DANGEROUS="rm -rf|git push --force|DROP TABLE|truncate"
if echo "$CMD" | grep -qE "$DANGEROUS"; then
  echo "❌ Comando bloqueado por política de segurança" >&2
  exit 1
fi
💉

Inyectar contexto en SessionStart

Al iniciar cada sesión, inyecta automáticamente el estado actual: branch, commits recientes, issues abiertas. El agente empieza con el contexto necesario.

#!/bin/bash # .claude/hooks/session-start.sh
echo "=== CONTEXTO DO PROJETO ==="
echo "Branch: $(git branch --show-current)"
echo "Último commit: $(git log -1 --oneline)"
echo "Arquivos modificados:"
git status --short | head -10

⚖️ Qué hacer y qué evitar

✓ Buenas prácticas

  • ✓ Scripts rápidos — los hooks con timeout corto no bloquean al agente
  • ✓ Mensajes claros en stderr cuando se bloquee
  • ✓ Usar matcher para aplicarlo solo donde tenga sentido
  • ✓ Versionar scripts en .claude/hooks/

✗ Trampas comunes

  • ✗ Hooks que tardan > 5s — bloquean la ejecución del agente
  • ✗ PreToolUse en todas las herramientas sin un matcher específico
  • ✗ Inyectar un output demasiado grande en SessionStart (presión de contexto)
  • ✗ Confiar solo en fail-open para hooks de seguridad críticos

🛡️ Seguridad y fail-open

Fail-open es el comportamiento predeterminado: si un hook falla (timeout, error del script, crash), el harness continúa la ejecución normalmente. La herramienta que el hook debería interceptar no queda bloqueada.

Esto es intencional para los hooks de enriquecimiento (lint, contexto): no quieres que un lint con errores interrumpa todo el trabajo. Pero, en los hooks de seguridad, fail-open es peligroso.

🔑 Regla práctica

Hooks de auditoría: fail-open está bien. Si falla el log, el trabajo continúa.
Hooks de seguridad: haz que el script sea extremadamente simple y rápido — el riesgo de falla cae a cero.
// Buenas prácticas de robustez en hooks
#!/bin/bash
# Sempre: timeout explícito para não travar o agente
# Leitura do stdin com timeout
INPUT=$(timeout 2 cat /dev/stdin 2>/dev/null || echo '{}')

# Parse seguro com fallback
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)

# Se nada para verificar, sair com sucesso (fail-open explícito)
[ -z "$CMD" ] && exit 0

# Verificação principal
if echo "$CMD" | grep -qE "rm -rf /|DROP DATABASE"; then
  echo "Comando bloqueado: risco de perda de dados irreversível" >&2
  exit 1
fi

exit 0

📊 Datos disponibles en cada hook

Evento Datos en stdin Impacto del output
Inicio de sesión Metadatos de la sesión Stdout inyectado como mensaje inicial
PreToolUse Nombre de la tool + tool_input (JSON) Exit 0=permite, 1=bloquea; stderr→agente
PostToolUse tool_input + tool_response (JSON) Stdout inyectado en el contexto actual
Stop Motivo del cierre Solo efectos secundarios (log, notificación)

⚡ Hooks más usados

  1. 1. PostToolUse Write → lint automático
  2. 2. PreToolUse Bash → bloqueo de comandos
  3. 3. SessionStart → inyección de contexto
  4. 4. Stop → notificación de finalización

🔧 Skill /update-config

La skill /update-config de Claude Code facilita agregar hooks a settings.json mediante un prompt en lenguaje natural: "agrega un hook de lint para TypeScript".

🎯 Resumen del Módulo 4.2

✓
4 eventos — SessionStart, PreToolUse, PostToolUse, Stop
✓
settings.json — configuración declarativa, versionable y heredada por el equipo
✓
Lint automático — PostToolUse en Write/Edit, resultado inyectado en el contexto
✓
Bloqueo — PreToolUse con exit 1 + mensaje en stderr
✓
Fail-open — un hook que falla no bloquea la ejecución de forma predeterminada

Siguiente módulo:

4.3 — ⌨️ Slash commands y workflows deterministas