Skip to content
Apothem
Référence

Hooks

Gestionnaires d'événements d'outils qui se déclenchent sur SessionStart, PreToolUse, PostToolUse et Stop, et chargent des contextes de message imposant l'isolation de git et la discipline des plans, avec un contrat d'exécution testé et un état par utilisateur.

Les hooks se déclenchent sur les événements d'outils et chargent des contextes de message que l'assistant traite comme des prompts système supplémentaires. Le câblage vit dans le bloc de hooks du settings.json de Claude Code (installation par le moteur) ou dans le hooks.json du plugin (installation par plugin), dans le fichier de hooks hooks.json de Codex, dans la table de hooks du settings.json de Qwen Code, dans la clé de hooks du .codebuddy/settings.local.json de CodeBuddy (SessionStart et PostToolUse uniquement), dans le fichier de hooks .kiro/hooks/apothem.json de Kiro (SessionStart uniquement), dans le fichier de hooks .github/hooks/apothem.json de GitHub Copilot (sessionStart et postToolUse uniquement), dans la clé de hooks du .devin/config.local.json de Windsurf (Devin Local) (SessionStart et PostToolUse uniquement) et dans le fichier de hooks de projet .cursor/hooks.json de Cursor (sessionStart uniquement).

ÉvénementDélai d'attenteObjectif
SessionStart30 secondesLire la mémoire, vérifier l'état du plan, signaler la disponibilité ; après une compaction, fournir le contexte de récupération ; signaler une base de connaissances qui a changé depuis sa référence
PreCompact30 secondesVérifier que l'état est externalisé avant la compaction (Codex, Qwen Code ; non enregistré sur Claude Code)
PostCompact30 secondesRestaurer l'état de travail après la compaction (Codex, Qwen Code ; non enregistré sur Claude Code)
Stop60 secondesProtocole de fin de session, activé sur demande
PostToolUse10 secondesAvis sur la taille du contexte : sauvegarder l'état au premier seuil, et au second l'opérateur est averti que la session est longue ; au plus deux fois par cycle de compaction ; s'exécute en arrière-plan sur Claude Code
PreToolUse (shell : Bash, PowerShell, run_shell_command)10 secondesIsolation des artefacts git, localité des plans, garde contre l'évaluation dynamique
PreToolUse (Write/Edit/NotebookEdit/apply_patch)10 secondesIsolation des références aux plans, en-tête de paternité, localité des plans, gardes de dépendances et d'évaluation dynamique
PreToolUse (AskUserQuestion)10 secondesBonne formation du marqueur (Recommended)

Répartiteur

Les entrées de hook installées par le moteur invoquent le répartiteur par le chemin absolu du script, avec un interpréteur résolu à l'installation. Les templates portent les jetons ${PYTHON_BIN} et ${HARNESS_ROOT}, que l'installateur rend au moment de l'installation. Claude Code et Qwen Code matérialisent le répartiteur sous <harness-root>/.apothem/support/hooks/ ; Codex, sous <harness-root>/hooks/. Les entrées de Claude Code utilisent la forme exec (command plus args) et n'ont donc besoin d'aucun shell. Le plugin Claude Code exécute chaque hook sous la forme bash "${CLAUDE_PLUGIN_ROOT}/lib/apothem/hooks/lib/bootstrap.sh" <event> [<message-file> ...], dans le shell de hook par défaut de Claude Code (sh sur macOS et Linux, Git Bash sur Windows) ; l'amorce localise un interpréteur Python 3.10+ et exécute le répartiteur fourni. Elle met en cache l'interpréteur trouvé dans interpreter-path (interpreter-path-ps1 pour bootstrap.ps1) sous le répertoire d'état des hooks (voir plus bas) et le réutilise tant que l'interpréteur existe encore, de sorte que la recherche dans le PATH s'exécute une seule fois plutôt qu'avant chaque appel d'outil ; supprimez le fichier de cache pour forcer une nouvelle recherche. Seuls un répertoire d'état absolu et un chemin en cache absolu sont pris en compte.

