Skip to content
Apothem
Referencia

Hooks

Manejadores de eventos de herramientas que se disparan en SessionStart, PreToolUse, PostToolUse y Stop, y cargan contextos de mensaje que imponen el aislamiento de git y la disciplina de planes, con un contrato de ejecución probado y estado por usuario.

Los hooks se disparan en eventos de herramientas y cargan contextos de mensaje que el asistente trata como prompts de sistema adicionales. El cableado reside en el bloque de hooks del settings.json de Claude Code (instalación del motor) o en el hooks.json del plugin (instalación del plugin), en el archivo de hooks hooks.json de Codex, en el mapa de hooks del settings.json de Qwen Code, en la clave de hooks de .codebuddy/settings.local.json de CodeBuddy (solo SessionStart y PostToolUse), en el archivo de hooks de Kiro .kiro/hooks/apothem.json (solo SessionStart), en el archivo de hooks de GitHub Copilot .github/hooks/apothem.json (solo sessionStart y postToolUse), en la clave de hooks de .devin/config.local.json de Windsurf (Devin Local) (solo SessionStart y PostToolUse) y en el archivo de hooks del proyecto de Cursor .cursor/hooks.json (solo sessionStart).

EventoTiempo de esperaPropósito
SessionStart30 segundosLeer la memoria, comprobar el estado del plan, informar de la disponibilidad; después de una compactación, entregar el contexto de recuperación; señalar una base de conocimiento que ha cambiado desde su línea base
PreCompact30 segundosVerificar que el estado se ha externalizado antes de la compactación (Codex, Qwen Code; no se registra en Claude Code)
PostCompact30 segundosRestaurar el estado de trabajo después de la compactación (Codex, Qwen Code; no se registra en Claude Code)
Stop60 segundosProtocolo de fin de sesión, opcional
PostToolUse10 segundosAviso de tamaño del contexto: guardar el estado en la primera marca, y en la segunda se informa al operador de que la sesión es larga; como máximo dos veces por ciclo de compactación; se ejecuta en segundo plano en Claude Code
PreToolUse (shell: Bash, PowerShell, run_shell_command)10 segundosAislamiento de artefactos de git, localidad de los planes, protección contra la evaluación dinámica
PreToolUse (Write/Edit/NotebookEdit/apply_patch)10 segundosAislamiento de referencias a planes, encabezado de autoría, localidad de los planes, protecciones de dependencias y de evaluación dinámica
PreToolUse (AskUserQuestion)10 segundosBuena formación del marcador (Recommended)

Despachador

Las entradas de hook instaladas por el motor invocan al despachador mediante la ruta absoluta del script, con un intérprete resuelto durante la instalación. Las plantillas llevan los tokens ${PYTHON_BIN} y ${HARNESS_ROOT}, que el instalador renderiza en el momento de la instalación. Claude Code y Qwen Code materializan el despachador bajo <harness-root>/.apothem/support/hooks/; Codex, bajo <harness-root>/hooks/. Las entradas de Claude Code usan la forma exec (command más args), de modo que no necesitan ninguna shell. El plugin de Claude Code ejecuta cada hook como bash "${CLAUDE_PLUGIN_ROOT}/lib/apothem/hooks/lib/bootstrap.sh" <event> [<message-file> ...], en la shell de hooks predeterminada de Claude Code (sh en macOS y Linux, Git Bash en Windows); el bootstrap localiza un intérprete de Python 3.10+ y ejecuta el despachador incluido. Guarda en caché el intérprete que encontró en interpreter-path (interpreter-path-ps1 para bootstrap.ps1) bajo el directorio de estado de los hooks (más abajo) y lo reutiliza mientras el intérprete siga existiendo, de modo que la búsqueda en el PATH se ejecuta una vez y no antes de cada llamada a una herramienta; borra el archivo de caché para forzar una nueva búsqueda. Solo se respetan un directorio de estado absoluto y una ruta en caché absoluta.

Cada superficie registra una entrada del despachador por evento y matcher, que enumera cada mensaje que ese matcher evalúa: un proceso lee la carga útil una vez, ejecuta cada protección y responde con un único sobre, de modo que un Write inicia un despachador y no uno por protección. La instalación del motor de Claude Code también ejecuta la puerta de conformidad (gate.py --hook) en Write y Edit como entrada propia. En Windows sin Git for Windows, usa la instalación del motor (apothem install --harness claude-code), cuyos hooks en forma exec no necesitan ninguna shell. El despachador es un script autónomo que solo usa la biblioteca estándar: arranca su propio sys.path, de modo que no se necesita ningún paquete apothem importable en el host. Desde un checkout del código fuente, la forma de módulo python -m apothem.hooks.dispatch <event> (con PYTHONPATH=src) ejecuta el mismo código para el desarrollo. El despachador carga hooks/lib/resolve_root.py para resolver la raíz del ecosistema y delega en hooks/emit_hook_context.py y hooks/session_start_bootstrap.py.

