devAlice
← AI Agents

Hooks de Claude Code — ejecutar comandos de shell automáticamente en eventos de sesión y herramientas

Los hooks en settings.json insertan comandos de shell al inicio de sesión, antes y después del uso de herramientas, y al detenerse. Cinco ejemplos: bloqueo de seguridad, autoformato, logging, alertas externas.

Los hooks de Claude Code — definidos bajo la clave hooks en settings.json — permiten ejecutar comandos de shell automáticamente al inicio de la sesión, antes y después de llamadas a herramientas, y al finalizar la sesión. Intercepta lo que el agente de IA está a punto de hacer, corrígelo después, limpia al final. Un potente punto de extensión.

Esta guía está orientada a Claude Code 1.x / la especificación oficial de hooks. Se basa en la configuración de Claude Code con una capa adicional de automatización y seguridad.

TL;DR

Evento hookSe disparaUso típico
SessionStartJusto después de que comienza la sesiónAviso de sincronización de memoria, validación de entorno
UserPromptSubmitJusto después de que el usuario envía un promptReconocimiento de slash commands, logging, posprocesado
PreToolUseJusto antes de una llamada a herramientaBloquear comandos peligrosos, proteger archivos secretos
PostToolUseJusto después de una llamada a herramientaAutoformato (Prettier), lint, logging de efectos secundarios
StopAl finalizar la sesiónResumen de cambios, copia de seguridad, notificación

Reglas clave:

  • Código de salida 2 bloquea la llamada a la herramienta (PreToolUse).
  • El JSON llega al stdin del hook: parsearlo con jq.
  • Los hooks son comandos de shell: tener en cuenta las diferencias de SO (macOS/Linux/Windows WSL).

Requisitos previos

  • Claude Code 1.x instalado — Configuración de Claude Code
  • jq instalado (brew install jq / apt install jq / winget install jqlang.jq)
  • Acceso de edición a settings.json (normalmente ~/.claude/settings.json)

Descargar la línea base

La línea base recomendada por devAlice: cinco hooks + lista de denegación/permiso. Descargarla y personalizar.

settings.json
# 1. Descargar
curl -fsSL https://devalice.jaceclub.com/assets/ai-agents/claude-code-hooks/settings.example.json -o settings.json
 
# 2. Verificar SHA-256
shasum -a 256 settings.json
# Esperado: 30c1d4117a935cac03b601c71125c11b71733c8c59517ab129fd8913cfb8a69e
 
# 3. Inspeccionar (especialmente los hooks PreToolUse)
less settings.json
 
# 4. Copiar a su ubicación (macOS/Linux)
mkdir -p ~/.claude
cp settings.json ~/.claude/settings.json

⚠️ Advertencia: los hooks ejecutan comandos de shell arbitrarios. Inspecciona siempre los hooks de otros antes de aplicarlos. Este ejemplo es una línea base segura de cinco hooks, pero revísalo y adáptalo a tu entorno.


1. Estructura de hooks (5 min)

La clave hooks en ~/.claude/settings.json:

{
  "hooks": {
    "NombreDelEvento": [
      {
        "matcher": "regex o nombre de herramienta",  // opcional
        "hooks": [
          {
            "type": "command",
            "command": "/bin/bash -lc 'comando de shell real'"
          }
        ]
      }
    ]
  }
}

Reglas de coincidencia:

  • Sin matcher: se dispara en cada evento
  • matcher = nombre de herramienta (Bash, Write, Edit, Read…): solo esa herramienta
  • matcher = regex (^/(?:ej|euijinpro)\\b): para UserPromptSubmit, coincide con el texto del prompt

JSON que llega al stdin:

{
  "session_id": "...",
  "transcript_path": "/ruta/a/transcript.jsonl",
  "cwd": "/Users/...",
  "tool_name": "Bash",
  "tool_input": { "command": "..." },
  "tool_response": { /* Solo PostToolUse */ }
}

Extraer con jq -r .tool_input.command.