Chaque surface enregistre une entrée de répartiteur par événement et par matcher, qui liste chaque message que ce matcher évalue : un seul processus lit la charge utile une fois, exécute chaque garde et répond avec une seule enveloppe, de sorte qu'un Write lance un seul répartiteur plutôt qu'un par garde. L'installation de Claude Code par le moteur exécute aussi le portail de conformité (gate.py --hook) sur Write et Edit, dans une entrée distincte. Sous Windows sans Git for Windows, utilisez l'installation par le moteur (apothem install --harness claude-code), dont les hooks en forme exec n'ont besoin d'aucun shell. Le répartiteur est un script autonome de la bibliothèque standard : il amorce son propre sys.path, de sorte qu'aucun paquet apothem importable n'est nécessaire sur l'hôte. Depuis un checkout des sources, la forme de module python -m apothem.hooks.dispatch <event> (avec PYTHONPATH=src) exécute le même code pour le développement. Le répartiteur charge hooks/lib/resolve_root.py pour la résolution de la racine de l'écosystème et délègue à hooks/emit_hook_context.py et hooks/session_start_bootstrap.py.

Contrats de fournisseur

Un environnement qui n'est pas Claude Code exécute le même répartiteur avec --contract <id> après le script, par exemple "<python>" "<project>/.apothem/support/hooks/dispatch.py" --contract codebuddy SessionStart. Le contrat (hooks/lib/vendor_contract.py) traduit les graphies d'événements et de noms d'outils et les clés de charge utile du fournisseur dans la forme Claude que lisent les gestionnaires, réécrit l'enveloppe dans la forme que lit le fournisseur et abandonne un événement que le fournisseur ne transmet pas au modèle. Sans l'option, rien n'est traduit et la sortie est l'enveloppe Claude, octet pour octet. L'option fait partie de la ligne de commande documentée du répartiteur : dispatch.py [--contract <id>] <Event> [<message> ...].

ContratLitSortieÉvénements transmis
codebuddyle stdin de Claudel'enveloppe ClaudeSessionStart, UserPromptSubmit, PostToolUse
devinle stdin de Claude avec des noms d'outils en minuscules (exec, write, edit) ; la racine du projet depuis DEVIN_PROJECT_DIRl'enveloppe ClaudeSessionStart, UserPromptSubmit, PostToolUse
cursordes événements en camelCase, conversation_id, workspace_roots{"additional_context": ...}SessionStart
copilotsessionId, toolName, toolArgs en camelCase, ou la forme PascalCase snake_case{"additionalContext": ...}SessionStart, PostToolUse, Notification
kiroun stdin proche de celui de Claude, des identifiants d'outils Kiro (execute_bash, fs_write, str_replace)du texte brutSessionStart, UserPromptSubmit
claudele stdin de Claudel'enveloppe Claudetous les événements

Les contrats codebuddy, copilot, cursor, devin et kiro sont aujourd'hui installés par un adaptateur Apothem (devin par l'adaptateur Windsurf) ; le contrat claude prouve que la ligne de commande par défaut est inchangée. Le répartiteur reste consultatif sous chaque contrat : aucune forme ne porte de permission, de décision, de blocage ni d'entrée d'outil réécrite.

Cursor et Devin Local chargent les hooks que contiennent les fichiers de réglages de Claude Code en plus des leurs, de sorte qu'avec l'installation Apothem de Claude Code sur la même machine, deux ensembles peuvent s'exécuter. L'amorce SessionStart n'est fournie qu'une fois : la commande propre au fournisseur (--contract cursor ou --contract devin) et la copie de Claude Code, qui est la ligne de commande par défaut, partagent un marqueur par session et par source dans le répertoire d'état des hooks, et celle qui s'exécute en second reste silencieuse. La ligne de commande par défaut n'y participe que lorsque l'environnement du processus nomme l'hôte (CURSOR_PROJECT_DIR, CURSOR_VERSION ou DEVIN_PROJECT_DIR) ; sous Claude Code, Codex et Qwen Code, elle ne lit jamais le marqueur. Le marqueur vaut pendant dix secondes, de sorte qu'un démarrage ultérieur de la même session, comme une seconde compaction, fournit encore son contexte de récupération. Aucun compteur PostToolUse n'est dédoublonné, si bien que le compteur de taille du contexte s'exécute aux deux endroits et atteint ses seuils plus tôt. L'installation affiche une note qui nomme le réglage du fournisseur permettant de désactiver l'import (« Include Third-Party Plugins, Skills, and Other Configs » de Cursor ; read_config_from.claude de Devin).