Contratos de proveedor

Un entorno que no es Claude Code ejecuta el mismo despachador con --contract <id> después del script, por ejemplo "<python>" "<project>/.apothem/support/hooks/dispatch.py" --contract codebuddy SessionStart. El contrato (hooks/lib/vendor_contract.py) traduce las grafías de eventos y de nombres de herramientas del proveedor y las claves de su carga útil a la forma de Claude que leen los manejadores, reescribe el sobre en la forma que lee el proveedor y descarta un evento que el proveedor no entrega al modelo. Sin la opción no se traduce nada y la salida es el sobre de Claude, byte a byte. La opción forma parte de la línea de comandos documentada del despachador: dispatch.py [--contract <id>] <Event> [<message> ...].

ContratoLeeSalidaEventos entregados
codebuddyla entrada estándar de Claudeel sobre de ClaudeSessionStart, UserPromptSubmit, PostToolUse
devinla entrada estándar de Claude con nombres de herramientas en minúsculas (exec, write, edit); la raíz del proyecto desde DEVIN_PROJECT_DIRel sobre de ClaudeSessionStart, UserPromptSubmit, PostToolUse
cursoreventos en camelCase, conversation_id, workspace_roots{"additional_context": ...}SessionStart
copilotsessionId, toolName, toolArgs en camelCase, o la forma PascalCase con snake_case{"additionalContext": ...}SessionStart, PostToolUse, Notification
kiroentrada estándar similar a la de Claude, ids de herramientas de Kiro (execute_bash, fs_write, str_replace)texto sin formatoSessionStart, UserPromptSubmit
claudela entrada estándar de Claudeel sobre de Claudetodos los eventos

Los contratos codebuddy, copilot, cursor, devin y kiro los instala hoy un adaptador de Apothem (devin, el adaptador de Windsurf); el contrato claude demuestra que la línea de comandos predeterminada no cambia. El despachador sigue siendo consultivo bajo todos los contratos: ninguna forma transporta un permiso, una decisión, un bloqueo ni una entrada de herramienta reescrita.

Cursor y Devin Local cargan los hooks que contienen los archivos de ajustes de Claude Code además de los suyos, así que con la instalación de Apothem para Claude Code en la misma máquina pueden ejecutarse dos conjuntos. El bootstrap de SessionStart se entrega una sola vez: el comando propio del proveedor (--contract cursor o --contract devin) y la copia de Claude Code, que es la línea de comandos predeterminada, comparten un marcador por sesión y origen en el directorio de estado de los hooks, y el que se ejecuta en segundo lugar queda en silencio. La línea de comandos predeterminada solo participa cuando el entorno del proceso nombra al anfitrión (CURSOR_PROJECT_DIR, CURSOR_VERSION o DEVIN_PROJECT_DIR); bajo Claude Code, Codex y Qwen Code nunca lee el marcador. El marcador vincula durante diez segundos, de modo que un inicio posterior de la misma sesión, como una segunda compactación, sigue entregando su contexto de recuperación. Ningún contador de PostToolUse se desduplica, así que el contador de tamaño del contexto se ejecuta en ambos lugares y alcanza sus umbrales antes de tiempo. La instalación imprime una nota que nombra el interruptor del proveedor para desactivar la importación ("Include Third-Party Plugins, Skills, and Other Configs" de Cursor; read_config_from.claude de Devin).

Un comando solo se registra donde el proveedor entrega su salida al modelo. Kiro añade la salida estándar de un hook al contexto solo para SessionStart y UserPromptSubmit, así que el archivo de Kiro registra únicamente SessionStart. Copilot lee additionalContext de sessionStart y postToolUse y solo campos de permiso de preToolUse, así que el archivo de Copilot registra esos dos eventos, con sus nombres en camelCase, y ninguna protección. Cada entrada de Copilot lleva un comando bash y otro powershell. Ambos archivos pertenecen a Apothem y nombran el intérprete y la ruta del proyecto de la máquina que ejecutó la instalación; las páginas de cada entorno explican lo que eso significa para un archivo que un repositorio confirma.