2. SessionStart — Cargar contexto al inicio

El hook más simple. Imprimir una línea cuando comienza la sesión:

"SessionStart": [
  {
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'printf \"[inicio de sesión] %s\\nmemory sync: latest (skip)\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
      }
    ]
  }
]

El stdout se expone como mensaje de sistema. Úsalo para:

  • Activar la carga automática de archivos de memoria
  • Inyectar contexto específico del proyecto
  • Mostrar el estado de git al comienzo de la sesión
# Ejemplo más rico
command='/bin/bash -lc "echo [inicio de sesión]; git -C \"$PWD\" status -s; git -C \"$PWD\" log --oneline -3"'

3. PreToolUse — Bloquear comandos peligrosos (el hook de mayor valor)

exit 2 bloquea. Aunque el agente de IA intente un comando de shell peligroso, lo detienes.

3.1 Bloquear patrones peligrosos

"PreToolUse": [
  {
    "matcher": "Bash",
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'cmd=$(jq -r .tool_input.command); case \"$cmd\" in *\"rm -rf /\"*|*\"sudo rm\"*|*\":(){\"*) echo \"comando peligroso bloqueado: $cmd\" >&2; exit 2;; esac'"
      }
    ]
  }
]

Patrones bloqueados:

  • rm -rf /: destrucción del sistema
  • sudo rm: eliminación amplia (permitir vía lista de permitidos si lo necesitas)
  • :(){: fork bomb (:(){ :|: & };:)

3.2 Bloquear ediciones de archivos secretos

{
  "matcher": "Write|Edit",
  "hooks": [
    {
      "type": "command",
      "command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in */.env|*/credentials.json|*/.aws/credentials) echo \"edición de archivo secreto bloqueada: $path\" >&2; exit 2;; esac'"
    }
  ]
}

Previene ediciones accidentales a .env, credentials.json, ~/.aws/credentials, etc.

3.3 Proteger directorios específicos

command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); case \"\$path\" in /etc/*|/System/*) echo \"ruta de sistema bloqueada: \$path\" >&2; exit 2;; esac"'

4. PostToolUse — Autoformato, lint y registro

Limpieza después de la llamada a la herramienta.

4.1 Autoformato con Prettier

"PostToolUse": [
  {
    "matcher": "Edit|Write|MultiEdit",
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'path=$(jq -r .tool_input.file_path); case \"$path\" in *.ts|*.tsx|*.js|*.jsx) prettier --write \"$path\" 2>/dev/null || true ;; esac'"
      }
    ]
  }
]

Después de un Write/Edit del agente de IA, Prettier limpia automáticamente. Añade ~0,5 s por edición.

4.2 Registrar archivos modificados

command='/bin/bash -lc "path=\$(jq -r .tool_input.file_path); echo \"[cambiado] \$(date +%T) \$path\" >> ~/.claude/change.log"'

Registro ordenado por tiempo de cada cambio de archivo en la sesión.

4.3 Type-check y lint (mover trabajo lento al hook Stop)

tsc --noEmit en cada edición es costoso. Mejor colocarlo en el hook Stop (§6).


5. UserPromptSubmit — Detección de slash commands y posprocesado

"UserPromptSubmit": [
  {
    "matcher": "^/(?:ej|euijinpro)\\b",
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'echo \"[ej skill] prompt marcado para logging en euijin.pro\" >&2'"
      }
    ]
  }
]
  • Detectar slash commands y activar sistemas externos (Slack, logging separado)
  • Etiquetar automáticamente palabras clave específicas (urgente, bug)
  • Verificar el umbral del presupuesto de tokens

6. Stop — Limpieza al final de sesión

"Stop": [
  {
    "hooks": [
      {
        "type": "command",
        "command": "/bin/bash -lc 'printf \"\\n[fin de sesión] %s\\n\" \"$(date \"+%Y-%m-%d %H:%M:%S\")\"'"
      }
    ]
  }
]