Une commande n'est enregistrée que là où le fournisseur transmet sa sortie au modèle. Kiro ajoute la sortie standard d'un hook au contexte pour SessionStart et UserPromptSubmit uniquement, de sorte que le fichier Kiro n'enregistre que SessionStart. Copilot lit additionalContext depuis sessionStart et postToolUse, et seulement des champs de permission depuis preToolUse, de sorte que le fichier Copilot enregistre ces deux événements, sous leurs noms en camelCase, et aucune garde. Chaque entrée Copilot porte une commande bash et une commande powershell. Les deux fichiers appartiennent à Apothem et nomment l'interpréteur et le chemin de projet de la machine qui a exécuté l'installation ; les pages des environnements expliquent ce que cela implique pour un fichier qu'un dépôt commite.

Devin Local lit additionalContext depuis SessionStart, UserPromptSubmit et PostToolUse et n'en documente aucun pour PreToolUse, de sorte que l'enregistrement Windsurf contient SessionStart et PostToolUse, sans matcher (le matcher de Devin est une expression régulière, et un matcher omis correspond à tous les outils). Il va dans la surcharge locale .devin/config.local.json, que Devin documente comme ignorée par git et exclut lui-même de git via .git/info/exclude. Le hook sessionStart de Cursor ajoute additional_context au contexte initial de la conversation ; la première version n'enregistre que cet événement. Le .cursor/hooks.json de Cursor est l'unique fichier de hooks du projet, il appartient donc à l'opérateur : l'installation y fusionne une seule entrée, conserve les entrées et les clés de l'opérateur, refuse un fichier qu'elle ne peut pas réécrire sans perte, et distingue sa propre entrée par le chemin du répartiteur dans la commande. L'application de bureau de Cursor exécute les commandes de hook via PowerShell sous Windows, de sorte que sur un hôte Windows la commande commence par & .

Dans les cinq fichiers, une entrée appartient à Apothem lorsque sa commande exécute .apothem/support/[<harness>/]hooks/dispatch.py avec --contract <id>, quel que soit le chemin de projet sous lequel elle a été écrite. Un projet renommé, déplacé ou cloné conserve donc un seul ensemble d'entrées, et un hook à vous, comme un hooks/dispatch.py dans votre propre arborescence ou python -m apothem.conformity.gate, n'est jamais remplacé ni supprimé. apothem verify signale l'enregistrement comme manquant dès qu'aucune des entrées d'Apothem ne subsiste dans le fichier, et une désinstallation qui ne peut pas lire le fichier le signale comme ignoré. Un chemin de projet ou d'interpréteur qui contient $, un accent grave, un guillemet double ou une barre oblique inverse est refusé à l'installation, en nommant le chemin, car la commande entre guillemets exécuterait un autre fichier.

Contextes de message

Les contextes Markdown par événement vivent sous src/apothem/hooks/messages/ :

  • Cycle de vie de la session : sessionstart.md, skill-index.md, precompact.md, postcompact.md, stop.md, posttooluse-proactive-compaction.md
  • Appels shell : pretooluse-bash.md, pretooluse-bash-plan-guard.md
  • Écritures de fichiers : 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
  • Gardes sur le contenu : pretooluse-dependency-guard.md, pretooluse-eval-guard.md
  • Questions : pretooluse-askuserquestion-recommended.md

Sur Claude Code, postcompact.md est fourni par le hook SessionStart lorsque la session reprend après une compaction, car Claude Code ne transmet pas la sortie des hooks PreCompact ou PostCompact à l'assistant.