Devin Local lee additionalContext de SessionStart, UserPromptSubmit y PostToolUse y no documenta ninguno para PreToolUse, así que el registro de Windsurf contiene SessionStart y PostToolUse, sin matcher (el matcher de Devin es una expresión regular, y uno omitido coincide con todas las herramientas). Va en la anulación local .devin/config.local.json, que Devin documenta como ignorada por git y que excluye de git por sí mismo mediante .git/info/exclude. El hook sessionStart de Cursor añade additional_context al contexto inicial de la conversación; la primera versión registra solo ese evento. El .cursor/hooks.json de Cursor es el único archivo de hooks del proyecto, así que pertenece al operador: la instalación fusiona en él una entrada, conserva las entradas y claves del operador, rechaza un archivo que no puede reescribir sin pérdida y distingue su propia entrada por la ruta del despachador en el comando. La aplicación de escritorio de Cursor ejecuta los comandos de hook mediante PowerShell en Windows, así que en un host Windows el comando empieza por & .

En los cinco archivos, una entrada es de Apothem cuando su comando ejecuta .apothem/support/[<harness>/]hooks/dispatch.py con --contract <id>, sea cual sea la ruta del proyecto bajo la que se escribió. Un proyecto renombrado, movido o clonado conserva por tanto un solo conjunto de entradas, y un hook tuyo, como un hooks/dispatch.py en tu propio árbol o python -m apothem.conformity.gate, nunca se sustituye ni se elimina. apothem verify informa de que falta el registro en cuanto no queda en el archivo ninguna de las entradas de Apothem, y una desinstalación que no puede leer el archivo lo notifica como omitido. Una ruta de proyecto o de intérprete que contiene $, un acento grave, una comilla doble o una barra invertida se rechaza en la instalación, nombrando la ruta, porque el comando entre comillas ejecutaría otro archivo.

Contextos de mensaje

Los contextos en Markdown por evento residen bajo src/apothem/hooks/messages/:

  • Ciclo de vida de la sesión: sessionstart.md, skill-index.md, precompact.md, postcompact.md, stop.md, posttooluse-proactive-compaction.md
  • Llamadas a la shell: pretooluse-bash.md, pretooluse-bash-plan-guard.md
  • Escrituras de archivos: pretooluse-write.md, pretooluse-edit.md, pretooluse-notebookedit.md, pretooluse-write-plan-guard.md, pretooluse-write-header-guard.md, pretooluse-edit-header-guard.md, pretooluse-conformity.md
  • Protecciones sobre el contenido: pretooluse-dependency-guard.md, pretooluse-eval-guard.md
  • Preguntas: pretooluse-askuserquestion-recommended.md

En Claude Code, el hook SessionStart entrega postcompact.md cuando la sesión se reanuda después de una compactación, porque Claude Code no pasa al asistente la salida de los hooks PreCompact ni PostCompact.

skill-index.md se genera, no se escribe: scripts/dev/generate_skill_index.py lo renderiza a partir del catálogo de skills y comandos, una línea por grupo de tareas, con las skills manuales mostradas como /apothem:<name>. En Claude Code, el hook SessionStart lo añade al contexto de la sesión cuando el archivo existe. Existe porque Claude Code elimina las descripciones de las skills de su listado en cuanto el listado supera su presupuesto de caracteres, y el índice mantiene cada skill localizable por tarea. Una instalación del plugin muestra las skills manuales como /apothem:<name>; una instalación del motor omite el prefijo. El texto se mantiene por debajo de 1,600 caracteres, y generate_skill_index.py --check falla cuando el archivo confirmado ya no coincide con el catálogo.

Cuando Apothem se ejecuta solo como plugin de Claude Code, ningún archivo de reglas se carga al iniciar. El hook SessionStart añade entonces un breve puntero al directorio rules/ incluido. Si ese directorio contiene apothem-rule-index.md, el puntero también indica a la sesión que lea primero las cuatro reglas principales, porque nada las carga en esta postura (agnostic-posture, authority-inquiry, session-closure, context-management), y nombra el índice, que dice qué regla leer para cada tarea. El texto del índice no se incrusta.

