← Claude Code Hub
✦ Tip #193 Sep 10, 2026

Hooks condicionales en Claude Code: actúa solo cuando lo necesitas

Tu hook de PreToolUse arranca un proceso en cada llamada, case o no. El campo `if` filtra antes de que ese proceso exista, con la misma sintaxis de reglas de permisos que ya usas en tu allowlist.

Sin if, cinco llamadas a Bash lanzan cinco veces el mismo script; con if: "Bash(git commit *)" solo la del commit cruza el filtro y las otras cuatro no llegan a lanzar proceso

TL;DR Pon "if": "Bash(git commit *)" en la entrada del hook y Claude Code no lanza el proceso cuando el comando no casa. Es la misma sintaxis Tool(patrón) de tus reglas de permisos, una sola regla por entrada, y con el nombre de la tool dentro ya no hace falta matcher. Cuidado con dónde lo pones: if solo se evalúa en los cinco eventos de tool. En un Stop o un SessionStart, ese hook no se salta una vez, deja de existir, y sin --debug hooks no te enteras.

Tienes un hook de PreToolUse sobre Bash. Se dispara con cada llamada: cada ls, cada cat, cada echo de dos palabras. El script lee el JSON, ve que no le toca y sale con 0. Has arrancado un proceso entero para no hacer nada, y lo repites decenas de veces por sesión.

Hay un campo que corta eso antes de que ocurra.

Resultado, de una sesión real con dos comandos seguidos:

> ejecuta git status --short y luego echo hola

# hook.log
{"hook_event_name":"PreToolUse","tool_name":"Bash",
 "tool_input":{"command":"git status --short", ...}}

# Una entrada. El echo no llegó a lanzar nada.

Cómo funciona

if vive en la entrada del hook, al lado de type y command. Lleva una regla de permisos con la sintaxis Tool(patrón) que ya usas en tu allowlist, la misma de las reglas deny, allow y ask.

Claude Code la evalúa cuando decide qué hooks casan con la llamada, antes de lanzar el proceso. Si la regla no casa, no hay fork, no hay intérprete arrancando y no hay stdin que escribir. La diferencia con un script que mira el JSON y sale pronto no es de milisegundos: es que uno de los dos no llega a existir.

Una regla por entrada. No hay && ni ||. Dos condiciones son dos entradas.

Cómo montarlo

1. Filtra por comando

{
  "hooks": {
    "PreToolUse": [
      {
        "hooks": [
          {
            "type": "command",
            "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/pre-commit.sh",
            "if": "Bash(git commit *)"
          }
        ]
      }
    ]
  }
}

Fíjate en lo que no hay: matcher. Con el nombre de la tool dentro del if, el matcher deja de hacer falta.

2. Filtra por ruta

{
  "type": "command",
  "command": "$CLAUDE_PROJECT_DIR/.claude/hooks/lint.sh",
  "if": "Write(src/**)"
}

Para Write y Edit el patrón casa contra la ruta del archivo, relativa a tu directorio de trabajo. Un Write sobre src/a.ts lanza el hook; uno sobre b.ts en la raíz, no. Desde la versión 2.1.214 src/** significa el src de la raíz y lo que cuelga de él; si quieres cualquier carpeta llamada src a cualquier profundidad, el patrón es **/src/**.

3. Sin paréntesis, toda la tool

{ "type": "command", "command": "...", "if": "Bash" }

Sin contenido de regla, if casa con todas las llamadas a esa tool. Útil para escribir el filtro entero en la entrada y no repartirlo entre matcher e if.

Los cinco eventos donde if se evalúa

if solo se evalúa en cinco eventos: PreToolUse, PostToolUse, PostToolUseFailure, PermissionRequest y PermissionDenied. Son los que traen una llamada a una tool contra la que casar.

En cualquier otro (Stop, SessionStart, Notification, PreCompact) la entrada no se salta esta vez y ya. Se descarta entera, siempre, y en silencio. Tu hook está escrito, el JSON es válido, y no se ejecuta nunca.

Para verlo hay que pedirlo. Arranca la sesión con --debug hooks y busca la línea en el log de esa sesión:

grep "if condition" ~/.claude/debug/<id-de-sesión>.txt
2026-09-10T19:04:47.314Z [DEBUG] Hook if condition "Bash(git *)" cannot be evaluated for non-tool event Stop

Si tu hook de Stop no dispara, mira si le has puesto un if antes de revisar el script.

if filtra, no protege

Con Bash el filtro es de mejor esfuerzo. Claude Code descompone el comando para ver qué se ejecuta de verdad, y cuando no puede saberlo, lanza el hook igual: $TOOL git push dispara un if de Bash(git *) porque no hay forma de saber qué expande esa variable. Un patrón con más que el nombre del comando también dispara ante $(), backticks o variables.

La documentación lo deja escrito: como el filtro es de mejor esfuerzo, para imponer un allow o un deny de verdad usa el sistema de permisos, no un hook. if es una herramienta de rendimiento.

Referencia

Pieza Valor
Eventos donde se evalúa PreToolUse · PostToolUse · PostToolUseFailure · PermissionRequest · PermissionDenied
Sintaxis Una regla de permisos: Bash(git *), Edit(*.ts), Write(src/**), o el nombre pelado Bash
Combinaciones Ninguna. Una regla por entrada; dos condiciones, dos entradas
Dónde vale Cualquier origen de hooks: settings.json de usuario, proyecto y local, hooks/hooks.json de plugin, y frontmatter de skill y de subagente
Tipos de hook command, http, mcp_tool, prompt, agent

Documentación oficial: Intercept and control agent behavior with hooks

No lo confundas con las reglas condicionales de .claude/rules/: aquellas deciden qué entra en tu contexto, esta decide si se lanza un proceso. Es la pieza que le faltaba a los hooks de toda la vida, donde el matcher solo sabe filtrar por nombre de tool. Y es la misma idea del matcher estrecho que auto-aprueba solo el plan, un punto más fina: allí acotas por tool, aquí por lo que esa tool va a hacer.

Requisitos

  • Verificado en Claude Code 2.1.267. El cambio de src/** es de la 2.1.214.
Workshop para equipos

Multiplica el output de tu equipo sin sacrificar calidad: workshop AI First de 6 a 8 horas, online, sobre la plataforma Claude.

Ver el workshop
Guía gratuita

Los 51 esenciales, en PDF.

Una página por tip. Cinco capítulos. Lo que de verdad uso a diario en producción — sin teoría, sin humo.

  • I. Empieza bien 10 tips
  • II. Conciencia 3 tips
  • III. Maestría 22 tips
  • IV. Autonomía 10 tips
  • V. Comparativa 6 tips
¿Eres desarrollador/a Web profesional?

Recibirás la guía por email · Te unes a la newsletter Gravitas · Cancela cuando quieras

de 51
#

Wmedia · 51 Tips
Guía gratuita · 51 tips · 5 capítulos

Los 51 esenciales, en PDF.

¿Eres desarrollador/a Web profesional? · Cancela cuando quieras