# Hooks de Claude Code: guía práctica con ejemplos

URL: https://agentvera.dev/es/blog/claude-code-hooks-guide/ · Publicado: 2026-10-23

Cómo funcionan los hooks de Claude Code: eventos, settings.json, matchers, códigos de salida y JSON, con ejemplos para formatear, bloquear comandos y avisarte.

**En resumen:**

- Los hooks se definen bajo la clave hooks de settings.json y siempre se ejecutan en su evento, porque los ejecuta Claude Code, no el modelo.
- Para bloquear una llamada a una herramienta, un hook PreToolUse sale con código 2 o devuelve JSON con permissionDecision en deny; el código de salida 1 no bloquea nada.
- Un hook deny sigue bloqueando llamadas con --dangerously-skip-permissions, pero los hooks no son un sandbox y se ejecutan con los permisos de tu usuario.

Los hooks de Claude Code son comandos que Claude Code ejecuta por sí mismo en puntos fijos de una sesión: antes de llamar a una herramienta, después de editar un archivo, al enviar un prompt, cuando Claude termina o necesita tu atención. Los defines bajo la clave `hooks` de `settings.json`. Un hook puede añadir contexto, formatear un archivo o bloquear una acción saliendo con código 2 o devolviendo una decisión en JSON. A diferencia de las instrucciones de `CLAUDE.md`, un hook siempre se ejecuta, porque lo ejecuta el harness, no el modelo.

