Hooks en un plugin de Claude Code: comprobar el repo al abrir la sesión con SessionStart
Hola, bienvenido. En esta lección vamos a añadir al plugin team su primer hook. Se ejecuta al abrir cada sesión de Claude Code y comprueba que el repo cumple el contrato del equipo: los scripts de Composer, las líneas que tienen que estar en .gitignore y la versión del plugin que el repo pide. Si falta algo, lo dice y ofrece el comando para arreglarlo. Si todo está en orden, no muestra nada. Así, lo que el equipo ha acordado se comprueba solo, sin que nadie tenga que acordarse.
El contrato que fijamos al preparar el proyecto solo sirve si todos los repos lo cumplen, y nadie va a revisarlo a mano cada mañana. En el equipo de Marta, cuando a un repo le faltaba algo, se descubría tarde: un composer pint:test que no existía, un settings.local.json que alguien había subido sin querer o un compañero con una versión vieja de la configuración. Un hook SessionStart lo detecta en el momento en que alguien abre Claude en ese repo, antes de que el problema afecte al trabajo de nadie.
Qué es un hook y qué eventos tiene lo tienes en la lección Qué son los hooks de Claude Code y cuándo usarlos (se abre en una pestaña nueva) del curso de Claude Code. Lo que cambia aquí es que el hook va en el plugin y no en el repo, y eso tiene sus reglas. Además de Claude Code, necesitas jq, que es lo que usan los scripts del plugin para leer JSON.
Compruébalo con jq --version. Si no lo tienes, en Ubuntu, Debian o WSL2 se instala así, desde cualquier pestaña:
Y en macOS, con Homebrew:
Los hooks de un plugin van en hooks/hooks.json
En un repo, los hooks se declaran en .claude/settings.json. En un plugin van en hooks/hooks.json, en la raíz del plugin, con una clave hooks que tiene la misma forma que la de los settings. Cuando el plugin está activo, Claude Code junta sus hooks con los del usuario y los del proyecto. Vamos a crear las carpetas que necesitamos en esta lección, en la pestaña claude-plugins:
Y el archivo de hooks, claude-plugins/plugins/team/hooks/hooks.json:
SessionStart se ejecuta al arrancar una sesión nueva, pero también al retomar una con --resume, después de /clear y después de compactar. El matcher startup lo deja solo en las sesiones nuevas, que es cuando tiene sentido revisar el repo.
${CLAUDE_PLUGIN_ROOT} es la ruta en la que está instalado el plugin, y es versionada: cada versión instalada tiene su carpeta, así que la ruta cambia cuando el plugin se actualiza. Para lo que tenga que sobrevivir a una actualización existe ${CLAUDE_PLUGIN_DATA}, una carpeta que comparten todas las versiones del plugin y todos los repos de la máquina. En este curso no la vamos a necesitar.
Fíjate en las comillas alrededor de ${CLAUDE_PLUGIN_ROOT}. Si la ruta tuviera un espacio, sin comillas el comando se partiría en 2 y el hook no se ejecutaría. claude plugin validate lo sabe, y sin las comillas esto es lo que responde en modo estricto:
El mensaje propone también la otra forma, con args, que ejecuta el script sin pasar por la shell. Las 2 funcionan. Nosotros nos quedamos con las comillas, que se leen de un vistazo.
- 02Qué comprueba el script doctor.sh
- 03systemMessage para la persona y additionalContext para el modelo
- 04La misma comprobación como skill: /team:doctor
- 05Probar el script sin abrir Claude: tests/run.sh y sus fixtures
- 06El env del repo y la prueba en una sesión