Hooks
Los hooks son comandos definidos por ti que Theus ejecuta automáticamente en puntos concretos de su ciclo de vida: antes o después de usar una herramienta, al enviar un prompt, al iniciar o cerrar una sesión, y mucho más.
Qué son
Un hook asocia un evento (por ejemplo, "justo antes de ejecutar una herramienta") con una acción (ejecutar un comando de shell, evaluar un prompt, llamar a una URL o lanzar un subagente verificador). Cuando el evento ocurre, Theus lanza esas acciones, les entrega información del evento por stdin en formato JSON, y actúa según lo que devuelven.
Los usos típicos son: formatear código automáticamente después de cada escritura, bloquear comandos peligrosos antes de que se ejecuten, inyectar contexto adicional al enviar un prompt, o disparar notificaciones y auditorías.
Todos los hooks ejecutan comandos arbitrarios en tu máquina. Por seguridad, Theus solo ejecuta hooks en un espacio de trabajo de confianza (el diálogo de confianza debe haberse aceptado). Revisa siempre lo que configuras: un hook es código que corre con tus permisos.
El comando /hooks
Dentro de Theus, el comando slash /hooks abre un menú interactivo para revisar y gestionar la configuración de hooks por evento de herramienta, mostrando las herramientas disponibles en tu contexto actual. Es la vía recomendada para inspeccionar qué hooks están activos.
Dónde se configuran
Los hooks viven bajo la clave hooks de tu settings.json. Theus lee la configuración de varias fuentes y las combina por precedencia:
| Archivo | Ámbito |
|---|---|
~/.theus/settings.json | Usuario (global, para todos tus proyectos) |
.theus/settings.json | Proyecto (compartido, versionable en el repo) |
.theus/settings.local.json | Proyecto, local (solo tú, no se versiona) |
managed-settings.json | Gestionado por administrador (requiere permisos de admin) |
Formato de configuración
La clave hooks es un objeto donde cada clave es el nombre del evento y cada valor es una lista de matchers. Cada matcher tiene un patrón opcional (matcher) y una lista de hooks a ejecutar:
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write",
"hooks": [
{
"type": "command",
"command": "prettier --write \"$THEUS_PROJECT_DIR\""
}
]
}
]
}
}
El matcher es un patrón de texto que se compara con el valor asociado al evento (para eventos de herramienta, el nombre de la herramienta, p. ej. Write o Bash). Si se omite, el matcher aplica a todos los casos del evento.
Tipos de hook
Cada entrada dentro de hooks se distingue por su campo type:
type | Qué hace | Campos clave |
|---|---|---|
command | Ejecuta un comando de shell | command, shell, timeout, async |
prompt | Evalúa un prompt con un modelo | prompt, model, timeout |
http | Hace un POST del JSON del evento a una URL | url, headers, allowedEnvVars |
agent | Lanza un subagente verificador | prompt, model, timeout |
Campos comunes
if: filtra cuándo corre el hook usando sintaxis de reglas de permiso (p. ej."Bash(git *)"). El hook solo se lanza si la llamada de herramienta coincide con el patrón; así evitas lanzar hooks para comandos que no aplican.timeout: tiempo máximo en segundos para esa acción concreta.statusMessage: mensaje personalizado a mostrar en el spinner mientras el hook corre.once: si estrue, el hook corre una sola vez y se elimina tras ejecutarse.async(solocommand): si estrue, el hook corre en segundo plano sin bloquear. ConasyncRewakeademás despierta al modelo si termina con código de salida 2 (error bloqueante).
Eventos disponibles
Estos son los eventos que puedes usar como clave dentro de hooks:
| Evento | Cuándo se dispara |
|---|---|
PreToolUse | Antes de ejecutar una herramienta. Puede aprobar, denegar o pedir permiso, y modificar la entrada. |
PostToolUse | Después de que una herramienta se ejecuta con éxito. |
PostToolUseFailure | Después de que una herramienta falla. |
UserPromptSubmit | Al enviar un prompt. Puede inyectar contexto adicional. |
SessionStart | Al iniciar la sesión. Puede añadir contexto o rutas a vigilar. |
SessionEnd | Al cerrar o limpiar la sesión (con un tiempo de ejecución ajustado). |
Stop / StopFailure | Cuando el asistente termina (o falla al terminar) su turno. |
SubagentStart / SubagentStop | Al iniciar o terminar un subagente. |
PreCompact / PostCompact | Antes y después de compactar la conversación. |
Notification | Al emitirse una notificación. |
PermissionRequest / PermissionDenied | Al solicitarse o denegarse un permiso. |
Setup | Durante la configuración inicial. |
TaskCreated / TaskCompleted | Al crearse o completarse una tarea. |
Elicitation / ElicitationResult | En flujos de solicitud de datos al usuario. |
ConfigChange | Al cambiar la configuración. |
WorktreeCreate / WorktreeRemove | Al crear o eliminar un worktree de git. |
InstructionsLoaded | Al cargarse las instrucciones del proyecto. |
CwdChanged | Al cambiar el directorio de trabajo. |
FileChanged | Al cambiar un archivo vigilado. |
TeammateIdle | Cuando un compañero de equipo queda inactivo. |
Datos que recibe el hook
Cada hook recibe por stdin un JSON con la información del evento. Todos los eventos incluyen campos base:
session_id: identificador de la sesión.transcript_path: ruta al transcript de la conversación.cwd: directorio de trabajo actual.hook_event_name: nombre del evento (p. ej.PreToolUse).permission_mode,agent_id,agent_type: presentes según el contexto (por ejemplo, cuando el hook se dispara dentro de un subagente).
Los eventos de herramienta añaden además tool_name, tool_input y tool_use_id; PostToolUse incorpora también tool_response.
Variables de entorno
Theus expone variables útiles al proceso del hook:
THEUS_PROJECT_DIR: raíz estable del proyecto (no la ruta del worktree), para que las rutas relativas resuelvan siempre contra el repositorio real.THEUS_ENV_FILE: ruta a un archivo.shdonde el hook puede escribir variables de entorno que se aplicarán a la sesión.
Cómo responde un hook
Un hook puede simplemente terminar (su código de salida y su stdout se registran), o devolver por stdout un JSON para controlar el flujo. Campos habituales:
continue: si esfalse, Theus no continúa; usastopReasonpara el mensaje.suppressOutput: oculta elstdoutdel transcript.decision:approveoblock, conreasoncomo explicación.systemMessage: mensaje de aviso que se muestra al usuario.hookSpecificOutput: salida específica del evento. Por ejemplo, enPreToolUsepermitepermissionDecision(allow/deny/ask) yupdatedInput; enUserPromptSubmitySessionStart,additionalContext.
Un hook también puede responder { "async": true } (con asyncTimeout opcional) para ejecutarse en segundo plano.
Ejemplos
Formatear tras cada escritura
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{ "type": "command", "command": "prettier --write \"$THEUS_PROJECT_DIR\"" }
]
}
]
}
}
Bloquear un comando peligroso antes de ejecutarlo
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"if": "Bash(rm -rf *)",
"command": "echo '{\"decision\":\"block\",\"reason\":\"Comando destructivo bloqueado por política.\"}'"
}
]
}
]
}
}
Inyectar contexto al enviar un prompt
{
"hooks": {
"UserPromptSubmit": [
{
"hooks": [
{ "type": "command", "command": "git-context.sh" }
]
}
]
}
}
Si el comando imprime { "hookSpecificOutput": { "hookEventName": "UserPromptSubmit", "additionalContext": "..." } }, ese texto se añade al contexto del turno.
Verificador con subagente
{
"hooks": {
"Stop": [
{
"hooks": [
{
"type": "agent",
"prompt": "Verifica que las pruebas unitarias se ejecutaron y pasaron."
}
]
}
]
}
}
Controles de seguridad
Además de requerir un espacio de trabajo de confianza, la configuración admite ajustes de seguridad relevantes:
- Deshabilitar por completo la ejecución de hooks (y de la status line).
- Ejecutar únicamente los hooks definidos en la configuración gestionada por el administrador, ignorando los de usuario, proyecto y local.
- Para hooks de tipo
http, restringir a qué URLs pueden apuntar mediante una allowlist con comodines (p. ej.https://hooks.example.com/*) y qué variables de entorno pueden interpolarse en las cabeceras.
Empieza siempre por /hooks para revisar tu configuración activa antes de editar settings.json a mano. Usa if para acotar cuándo corre cada hook y evita sobrecargar cada ejecución de herramienta.