Los hooks de Claude Code son acciones, normalmente comandos, que Claude Code ejecuta automáticamente en momentos concretos de la sesión, como antes de editar un archivo o al terminar una respuesta, sin depender de que se lo pidas. Los subagentes de Claude Code son asistentes especializados que trabajan en su propia ventana de contexto y devuelven solo el resultado.
En un proyecto WordPress, la combinación sirve para tres cosas muy concretas:
- Que Claude no pueda tocar wp-config.php ni la base de datos de producción.
- Que todo el PHP que escriba pase el estándar de WordPress.
- Que las tareas pesadas no ensucien tu conversación principal.
En esta guía tienes la configuración que usamos en Doowebs, con los JSON y scripts listos para copiar.
Si aún no trabajas con Claude Code a diario, empieza por cómo usar Claude Code en Cursor y vuelve aquí.
¿Qué son los Claude Code hooks?
Un hook de Claude Code es una regla del tipo «cuando ocurra X, ejecuta Y» que vive en un archivo de configuración. Se evalúa en cada evento, independientemente de lo que haya en el prompt o de lo larga que sea la conversación.
Un hook se ejecuta siempre; un prompt, no siempre
Cuando le pides a Claude en el chat «pasa siempre el linter después de editar», esa instrucción compite con todo lo demás que hay en el contexto y puede olvidarse en una sesión larga. Un hook no. Lo ejecuta el propio programa en cuanto se cumple el evento. Por eso conviene convertir en hook todo lo que tiene que ocurrir sin excepciones y dejar en el prompt o en el CLAUDE.md lo que depende del contexto.
Claude Code hooks vs. hooks de WordPress
Si vienes de WordPress, la palabra hook ya significa otra cosa. Conviene separarlas desde el principio:
| Hooks de WordPress | Claude Code hooks | |
| Qué son | Puntos de extensión que definen el núcleo, los temas y los plugins (do_action, apply_filters), a los que te enganchas con add_action y add_filter | Comandos que Claude Code lanza en eventos de su propia sesión |
| Dónde viven | En PHP: functions.php del tema, plugins | En JSON: settings.json de Claude Code |
| Cuándo se ejecutan | Mientras WordPress procesa una petición | Mientras Claude Code trabaja en tu proyecto (antes o después de editar, al terminar, al compactar…) |
| Para qué sirven | Modificar el comportamiento de la web | Controlar y automatizar lo que hace la IA en tu código |
Los dos conceptos conviven perfectamente: puedes tener un hook de Claude Code que valide los add_action que Claude escribe en tu tema hijo.
Cómo funcionan los hooks de Claude Code
Todo hook tiene tres piezas en el mismo bloque JSON: el evento que lo dispara, el matcher que filtra a qué llamadas responde y la acción que se ejecuta.
Evento, matcher y acción
- Evento: el momento en que Claude Code comprueba si hay algo registrado, por ejemplo PreToolUse (antes de usar una herramienta) o Stop (al terminar de responder).
- Matcher: en los eventos de herramienta se compara con el nombre de la herramienta. Edit|Write responde solo a ediciones de archivo y Bash solo a comandos de terminal. Si lo dejas vacío, salta siempre. Distingue mayúsculas: bash no coincide con Bash.
- Acción: lo que se ejecuta, normalmente un comando o script de shell.
Los eventos más útiles en un proyecto WordPress
Claude Code expone más de 30 eventos; la lista completa está en la referencia oficial de hooks.
Estos son los que más se utilizan en WordPress:
| Evento | Cuándo salta | Uso típico en WordPress |
| PreToolUse | Antes de que Claude use una herramienta | Bloquear ediciones de wp-config.php o comandos WP-CLI destructivos |
| PostToolUse | Después de que la herramienta termina | Pasar php -l y PHPCS al PHP recién editado |
| UserPromptSubmit | Al enviar un prompt | Añadir contexto dinámico (rama de git, entorno) |
| SessionStart | Al iniciar o reanudar sesión, y tras compactar | Recordar versión de WordPress, PHP y tema activo |
| Notification | Cuando Claude te pide permiso o se queda esperando | Aviso de escritorio |
| Stop | Cuando Claude termina de responder | Comprobar que los tests pasan antes de dar el trabajo por cerrado |
| SubagentStart / SubagentStop | Cuando un subagente arranca o termina | Preparar o limpiar recursos de ese subagente |
Los 5 tipos de hook
La acción no tiene por qué ser un comando de shell:
- command: ejecuta un comando o script. Es el más habitual y el que usaremos aquí.
- http: envía los datos del evento por POST a una URL (útil para Slack o Make).
- mcp_tool: llama a una herramienta de un servidor MCP. Si ya conectas WordPress por MCP, mira nuestra guía de MCP en WordPress.
- prompt: delega una decisión de una sola vuelta en un modelo.
- agent: lanza un subagente con herramientas para verificar una condición. Es experimental; para algo crítico, usa command.
Cómo se comunica un hook con Claude: stdin, exit code y stderr
Al dispararse, Claude Code envía al hook un JSON por la entrada estándar con el contexto del evento. La herramienta, el archivo o el comando. El hook responde con su código de salida:
- Exit 0: todo correcto, la acción continúa.
- Exit 2: error bloqueante. En los eventos que admiten bloqueo, la acción se frena y el texto que escribas en stderr le llega a Claude para que corrija o cambie de enfoque.
- Otro código: error no bloqueante; se muestra pero Claude sigue.
En SessionStart, lo que el comando escribe en stdout se añade al contexto de Claude.
Dónde se configuran los hooks
| Archivo | Alcance | ¿Se sube a git? |
| ~/.claude/settings.json | Todos tus proyectos | No, es personal |
| .claude/settings.json | Este proyecto | Sí, lo comparte todo el equipo |
| .claude/settings.local.json | Este proyecto, solo tu máquina | No, se ignora por defecto |
En un proyecto WordPress de agencia, las reglas de seguridad y calidad van en .claude/settings.json para que las tenga cualquier desarrollador que clone el repositorio. Los avisos de escritorio, en tu settings.json personal.
Hooks de Claude Code listos para un proyecto WordPress
Todos los scripts van en .claude/hooks/ dentro del proyecto, necesitan permisos de ejecución (chmod +x .claude/hooks/*.sh) y usan jq para leer el JSON de entrada. En Windows, ejecútalos desde WSL o Git Bash. Ojo: en Windows nativo sin Git for Windows, Claude Code usa la herramienta PowerShell en lugar de Bash, así que los hooks con matcher Bash no saltan y no te protegen. Recuerda que un hook se ejecuta con tus permisos y sin pedir confirmación. Te recomiendo que los leas antes de pegarlos.
¿Cómo bloquear ediciones de wp-config.php, .htaccess y uploads?
Antes de cada edición comprueba la ruta y frena la operación si coincide con un archivo protegido. Claude recibe el motivo y te pedirá que hagas tú el cambio. Encaja con el resto de medidas de seguridad en WordPress.
.claude/hooks/protect-wp.sh:
#!/bin/bash
FILE_PATH=$(jq -r '.tool_input.file_path // empty')
PROTECTED=("wp-config.php" ".htaccess" "wp-content/uploads/" ".env" ".git/")
for pattern in "${PROTECTED[@]}"; do
if [[ "$FILE_PATH" == *"$pattern"* ]]; then
echo "Bloqueado: $FILE_PATH está protegido ($pattern). Propón el cambio y lo aplica una persona." >&2
exit 2
fi
done
exit 0
Si necesitas tocar el wp-config.php de forma controlada, repasa antes cómo optimizar WordPress desde wp-config.php.
Validar el PHP con php -l y PHPCS
Después de cada edición, si el archivo es PHP, el hook comprueba la sintaxis y, si tienes instalado WordPress Coding Standards, pasa PHPCS. Si algo falla, devuelve el error a Claude con exit 2 para que lo corrija en el siguiente paso.
.claude/hooks/php-quality.sh:
#!/bin/bash
FILE_PATH=$(jq -r '.tool_input.file_path // empty')
[[ "$FILE_PATH" == *.php ]] || exit 0
if ! php -l "$FILE_PATH" > /dev/null 2>&1; then
php -l "$FILE_PATH" >&2
exit 2
fi
if command -v phpcs > /dev/null; then
phpcs --standard=WordPress --warning-severity=0 -q "$FILE_PATH" 1>&2 || exit 2
fi
exit 0
–warning-severity=0 hace que solo bloqueen los errores, no los avisos; quítalo si quieres un estándar más estricto.
¿Cómo frenar comandos WP-CLI destructivos?
WP-CLI es potentísimo y por eso peligroso en manos de un agente. Este hook bloquea el reseteo o borrado de la base de datos y obliga a que cualquier wp search-replace se lance primero con –dry-run. La ejecución real la lanzas tú, después de revisar el resultado.
.claude/hooks/guard-wpcli.sh:
#!/bin/bash
CMD=$(jq -r '.tool_input.command // empty')
if echo "$CMD" | grep -qE 'wp (db (reset|drop|import)|site empty)'; then
echo "Bloqueado: operación destructiva de WP-CLI. Pídesela a una persona." >&2
exit 2
fi
if echo "$CMD" | grep -q 'wp search-replace' && ! echo "$CMD" | grep -q -- '--dry-run'; then
echo "Lanza wp search-replace con --dry-run y enseña el resultado. La ejecución real la hace una persona." >&2
exit 2
fi
exit 0
¿Quieres recuperar el contexto del proyecto al compactar?
Cuando la conversación se llena, Claude Code la resume y pierde detalles. Un hook SessionStart con matcher compact vuelve a inyectar lo esencial justo después.
.claude/hooks/wp-context.sh:
#!/bin/bash
echo "Proyecto WordPress $(wp core version 2>/dev/null) con PHP $(php -r 'echo PHP_VERSION;')."
echo "Tema activo: $(wp theme list --status=active --field=name 2>/dev/null). Los cambios de plantilla van en el tema hijo, nunca en el padre."
Si el contexto es fijo y no cambia entre sesiones, ponlo en el CLAUDE.md del proyecto en lugar de en un hook.
Comprobar los tests antes de cerrar el turno
Un hook Stop que sale con exit 2 obliga a Claude a seguir trabajando. Es útil para que no dé por terminada una tarea con tests rotos, pero tiene una trampa: si no compruebas el campo stop_hook_active, Claude puede entrar en bucle. El script lo comprueba primero.
.claude/hooks/tests-on-stop.sh:
#!/bin/bash
INPUT=$(cat)
[ "$(echo "$INPUT" | jq -r '.stop_hook_active')" = "true" ] && exit 0
jq -e '.scripts.test' composer.json > /dev/null 2>&1 || exit 0
if ! OUT=$(composer test 2>&1); then
echo "Los tests fallan. Corrígelos antes de terminar:" >&2
echo "$OUT" | tail -n 30 >&2
exit 2
fi
exit 0
Si tu composer.json no tiene un script test, el hook no hace nada. Ten en cuenta que Stop salta en cada respuesta: si tu suite tarda mucho, ejecuta solo los tests rápidos.
La configuración completa en .claude/settings.json
Este bloque registra los cinco hooks anteriores. Va al primer nivel del archivo; si ya tienes un bloque hooks, añade los eventos sin borrar los existentes.
{
"hooks": {
"PreToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "bash \"$CLAUDE_PROJECT_DIR\"/.claude/hooks/protect-wp.sh"
}
]
},
{
"matcher": "Bash",
"hooks": [
{ "type": "command", "command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/guard-wpcli.sh" }
]
}
],
"PostToolUse": [
{
"matcher": "Edit|Write",
"hooks": [
{ "type": "command", "command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/php-quality.sh" }
]
}
],
"SessionStart": [
{
"matcher": "compact",
"hooks": [
{ "type": "command", "command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/wp-context.sh" }
]
}
],
"Stop": [
{
"hooks": [
{ "type": "command", "command": "bash $CLAUDE_PROJECT_DIR/.claude/hooks/tests-on-stop.sh" }
]
}
]
}
}
Para comprobar que se han cargado, escribe /hooks en la sesión: verás cada evento con un contador. El listado es de solo lectura; los cambios se hacen en el JSON.
¿Qué son los subagentes en Claude Code?
Un subagente es un asistente de IA especializado que trabaja en su propia ventana de contexto, con su propio prompt de sistema, sus herramientas y sus permisos, y que al terminar devuelve solo un resumen a la conversación principal.
Contexto, herramientas y permisos propios
Si le pides a Claude que revise los 40 archivos PHP de un tema, todo ese contenido entra en tu conversación y se queda ahí aunque no lo vuelvas a necesitar. Un subagente hace esa lectura en su propio contexto y solo te devuelve el resultado, así que la sesión principal sigue ligera y Claude no pierde el hilo. Además puedes limitarle las herramientas (solo lectura, por ejemplo) y asignarle un modelo más barato para tareas sencillas.
Ojo con el coste: cada subagente hace sus propias peticiones al modelo, que cuentan para los mismos límites de uso que tu conversación. Si te preocupa el consumo, revisa nuestra guía de optimización de tokens con Claude y Cursor.
¿Qué subagentes trae Claude Code?
- Explore: rápido y de solo lectura, para buscar y entender una base de código.
- Plan: investiga el código en modo plan antes de proponerte un plan.
- General-purpose: para tareas complejas de varios pasos que exigen explorar y modificar.
Claude los lanza solo cuando los necesita, probablemente ya los has usado sin saberlo.
¿Cuándo no debes usar subagentes?
No compensan en tareas de ida y vuelta donde cada paso depende del anterior ni en cambios rápidos y concretos: el subagente arranca sin conocer tu conversación y reunir ese contexto le cuesta más de lo que ahorra.
¿Cómo crear un subagente para WordPress?
Un subagente es un archivo Markdown con una cabecera YAML y debajo sus instrucciones. Puede vivir en .claude/agents/ o en ~/.claude/agents/ (todos tus proyectos). Solo name y description son obligatorios.
Desde Claude Code v2.1.198, el comando /agents ya no abre un asistente interactivo. Para crear uno, pídeselo a Claude en lenguaje natural o escribe el archivo a mano.
Ejemplo 1: revisor de seguridad de WordPress
.claude/agents/wp-security-reviewer.md:
---
name: wp-security-reviewer
description: Revisa temas y plugins de WordPress en busca de vulnerabilidades. Úsalo de forma proactiva cuando se cree o modifique código PHP de un tema o plugin.
tools: Read, Grep, Glob
model: sonnet
---
Eres un auditor de seguridad de WordPress. Revisa el código indicado y busca:
- Entradas sin sanitizar (falta de sanitize_text_field, absint, etc.).
- Salidas sin escapar (esc_html, esc_attr, esc_url, wp_kses).
- Formularios y acciones AJAX sin nonce ni comprobación de capacidades.
- Consultas SQL sin $wpdb->prepare().
- Archivos PHP accesibles directamente sin comprobar ABSPATH.
Devuelve una tabla con archivo, línea, problema, severidad (crítica, alta, media) y la corrección propuesta. No modifiques ningún archivo.
Al limitar tools a Read, Grep y Glob, el subagente no puede editar nada aunque quiera. Sus hallazgos complementan los plugins y trucos de seguridad para WordPress.
¿Cómo invocar un subagente?
- Lenguaje natural: «usa el subagente wp-security-reviewer para revisar el plugin de reservas». Claude normalmente delega, pero es una sugerencia.
- Mención con @: escribe @ y elígelo de la lista, o teclea @agent-wp-security-reviewer. Garantiza que se ejecuta ese subagente.
- Toda la sesión: claude –agent wp-security-reviewer arranca la sesión con las instrucciones, herramientas y modelo del subagente.
Claude decide cuándo delegar leyendo la description. Escríbela con los casos concretos en los que debe activarse y no con una definición abstracta. Mantenlas breves: si las descripciones de tus subagentes suman más de 15.000 tokens, Claude Code muestra una advertencia al arrancar.
¿Cómo trabajan por dentro los subagentes?
- En sesiones interactivas, los subagentes se ejecutan en segundo plano por defecto, con un conjunto de herramientas integradas más reducido.
- Un subagente puede lanzar sus propios subagentes hasta tres niveles de profundidad (variable CLAUDE_CODE_MAX_SUBAGENT_SPAWN_DEPTH).
- Hay un límite de 20 subagentes concurrentes por sesión (variable CLAUDE_CODE_MAX_CONCURRENT_SUBAGENTS).
- Con isolation: worktree en el frontmatter, el subagente trabaja en una copia aislada del repositorio: útil para probar la actualización de un plugin sin tocar tu rama.
El detalle de todos los campos está en la documentación oficial de subagentes.
¿Se pueden crear hooks dentro de subagentes?
Un subagente puede declarar sus propios hooks en el frontmatter, que solo se ejecutan mientras ese subagente está activo. Así puedes darle acceso a una herramienta potente pero con reglas propias.
Ejemplo 2: subagente que solo puede leer la base de datos
Queremos un subagente que responda preguntas sobre los datos de WordPress (pedidos, usuarios, metadatos) usando wp db query, pero que no pueda modificar nada.
.claude/agents/wp-db-reader.md:
---
name: wp-db-reader
description: Ejecuta consultas SELECT de solo lectura sobre la base de datos de WordPress con WP-CLI. Úsalo para analizar datos o generar informes.
tools: Bash
hooks:
PreToolUse:
- matcher: "Bash"
hooks:
- type: command
command: "./.claude/hooks/wp-readonly-sql.sh"
---
Eres un analista de datos de WordPress con acceso de solo lectura. Usa únicamente `wp db query` con consultas SELECT y respeta el prefijo de tablas del proyecto. Si te piden modificar datos, explica que solo tienes acceso de lectura.
.claude/hooks/wp-readonly-sql.sh:
#!/bin/bash
CMD=$(jq -r '.tool_input.command // empty')
if ! echo "$CMD" | grep -qE '^wp db query'; then
echo "Bloqueado: este subagente solo puede usar wp db query." >&2
exit 2
fi
if echo "$CMD" | grep -iqE '(^|[^a-z])(insert|update|delete|drop|create|alter|truncate|replace)([^a-z]|$)'; then
echo "Bloqueado: solo se permiten consultas SELECT." >&2
exit 2
fi
exit 0
Si pides al subagente un UPDATE, el script sale con código 2, Claude Code bloquea el comando y el subagente recibe el motivo. Si trabajas con tablas personalizadas en WordPress, este subagente es una forma segura de explorarlas. Es una capa de protección, no un blindaje: en producción, conéctalo además con un usuario MySQL de solo lectura.
SubagentStart, SubagentStop y el evento Stop
- Un hook Stop definido en el frontmatter de un subagente se convierte automáticamente en SubagentStop.
- En settings.json puedes usar SubagentStart y SubagentStop con el nombre del subagente como matcher, por ejemplo para registrar cuándo se ejecutó wp-db-reader:
{
"hooks": {
"SubagentStop": [
{
"matcher": "wp-db-reader",
"hooks": [
{ "type": "command", "command": "echo \"$(date) wp-db-reader terminado\" >> ~/.claude/subagents.log" }
]
}
]
}
}
Los hooks de settings.json (como protect-wp.sh) también se aplican dentro de los subagentes, así que la protección de wp-config.php sigue activa aunque delegues.
¿Por qué tu hook de subagente no salta?
Los hooks del frontmatter de un subagente de proyecto solo se ejecutan si has aceptado el diálogo de confianza de la carpeta que contiene el archivo del agente. Hasta entonces, el subagente funciona pero sin sus hooks.
Los subagentes que vienen de plugins ignoran directamente los campos hooks, mcpServers y permissionMode: si los necesitas, copia el archivo a .claude/agents/.
Hook, subagente, skill o CLAUDE.md ¿cuál usar?
| Necesitas… | Usa |
| Que algo ocurra siempre, sin excepciones (bloquear, formatear, validar) | Hook |
| Sacar una tarea pesada de tu conversación y recibir solo el resumen | Subagente |
| Un procedimiento reutilizable que Claude carga cuando viene a cuento | Skill |
| Normas fijas del proyecto que Claude debe conocer en cada sesión | CLAUDE.md |
| Una herramienta potente con reglas propias (Bash solo para SELECT) | Subagente + hook en su frontmatter |
Las skills merecen su propio artículo: lo tienes en Claude Skills para diseño web profesional.
¿Cómo depurar los errores frecuentes?
El hook no aparece en /hooks. Claude Code no ha llegado a leerlo. Revisa:
- JSON inválido: las comas finales y los comentarios no están permitidos, y un solo error impide cargar todos los hooks.
- El bloque está en el settings.json de otro proyecto.
- hooks está anidado dentro de otra clave en lugar de al primer nivel.
Aparece pero no hace nada. Prueba el script a mano pasándole un JSON de ejemplo y mira el código de salida:
echo '{"tool_name":"Edit","tool_input":{"file_path":"wp-config.php"}}' | bash .claude/hooks/protect-wp.sh
echo $?
Si devuelve 2 y el mensaje de bloqueo, el script funciona. Otros fallos habituales: falta el chmod +x, el matcher está mal escrito o el comando no está en el PATH (usa rutas absolutas o $CLAUDE_PROJECT_DIR).
Necesitas más detalle. Arranca con claude –debug-file /tmp/claude.log para tener el registro completo, o pulsa Ctrl+O para ver en la transcripción qué hooks saltaron.
El subagente nunca se lanza solo. La description es demasiado vaga. Reescríbela con los casos concretos de uso o invócalo con @agent-nombre.
Desarrollo WordPress con IA, con red de seguridad
Con cinco hooks y dos subagentes, Claude Code deja de ser un asistente que puede romper tu instalación y se convierte en un desarrollador que respeta las normas del proyecto: no toca la configuración ni la base de datos, escribe PHP que pasa el estándar de WordPress y delega lo pesado sin perder el hilo.
En Doowebs aplicamos este flujo en los proyectos de programación web a medida. Si quieres un desarrollo WordPress con IA aplicada y control de calidad desde el primer commit, hablemos.