Cuando el proyecto contiene una base de conocimiento creada por la skill knowledge-wiki, el hook SessionStart compara su huella con la registrada en la última línea base y, si difieren, añade una línea que sugiere el modo de deriva de la skill (--mode drift --kb <path>). La huella es el número de páginas más la entrada de registro más reciente; la copia registrada es la línea baseline más reciente del log.md de la base, que solo escribe el modo de deriva. El hook examina cuatro rutas bajo la raíz del proyecto, en orden: knowledge-base/, wiki/, docs/knowledge-base/ y docs/wiki/, y solo un directorio que contenga tanto index.md como log.md. Un proyecto sin ninguna paga menos de un milisegundo. Con una, el hook lee como máximo los últimos 256 KiB de log.md y lista tres directorios planos; nunca recorre el árbol y nunca escribe. La línea lleva una ruta fija, una fecha y dos recuentos de páginas, nunca texto de la base. Permanece en silencio cuando no hay base, ni línea base, ni cambio, o hay un archivo que no puede leer, y se repite en cada sesión hasta que el modo de deriva registra una nueva línea base. Una base en cualquier otra ruta funciona en todos los modos de la skill, pero no recibe la línea de inicio de sesión.

El despachador carga cada archivo de contexto cuando se dispara su evento y su argumento de contexto está registrado en el archivo de ajustes. La línea de comentario de la licencia y la sección ## Bindings del final se eliminan antes de que el texto llegue al asistente; ambas existen para los mantenedores y para la puerta de conformidad.

Una protección que no encuentra nada no inyecta nada. Una protección que se dispara inyecta solo la sección ## Advisory de su mensaje, rellenada con lo que encontró (la ruta, el identificador, la primitiva), seguida de Full procedure: <message-file>. El resto del archivo es el procedimiento completo, que el asistente lee solo cuando necesita el detalle. Las protecciones juzgan "dentro del proyecto" con respecto a la raíz de git por encima del directorio de trabajo de la llamada a la herramienta, nunca con respecto al árbol en el que está instalado el hook.

Contrato de ejecución

Cada comando de hook registrado cumple el mismo contrato, que tests/hooks/test_hook_contract.py comprueba en el plugin, en la instalación del motor de Claude Code, en Codex, en Qwen Code y en la instalación de CodeBuddy (los archivos de Kiro, Copilot, Cursor y Windsurf se comprueban frente a sus contratos con tests/hooks/test_native_hook_registrations.py y se ejecutan como los ejecutan los proveedores con tests/integration/test_native_hook_files_roundtrip.py, test_cursor_hooks_roundtrip.py y test_windsurf_hooks_roundtrip.py):

  • Se ejecuta desde un checkout simple. Sale con 0, y la salida estándar está vacía o es un único objeto JSON. Ningún hook depende del bit de ejecución de un archivo.
  • Se registra una sola vez. Cada combinación de evento y matcher tiene una entrada del despachador por superficie, que enumera cada mensaje que evalúa, de modo que una protección nunca se ejecuta dos veces en una misma llamada a una herramienta.
  • Acotado. Ningún texto emitido supera los 10,000 caracteres. La protección de dependencias solo se pronuncia cuando una escritura apunta a un manifiesto de dependencias o a un archivo de bloqueo, y la protección de evaluación dinámica, solo cuando el contenido o el comando contiene una primitiva de evaluación o de deserialización insegura. El aviso de compactación se dispara como máximo dos veces por ciclo de compactación.
  • Usa los canales que el entorno entrega. La orientación de PreToolUse viaja en hookSpecificOutput; la protección de AskUserQuestion devuelve additionalContext, o permissionDecision: deny con la opción estricta activada. Claude Code descarta la salida de PreCompact y PostCompact, así que en Claude Code el contexto de recuperación posterior a la compactación llega con el evento SessionStart cuyo origen es compact.
  • Nunca ejecuta código del proyecto. El bootstrap resuelve su propio árbol instalado y nunca toma un script del proyecto abierto. La orientación del inicio de sesión se lee del árbol instalado; el directorio del proyecto solo se lee para los resúmenes de planes y de memoria y la comprobación de deriva de la base de conocimiento.
  • Falla en abierto. Un archivo de mensaje que falta, un intérprete que falta o un estado corrupto producen la salida 0 y un sobre válido.