skill-index.md est généré, pas rédigé : scripts/dev/generate_skill_index.py le produit à partir du catalogue des skills et des commandes, une ligne par groupe de tâches, les skills manuelles étant affichées sous la forme /apothem:<name>. Sur Claude Code, le hook SessionStart l'ajoute au contexte de la session lorsque le fichier existe. Il existe parce que Claude Code retire les descriptions des skills de sa liste dès que celle-ci dépasse son budget de caractères, et l'index permet de retrouver chaque skill par tâche. Une installation par plugin affiche les skills manuelles sous la forme /apothem:<name> ; une installation par le moteur retire le préfixe. Le texte reste sous 1 600 caractères, et generate_skill_index.py --check échoue lorsque le fichier commité ne correspond plus au catalogue.

Lorsque Apothem s'exécute seul en tant que plugin Claude Code, aucun fichier de règle ne se charge au lancement. Le hook SessionStart ajoute alors un court pointeur vers le répertoire rules/ fourni. Si ce répertoire contient apothem-rule-index.md, le pointeur indique aussi à la session de lire d'abord les quatre règles de base, car rien ne les charge dans cette configuration (agnostic-posture, authority-inquiry, session-closure, context-management), et nomme l'index, qui indique quelle règle lire pour quelle tâche. Le texte de l'index n'est pas inséré en ligne.

Lorsque le projet contient une base de connaissances créée par la skill knowledge-wiki, le hook SessionStart compare son empreinte à celle enregistrée lors de la dernière référence et, si elles diffèrent, ajoute une ligne qui suggère le mode de dérive de la skill (--mode drift --kb <path>). L'empreinte est le nombre de pages plus l'entrée de journal la plus récente ; la copie enregistrée est la ligne baseline la plus récente du log.md de la base, que seul le mode de dérive écrit. Le hook examine quatre chemins sous la racine du projet, dans l'ordre : knowledge-base/, wiki/, docs/knowledge-base/ et docs/wiki/, et seulement un répertoire qui contient à la fois index.md et log.md. Un projet qui n'en a pas paie moins d'une milliseconde. Avec une base, le hook lit au plus les 256 derniers Kio de log.md et liste trois répertoires à plat ; il ne parcourt jamais l'arborescence et n'écrit jamais. La ligne porte un chemin fixe, une date et deux nombres de pages, jamais de texte issu de la base. Il reste silencieux lorsqu'il n'y a pas de base, pas de référence, pas de changement, ou un fichier qu'il ne peut pas lire, et il se répète à chaque session jusqu'à ce que le mode de dérive enregistre une nouvelle référence. Une base située à tout autre chemin fonctionne dans chaque mode de la skill mais ne reçoit aucune ligne au démarrage de session.

Chaque fichier de contexte est chargé par le répartiteur lorsque son événement se déclenche et que son argument de contexte est enregistré dans le fichier de réglages. La ligne de commentaire de licence et la section finale ## Bindings sont retirées avant que le texte n'atteigne l'assistant ; toutes deux existent pour les mainteneurs et le portail de conformité.

Une garde qui ne trouve rien n'injecte rien. Une garde qui se déclenche n'injecte que la section ## Advisory de son message, remplie de ce qu'elle a trouvé (le chemin, l'identifiant, la primitive), suivie de Full procedure: <message-file>. Le reste du fichier est la procédure complète, que l'assistant ne lit que lorsqu'il a besoin du détail. Les gardes jugent « à l'intérieur du projet » par rapport à la racine git située au-dessus du répertoire de travail de l'appel d'outil, jamais par rapport à l'arborescence dans laquelle le hook est installé.

Contrat d'exécution