Usos:

  • Resumen del estado de git de los archivos modificados
  • Copia de seguridad (rsync o git stash)
  • Notificación externa al cerrar la sesión
  • Una ronda de type-check y build
command='/bin/bash -lc "if git diff --quiet; then echo \"[limpio]\"; else echo \"[cambios]\"; git diff --stat; fi"'

7. Permisos: deny/allow

Separado de los hooks, pero la clave de seguridad más importante de settings.json.

"permissions": {
  "deny": [
    "Bash(rm -rf /:*)",
    "Bash(sudo rm:*)",
    "Read(.env)",
    "Read(.env.local)",
    "Read(.aws/credentials)",
    "Read(*/credentials.json)"
  ],
  "allow": [
    "Bash(git status:*)",
    "Bash(git log:*)",
    "Bash(git diff:*)",
    "Bash(pnpm:*)",
    "Bash(npm:*)",
    "Bash(node:*)"
  ]
}
  • deny gana incondicionalmente: complementa los bloqueos de hooks
  • allow aprueba automáticamente sin confirmación del usuario (comandos seguros de uso frecuente)
  • Todo lo demás pregunta al usuario cada vez

Los hooks de esta guía se solapan con deny: defensa en profundidad.


8. Ubicaciones y prioridad de settings.json

UbicaciónÁmbitoNotas
~/.claude/settings.jsonGlobal de usuarioTodos los proyectos
<proyecto>/.claude/settings.jsonProyectoSe combina con el usuario al entrar al proyecto
<proyecto>/.claude/settings.local.jsonProyecto + local de usuarioSe recomienda añadir a .gitignore

Orden de combinación: usuario → proyecto → proyecto.local. El posterior sobreescribe al anterior.

Los hooks se acumulan en lugar de fusionarse: 5 hooks de usuario + 3 hooks de proyecto = los 8 se ejecutan.


9. Depuración: cuando el hook no se dispara

9.1 Inspeccionar el payload de stdin

Hook de depuración temporal:

{
  "matcher": "Bash",
  "hooks": [
    {
      "type": "command",
      "command": "/bin/bash -lc 'cat > /tmp/claude-hook-debug.json'"
    }
  ]
}

cat /tmp/claude-hook-debug.json | jq .

9.2 Probar códigos de salida manualmente

command='/bin/bash -lc "echo dbg >&2; exit 2"'

→ Verificar que el bloqueo en PreToolUse funciona.

9.3 Confirmar que el hook se dispara

command='/bin/bash -lc "echo \"[hook disparado $$ $(date +%T)]\" >> ~/.claude/hook-trace.log"'

Verificar el log después de una sesión.

9.4 Errores de sintaxis JSON

settings.json es implacable: las comas finales y los escapes omitidos son errores frecuentes. Validar con jq . ~/.claude/settings.json.


10. Solución de problemas

"command not found: jq"

brew install jq (macOS) / sudo apt install jq (Linux) / winget install jqlang.jq (Windows).

El hook se dispara pero no hace nada

  • command debe ser una sola línea. Para varias líneas, usar \\n o llamar a un script separado.
  • Revisar los escapes de shell: \" requiere doble escape dentro de JSON.

PreToolUse: exit 2 ignorado

  • Confirmar que exit 2 realmente es la última instrucción del comando de shell.
  • Un entorno con set -e puede hacer que case ;; salga con 0: usar exit 2 directamente dentro de la rama coincidente.

Prettier no se ejecuta

  • Confirmar que Prettier está instalado en la raíz del proyecto (pnpm prettier --version).
  • El PATH del hook puede no incluir node_modules/.bin: usar npx prettier o una ruta absoluta.

Los cambios en settings.json no se aplican

  • Se requiere reiniciar la sesión. O /config reload si está soportado.
  • Podría estar siendo ignorado silenciosamente por un error de sintaxis JSON. Validar con jq ..

Pasos siguientes

Referencias

Registro de cambios

  • 2026-05-12 — Borrador inicial (devAlice M2 seed expansion)