Esta guía cubre los eventos que importan, el formato de configuración, los matchers, los códigos de salida y el JSON, tres hooks que puedes copiar hoy y las notas de seguridad que conviene leer antes de añadir ninguno. Los datos salen de la [referencia de hooks](https://code.claude.com/docs/en/hooks "Referencia de hooks de Claude Code") y de la [guía de hooks](https://code.claude.com/docs/en/hooks-guide "Guía de hooks de Claude Code") oficiales. Si solo quieres la definición corta, consulta la [entrada del glosario sobre hooks de Claude Code](/es/glossary/claude-code-hooks/ "Hooks de Claude Code").

## Por qué usar hooks en lugar de instrucciones

Puedes escribir «ejecuta siempre Prettier después de editar» en `CLAUDE.md`, y Claude lo hará casi siempre. «Casi» es el problema. Las instrucciones son consejos que el modelo sopesa frente a todo lo demás en su contexto. Los hooks son código que se ejecuta cada vez que salta el evento.

Eso hace de los hooks la herramienta adecuada para tres tipos de reglas:

- **Cosas que deben ocurrir siempre**, como formatear, pasar el linter o registrar logs.
- **Cosas que nunca deben ocurrir**, como editar `.env` o ejecutar `rm -rf` en la carpeta equivocada.
- **Cosas de las que quieres enterarte**, como que Claude esté esperando una respuesta de permisos mientras tú estás en otra ventana.

Deja los consejos de estilo y el conocimiento del proyecto en [CLAUDE.md](/es/blog/claude-md-agents-md/ "CLAUDE.md y AGENTS.md") y pon las reglas estrictas en hooks.

## Eventos de hooks que de verdad vas a usar

La referencia enumera más de treinta eventos, y la lista crece con cada versión. La mayoría de las configuraciones reales usan un puñado:

| Evento | Cuándo se dispara | ¿Puede bloquear? |
| --- | --- | --- |
| `PreToolUse` | Antes de ejecutar una llamada a una herramienta | Sí, bloquea la llamada |
| `PostToolUse` | Después de que una llamada a una herramienta termina bien | No, la herramienta ya se ejecutó; stderr llega a Claude |
| `UserPromptSubmit` | Cuando envías un prompt | Sí, el prompt nunca llega a Claude |
| `Notification` | Cuando Claude Code envía una notificación, como una petición de permiso o una espera por inactividad | No |
| `Stop` | Cuando Claude termina su respuesta | Sí, Claude sigue trabajando |
| `SubagentStart` / `SubagentStop` | Cuando un subagente empieza o termina | Solo `SubagentStop` |
| `SessionStart` | Al arrancar, reanudar, con `/clear` o al compactar | No, pero stdout pasa a ser contexto |
| `PreCompact` | Antes de compactar el contexto | Sí, bloquea la compactación |
| `PermissionRequest` | Cuando está a punto de mostrarse un diálogo de permisos | Solo mediante JSON, no con exit 2 |
| `SessionEnd` | Cuando termina la sesión | No |

`PreToolUse` y `PostToolUse` cubren la mayoría de las necesidades. `Notification` y `Stop` son los de «avísame cuando termine o se atasque».

## Cómo se configuran los hooks en settings.json

Los hooks viven en los mismos archivos de ajustes que los permisos:

| Archivo | Alcance | ¿Se comparte con git? |
| --- | --- | --- |
| `~/.claude/settings.json` | Todos tus proyectos | No |
| `.claude/settings.json` | Un proyecto | Sí |
| `.claude/settings.local.json` | Un proyecto, solo tú | No (en gitignore) |
| Ajustes de política gestionada | Toda la organización | Los fijan los administradores |

Los plugins pueden incluir hooks en `hooks/hooks.json`, y las skills y los subagentes pueden definir hooks en su frontmatter.

La estructura tiene tres niveles: el evento, un grupo de matcher y una lista de handlers.

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

El `type` de un handler puede ser `command` (un comando de shell), `http` (un POST a una URL), `mcp_tool` (una herramienta de un servidor MCP que hayas conectado), `prompt` (una comprobación rápida de sí/no hecha por un modelo) o `agent` (una comprobación experimental con un subagente). El tiempo de espera por defecto es de 600 segundos para los handlers de comando, HTTP y herramienta MCP, de 30 segundos para los hooks de prompt y de 60 para los de agente.

Cada hook recibe JSON por stdin. Los campos comunes son `session_id`, `transcript_path`, `cwd`, `permission_mode` y `hook_event_name`. Los eventos de herramienta añaden `tool_name`, `tool_input` y `tool_use_id`. Tu script lee ese JSON, normalmente con `jq`, y decide qué hacer. La variable de entorno `CLAUDE_PROJECT_DIR` apunta a la raíz del proyecto, útil para referenciar scripts guardados en el repositorio.

Puedes escribir `/hooks` dentro de Claude Code para ver qué hay cargado y de qué archivo viene cada hook. El navegador es de solo lectura, así que los hooks se cambian editando el JSON.

## Matchers: qué llamadas a herramientas ve un hook

El `matcher` decide qué llamadas activan un hook. En los eventos de herramienta, coincide con el nombre de la herramienta:

| Matcher | Significado |
| --- | --- |
| `"*"`, `""` o sin matcher | Todas las llamadas |
| `"Bash"` | Exactamente la herramienta Bash |
| `"Edit\|Write"` | Edit o Write (una lista) |
| `"mcp__memory__.*"` | Cualquier herramienta del servidor MCP `memory` |
| `"mcp__.*__write.*"` | Cualquier herramienta MCP cuyo nombre empiece por `write` |

La regla es sencilla. Si el matcher solo tiene letras, dígitos, `_`, `-`, espacios, comas o `|`, es un nombre exacto o una lista de nombres. Cualquier otro carácter lo convierte en una expresión regular de JavaScript. Las herramientas MCP se llaman `mcp__<servidor>__<herramienta>`.

Algunos eventos ignoran el matcher, entre ellos `UserPromptSubmit` y `Stop`, porque no hay nada con lo que comparar. Otros comparan algo distinto del nombre de una herramienta. `SessionStart` compara cómo empezó la sesión (`startup`, `resume`, `clear`, `compact`), y `Notification` compara el tipo de notificación, como `permission_prompt` o `idle_prompt`.

Los eventos de herramienta también aceptan un campo `if` con la sintaxis de las reglas de permisos, de modo que un hook de Bash se ejecute solo para, por ejemplo, `"Bash(git *)"`. La documentación lo describe como un filtro de mejor esfuerzo, así que deja la comprobación real en tu script.

## Códigos de salida y JSON

Un hook de tipo comando se comunica con Claude Code de dos maneras: el código de salida y, opcionalmente, JSON por stdout.

| Código de salida | Qué ocurre |
| --- | --- |
| `0` | Éxito. stdout se interpreta como JSON si es un único objeto JSON. En `UserPromptSubmit` y `SessionStart`, el texto plano de stdout se añade al contexto de Claude. |
| `2` | Error bloqueante. stderr es el motivo. En `PreToolUse` se cancela la llamada, en `UserPromptSubmit` se rechaza el prompt, en `Stop` Claude sigue. |
| Cualquier otro, incluido `1` | Error no bloqueante. Claude Code lo anota y continúa. |

La trampa aquí es el código de salida 1. Muchos scripts fallan con 1 por defecto, y eso **no** bloquea nada. Si quieres decir «para», sal con 2.

Para un control más fino, sal con 0 e imprime JSON. Un hook `PreToolUse` puede permitir, denegar o preguntar:

```json
{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Use rg instead of grep for better performance"
  }
}
```

`permissionDecision` admite `allow`, `deny`, `ask` o `defer`. Un hook también puede reescribir la llamada con `updatedInput`, o pasarle texto extra a Claude con `additionalContext`. Los campos que funcionan en cualquier evento incluyen `continue: false` (detiene a Claude por completo, con un `stopReason`) y `systemMessage` (un aviso que te muestra a ti).

Cuando coinciden varios hooks, se ejecutan en paralelo y gana la respuesta más restrictiva: deny gana a defer, defer gana a ask, ask gana a allow. Un `allow` de un hook no anula una regla deny de tus permisos.

## Ejemplo 1: formatear archivos después de cada edición

Es el hook con el que empieza la mayoría. Ejecuta Prettier sobre cualquier archivo que Claude escriba o edite:

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

Cambia por `ruff format`, `gofmt -w` o `rustfmt` para otros lenguajes. Como el formateador se ejecuta después de la edición, Claude ve el archivo ya formateado la próxima vez que lo lee, y dejas de ver diffs de puro estilo en la revisión.

## Ejemplo 2: bloquear comandos peligrosos y archivos protegidos

Un hook `PreToolUse` es el sitio para los límites estrictos. Este script, adaptado de la guía oficial, bloquea las ediciones en archivos sensibles. Guárdalo como `.claude/hooks/protect-files.sh`:

```bash
#!/bin/bash
INPUT=$(cat)
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')

PROTECTED_PATTERNS=(".env" "package-lock.json" ".git/")

for pattern in "${PROTECTED_PATTERNS[@]}"; do
  if [[ "$FILE_PATH" == *"$pattern"* ]]; then
    echo "Blocked: $FILE_PATH matches protected pattern '$pattern'" >&2
    exit 2
  fi
done

exit 0
```

Hazlo ejecutable con `chmod +x .claude/hooks/protect-files.sh` y regístralo:

```json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-files.sh"
          }
        ]
      }
    ]
  }
}
```

El mismo patrón sirve para comandos de shell. Haz match con `Bash`, lee `.tool_input.command` y sal con 2 cuando contenga algo que nunca quieres, como `rm -rf /`, `git push --force` o `DROP TABLE`. Ten en cuenta que una comprobación de cadenas es fácil de esquivar, así que trátala como un cinturón de seguridad, no como un cerrojo.

## Ejemplo 3: recibir una notificación de escritorio

Las tareas largas se detienen a esperarte más a menudo de lo que te gustaría. Un hook `Notification` te avisa al instante. En macOS:

```json
{
  "hooks": {
    "Notification": [
      {
        "matcher": "",
        "hooks": [
          {
            "type": "command",
            "command": "osascript -e 'display notification \"Claude Code needs your attention\" with title \"Claude Code\"'"
          }
        ]
      }
    ]
  }
}
```

En Linux, usa en su lugar `notify-send 'Claude Code' 'Claude Code needs your attention'`. Pon el matcher en `permission_prompt` si solo te importan las peticiones de permiso, o añade un hook `Stop` con el mismo comando para enterarte cuando Claude termine.

## Más patrones útiles

- **Reinyectar contexto tras la compactación.** Un hook `SessionStart` con el matcher `compact` puede hacer `echo` de un recordatorio corto de tus reglas. Su stdout se añade al contexto nuevo.
- **Auditar cambios de configuración.** Un hook `ConfigChange` puede añadir cada cambio de ajustes a un archivo de log.
- **Control de calidad en Stop.** Un hook `Stop` puede ejecutar tus tests y salir con 2 con los fallos, para que Claude siga corrigiendo. Comprueba el campo `stop_hook_active` de la entrada para evitar un bucle infinito. Claude Code también anula un hook Stop tras varios bloqueos seguidos.
- **Autoaprobar un prompt seguro.** Un hook `PermissionRequest` puede responder a un diálogo concreto con `{"behavior": "allow"}` dentro de su objeto de decisión. Mantén el matcher estrecho.

## Notas de seguridad antes de añadir hooks

Los hooks se ejecutan como comandos de shell con los permisos de tu usuario, sin preguntar. Ese es el objetivo, y también el riesgo.

- **Lee los hooks antes de fiarte de un repositorio.** El `.claude/settings.json` de un proyecto puede incluir hooks. Revísalos como revisarías un script `postinstall`.
- **Pon comillas a tus variables.** La entrada del hook viene del modelo, y el modelo lee archivos y páginas web que pueden contener [inyección de prompts](/es/glossary/prompt-injection/ "Inyección de prompts"). Usa `"$VAR"`, nunca hagas eval de la entrada y prefiere `jq` a un análisis hecho a mano.
- **Usa rutas absolutas o relativas al proyecto** con `$CLAUDE_PROJECT_DIR`, para que un hook no dependa de la carpeta actual.
- **Mantén los hooks rápidos.** Un hook `PreToolUse` lento ralentiza cada llamada a una herramienta. Define un `timeout`.
- **Los hooks no son un sandbox.** Un hook deny bloquea llamadas incluso con `--dangerously-skip-permissions`, lo cual es útil, pero un agente decidido o confundido puede llegar al mismo resultado por otro camino. Combina los hooks con reglas deny y aislamiento, como se explica en [seguridad de los agentes de programación con IA](/es/blog/ai-coding-agent-security/ "Seguridad de los agentes de programación con IA") y [saltarse los permisos con seguridad](/es/blog/claude-code-skip-permissions/ "Claude Code --dangerously-skip-permissions").
- **Conoce los interruptores de apagado.** `"disableAllHooks": true` desactiva los hooks, y los administradores pueden definir `allowManagedHooksOnly` en los ajustes gestionados para bloquear los hooks de usuario y de proyecto.

Cuando un hook se porta mal, inicia Claude Code con `claude --debug`, o escribe el log en un archivo con `--debug-file /tmp/claude.log`, para ver el matcher, el código de salida y la salida de cada hook.

## Cómo usa AgentVera los hooks

AgentVera es una app de escritorio que ejecuta Claude Code, Codex, Gemini CLI y otras CLI de programación lado a lado. Los hooks son una parte importante de cómo trabaja con Claude Code.

- **Tus hooks siguen siendo tuyos.** AgentVera pasa sus propios hooks a cada proceso de Claude Code con el flag `--settings`. Claude Code los combina con los hooks de tus archivos de ajustes, y tu `settings.json` no se edita.
- **El estado de los agentes viene de los hooks.** El estado que muestra cada agente en la app se lee de los propios eventos de hook de la CLI, no se adivina a partir del texto de la terminal.
- **Las smart tools son hooks.** La función de [smart tools](/es/features/smart-tools/ "Smart tools") añade cuatro hooks a los agentes locales de Claude Code: bloquea las lecturas repetidas del mismo archivo, ejecuta solo los tests que afecta una edición, comprueba errores justo después de una edición y comparte resultados entre agentes. Están activadas por defecto y puedes desactivarlas en Settings → “Token saving”.
- **No hace falta un script de avisos.** [Notificaciones y aprobaciones](/es/features/notifications-and-approvals/ "Notificaciones y aprobaciones") reúne en una sola cola a todos los agentes que esperan aprobación, envía notificaciones de escritorio mientras la app está en segundo plano y puede avisar a tu teléfono por Telegram.

Qwen Code también recibe los hooks de AgentVera, porque su sistema de hooks sigue los eventos y el formato JSON de Claude Code.

## Para terminar

Empieza con un formateador `PostToolUse` y una barrera `PreToolUse`, compruébalos con `/hooks` y `--debug`, y añade más solo cuando una regla se siga incumpliendo. Sal con 2 para bloquear, imprime JSON cuando necesites más control y trata cada hook como código que se ejecuta con tus permisos.

Para seguir leyendo:

- [Hooks de Claude Code (glosario)](/es/glossary/claude-code-hooks/ "Hooks de Claude Code")
- [Smart tools](/es/features/smart-tools/ "Smart tools")
- [Notificaciones y aprobaciones](/es/features/notifications-and-approvals/ "Notificaciones y aprobaciones")
- [Agente Claude Code](/es/agents/claude-code/ "Claude Code en AgentVera")

Para probar AgentVera, empieza en la [página de inicio](/es/ "AgentVera").

## Preguntas frecuentes

**¿Qué son los hooks de Claude Code?** Los hooks son comandos de shell, llamadas HTTP, herramientas MCP o prompts que Claude Code ejecuta por sí mismo en puntos concretos de una sesión, como antes de llamar a una herramienta, después de editar un archivo o cuando Claude necesita tu atención. Los defines en settings.json.

**¿Dónde pongo los hooks de Claude Code?** En la clave hooks de un archivo de ajustes: ~/.claude/settings.json para todos tus proyectos, .claude/settings.json para un proyecto que compartes con git o .claude/settings.local.json para un proyecto solo en tu máquina. Los plugins, las skills y los subagentes también pueden definir hooks.

**¿Cómo bloquea un hook una llamada a una herramienta?** Un hook PreToolUse sale con código 2 y escribe el motivo en stderr, o sale con 0 e imprime JSON con permissionDecision en deny. Claude Code cancela la llamada y le muestra el motivo a Claude. El código de salida 1 no bloquea.

**¿Se ejecutan los hooks con --dangerously-skip-permissions?** Sí. Un hook que devuelve deny bloquea la llamada a la herramienta incluso en modo bypassPermissions. Eso convierte a un hook PreToolUse en una última barrera útil para comandos que nunca quieres, aunque no es un sandbox.

**¿Cómo depuro un hook de Claude Code que no se dispara?** Abre /hooks para ver qué hooks están cargados y de dónde vienen, comprueba que el matcher coincide con el nombre exacto de la herramienta e inicia Claude Code con --debug o --debug-file para ver cada ejecución de hook, su código de salida y su salida.

---

**Sobre AgentVera.** AgentVera es una app de escritorio para macOS, Windows y Linux, desarrollada por Solviera Teknoloji. Ejecuta 18 agentes de programación con IA (Claude Code, Codex, Gemini CLI, Copilot, Cursor, Antigravity, opencode, Hermes, Qwen Code, Kimi Code, Kiro, Amp, Droid, Goose, Vibe, Crush, Aider, Cline) en paralelo, por proyecto, en terminales reales con las instalaciones y sesiones de CLI del propio usuario. El plan gratuito tiene agentes ilimitados. Descargar: https://agentvera.dev/es/download/ · Precios: https://agentvera.dev/es/pricing/