Chaque commande de hook enregistrée respecte le même contrat, vérifié par tests/hooks/test_hook_contract.py sur le plugin, l'installation de Claude Code par le moteur, Codex, Qwen Code et l'installation de CodeBuddy (les fichiers Kiro, Copilot, Cursor et Windsurf sont vérifiés par rapport à leurs contrats par tests/hooks/test_native_hook_registrations.py et exécutés comme les fournisseurs les exécutent par tests/integration/test_native_hook_files_roundtrip.py, test_cursor_hooks_roundtrip.py et test_windsurf_hooks_roundtrip.py) :

  • S'exécute depuis un simple checkout. Code de sortie 0, et la sortie standard est vide ou contient un seul objet JSON. Aucun hook ne dépend du bit exécutable d'un fichier.
  • Enregistré une seule fois. Chaque événement et matcher a une seule entrée de répartiteur par surface, qui liste chaque message qu'il évalue, de sorte qu'une garde ne s'exécute jamais deux fois sur un même appel d'outil.
  • Borné. Aucun texte émis ne dépasse 10 000 caractères. La garde de dépendances ne s'exprime que lorsqu'une écriture cible un manifeste de dépendances ou un fichier de verrouillage, et la garde d'évaluation dynamique uniquement lorsque le contenu ou la commande contient une primitive d'évaluation ou de désérialisation non sûre. L'avis de compaction se déclenche au plus deux fois par cycle de compaction.
  • Utilise les canaux que l'environnement transmet. Les consignes PreToolUse transitent dans hookSpecificOutput ; la garde AskUserQuestion renvoie additionalContext, ou permissionDecision: deny sous l'activation stricte sur demande. Claude Code ignore la sortie de PreCompact et de PostCompact ; sur Claude Code, le contexte de récupération post-compaction arrive donc avec l'événement SessionStart dont la source est compact.
  • N'exécute jamais le code du projet. L'amorce résout sa propre arborescence installée et ne prend jamais de script dans le projet ouvert. Les consignes de démarrage de session sont lues depuis l'arborescence installée ; le répertoire du projet n'est lu que pour les résumés de plan et de mémoire et pour la vérification de dérive de la base de connaissances.
  • Échoue en mode ouvert. Un fichier de message manquant, un interpréteur manquant ou un état corrompu produit un code de sortie 0 et une enveloppe valide.
VariableDéfautEffet
APOTHEM_HOOKS_DISABLEnon définie1, true, yes ou on réduit au silence chaque hook routé par le répartiteur ainsi que le hook du portail de conformité de l'installation par le moteur
APOTHEM_SESSION_END_ENABLEDdésactivé1, true, yes ou on active le protocole de fin de session sur Stop. Un hook Stop qui renvoie du contexte fait continuer l'assistant, de sorte que le protocole coûte un tour supplémentaire par session ; il ne se déclenche jamais sur un arrêt qui suit déjà une continuation déclenchée par un hook
APOTHEM_SESSION_END_MIN_STOPS3Fins de tour avant que le protocole de fin de session puisse se déclencher
APOTHEM_PROACTIVE_COMPACTION_ENABLEDactivé0, false, no ou off réduit au silence l'avis de compaction
APOTHEM_CONTEXT_WINDOW_TOKENSnon définieTaille de la fenêtre de contexte ; avec elle (ou avec CLAUDE_CODE_AUTO_COMPACT_WINDOW de Claude Code), les avis se déclenchent à 50 % et 75 % de la fenêtre, sinon à 120 000 et 250 000 jetons
APOTHEM_PROACTIVE_COMPACTION_TOOL_THRESHOLD18Appels d'outils par fenêtre d'avis, utilisé uniquement lorsque la taille du contexte ne peut pas être mesurée
APOTHEM_PROACTIVE_COMPACTION_OUTPUT_THRESHOLD20000Octets de sortie d'outil par fenêtre d'avis, utilisé uniquement lorsque la taille du contexte ne peut pas être mesurée
APOTHEM_HOOK_STATE_DIRvoir ci-dessousRépertoire de l'état de hook par session

L'avis de compaction mesure le contexte à partir de l'enregistrement d'usage le plus récent de la transcription de la session (jetons d'entrée plus jetons en cache), de sorte qu'il reflète ce que le modèle contient réellement. Une forte baisse de cette taille marque une compaction et réinitialise le budget de deux avis. Lorsqu'aucune transcription n'est disponible, il compte plutôt les appels d'outils et les octets de sortie.

L'état de hook par session vit dans un répertoire par utilisateur créé avec le mode 0700 : APOTHEM_HOOK_STATE_DIR s'il est défini, sinon ${CLAUDE_PLUGIN_DATA}/state pour le plugin Claude Code, sinon $XDG_STATE_HOME/apothem, sinon ~/.local/state/apothem (%LOCALAPPDATA%\apothem\state sous Windows).

Inventaire généré des messages de hook

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