VariableValor predeterminadoEfecto
APOTHEM_HOOKS_DISABLEsin definir1, true, yes u on silencia cada hook enrutado por el despachador y el hook de la puerta de conformidad de la instalación del motor
APOTHEM_SESSION_END_ENABLEDdesactivado1, true, yes u on activa el protocolo de fin de sesión en Stop. Un hook Stop que devuelve contexto hace que el asistente continúe, así que el protocolo cuesta un turno adicional por sesión; nunca se dispara en una parada que ya sigue a una continuación provocada por un hook
APOTHEM_SESSION_END_MIN_STOPS3Finales de turno antes de que el protocolo de fin de sesión pueda dispararse
APOTHEM_PROACTIVE_COMPACTION_ENABLEDactivado0, false, no u off silencia el aviso de compactación
APOTHEM_CONTEXT_WINDOW_TOKENSsin definirTamaño de la ventana de contexto; con ella (o con CLAUDE_CODE_AUTO_COMPACT_WINDOW de Claude Code), los avisos se disparan al 50% y al 75% de la ventana; si no, a 120,000 y 250,000 tokens
APOTHEM_PROACTIVE_COMPACTION_TOOL_THRESHOLD18Llamadas a herramientas por ventana de aviso, usado solo cuando no se puede medir el tamaño del contexto
APOTHEM_PROACTIVE_COMPACTION_OUTPUT_THRESHOLD20000Bytes de salida de herramientas por ventana de aviso, usado solo cuando no se puede medir el tamaño del contexto
APOTHEM_HOOK_STATE_DIRconsulta más abajoDirectorio para el estado de los hooks por sesión

El aviso de compactación mide el contexto a partir del registro de uso más reciente de la transcripción de la sesión (tokens de entrada más tokens en caché), de modo que refleja lo que el modelo contiene realmente. Una caída brusca de ese tamaño marca una compactación y reinicia el presupuesto de dos avisos. Donde no hay transcripción disponible, cuenta en su lugar las llamadas a herramientas y los bytes de salida.

El estado de los hooks por sesión reside en un directorio por usuario creado con el modo 0700: APOTHEM_HOOK_STATE_DIR cuando está definida; si no, ${CLAUDE_PLUGIN_DATA}/state para el plugin de Claude Code; si no, $XDG_STATE_HOME/apothem; y si no, ~/.local/state/apothem (%LOCALAPPDATA%\apothem\state en Windows).

Inventario de mensajes de hook generado

NameDescriptionSource
postcompactPost-compaction Blind Bootstrap. Restore working state from durable files; conversation context is now compressed.src/apothem/hooks/messages/postcompact.md
posttooluse-proactive-compactionProactive-compaction tracker for PostToolUse tool calls.src/apothem/hooks/messages/posttooluse-proactive-compaction.md
precompactPre-compaction protocol. Externalize all in-conversation state BEFORE the harness compresses context.src/apothem/hooks/messages/precompact.md
pretooluse-askuserquestion-recommendedCall-time (Recommended)-marker guard for AskUserQuestion tool calls.src/apothem/hooks/messages/pretooluse-askuserquestion-recommended.md
pretooluse-bash-plan-guardPlans-Discipline write guard for Bash tool calls (companion to pretooluse-write-plan-guard.md).src/apothem/hooks/messages/pretooluse-bash-plan-guard.md
pretooluse-bashGit write guard: plan-internal isolation, numeric-prefix discipline, human-only authorship.src/apothem/hooks/messages/pretooluse-bash.md
pretooluse-conformity> Advisory: this hook reports; it does not block. Mechanical enforcement runs in CI.src/apothem/hooks/messages/pretooluse-conformity.md
pretooluse-dependency-guardDependency-manifest supply-chain guard (advisory).src/apothem/hooks/messages/pretooluse-dependency-guard.md
pretooluse-edit-header-guardSPDX-header inject guard for Edit tool calls.src/apothem/hooks/messages/pretooluse-edit-header-guard.md
pretooluse-editCodebase isolation (Edit scope).src/apothem/hooks/messages/pretooluse-edit.md
pretooluse-eval-guardDynamic-eval / unsafe-deserialization guard — advisory prompt-injection defense.src/apothem/hooks/messages/pretooluse-eval-guard.md
pretooluse-notebookeditCodebase isolation (notebook scope).src/apothem/hooks/messages/pretooluse-notebookedit.md
pretooluse-write-header-guardSPDX-header inject guard for Write tool calls.src/apothem/hooks/messages/pretooluse-write-header-guard.md
pretooluse-write-plan-guardPlans-Discipline write guard for Write / Edit / NotebookEdit tool calls.src/apothem/hooks/messages/pretooluse-write-plan-guard.md
pretooluse-writeCodebase isolation + frontmatter compliance.src/apothem/hooks/messages/pretooluse-write.md
sessionstartSession-start conformity posture.src/apothem/hooks/messages/sessionstart.md
skill-indexSkills by task. /apothem:name = manual, run only on request.src/apothem/hooks/messages/skill-index.md
stopSession-end protocol: externalize state; evaluate memory and artifacts.src/apothem/hooks/messages/stop.md

On this page