# src/hooks/ — ~52 Lifecycle Hooks Across 58 Dirs **Generated:** 2026-05-15 ## OVERVIEW 52 hooks (5 of the 58 dirs are `zauc-mocks-*` test scaffolds + 1 `shared/`). 5-tier composition wired in `src/plugin/hooks/`. All hooks follow `createXXXHook(deps) → HookFunction` factory pattern. ## TIER COMPOSITION | Tier | Composer | Base | With team-mode | Where | |------|----------|------|----------------|-------| | **Session** | `create-session-hooks.ts` | 24 | 24 | OpenCode session lifecycle + chat.params + chat.message | | **Tool Guard** | `create-tool-guard-hooks.ts` | 16 | 17 | Pre/post tool execution (+1: `team-tool-gating`) | | **Transform** | `create-transform-hooks.ts` | 5 | 7 | `experimental.chat.messages.transform` (+2: `team-mode-status-injector`, `team-mailbox-injector`) | | **Continuation** | `create-continuation-hooks.ts` | 7 | 7 | Boulder/atlas/compaction/notification | | **Skill** | `create-skill-hooks.ts` | 2 | 2 | Skill awareness (categorySkillReminder, autoSlashCommand) | | **Direct event handlers** | `src/plugin/event.ts` | 0 | +4 | `team-session-events/` sub-files: `team-idle-wake-hint`, `team-lead-orphan-handler`, `team-member-error-handler`, `team-member-status-handler` | Total exposed hooks: **54 base, 61 with team-mode** (counts the 4 team-session-events handlers individually). Hook name allowlist for `disabled_hooks`: all configurable hook names enumerated in [`src/config/schema/hooks.ts`](file:///Users/yeongyu/local-workspaces/omo/src/config/schema/hooks.ts) `HookNameSchema`. Team-session-event sub-hooks are not individually listed in the schema — they activate together with `team_mode.enabled`. ### Tier 1: Session Hooks (24) | Hook | Event | Purpose | |------|-------|---------| | `contextWindowMonitor` | session.idle | Track context usage | | `preemptiveCompaction` | session.idle | Trigger compaction before limit | | `sessionRecovery` | session.error | Recover from structural errors (tool_result_missing, thinking_block_order) | | `sessionNotification` | session.idle | OS notifications on completion | | `thinkMode` | chat.params | Model variant switching for extended thinking | | `anthropicContextWindowLimitRecovery` | session.error | Multi-strategy context recovery (truncation, compaction, dedup) | | `autoUpdateChecker` | session.created | Check npm for plugin updates | | `agentUsageReminder` | chat.message | Remind about available agents | | `nonInteractiveEnv` | chat.message | Adjust behavior for `run` command | | `interactiveBashSession` | tool.execute | Tmux session lifecycle for interactive_bash tool | | `ralphLoop` | event | Self-referential dev loop (boulder continuation) | | `editErrorRecovery` | tool.execute.after | Retry failed file edits | | `delegateTaskRetry` | tool.execute.after | Retry failed task delegations | | `startWork` | chat.message | `/start-work` command handler | | `prometheusMdOnly` | tool.execute.before | Enforce .md-only writes for Prometheus | | `sisyphusJuniorNotepad` | chat.message | Notepad injection for subagents | | `questionLabelTruncator` | tool.execute.before | Truncate long Question tool labels | | `taskResumeInfo` | chat.message | Inject task context on resume | | `anthropicEffort` | chat.params | Adjust reasoning effort level | | `modelFallback` | chat.params | Provider-level proactive model fallback | | `noSisyphusGpt` | chat.message | Block Sisyphus from non-GPT providers (with warning toast) | | `noHephaestusNonGpt` | chat.message | Block Hephaestus from non-GPT models | | `runtimeFallback` | event | Reactive auto-switch on API provider errors | | `legacyPluginToast` | chat.message | Show toast when legacy plugin name detected | ### Tier 2: Tool Guard Hooks (16) | Hook | Event | Purpose | |------|-------|---------| | `commentChecker` | tool.execute.after | Block AI-slop comment patterns (binary: `@code-yeongyu/comment-checker`) | | `toolOutputTruncator` | tool.execute.after | Truncate oversized tool output | | `directoryAgentsInjector` | tool.execute.before | Inject dir-local AGENTS.md into context | | `directoryReadmeInjector` | tool.execute.before | Inject dir-local README.md into context | | `emptyTaskResponseDetector` | tool.execute.after | Detect empty task results | | `rulesInjector` | tool.execute.before | Conditional rules injection (AGENTS.md, .rules) | | `tasksTodowriteDisabler` | tool.execute.before | Disable TodoWrite when Sisyphus task system active | | `writeExistingFileGuard` | tool.execute.before | Require Read before Write/Edit on existing files | | `bashFileReadGuard` | tool.execute.before | Guard bash commands that read files (cat/head/tail) | | `readImageResizer` | tool.execute.after | Resize large images for context efficiency | | `todoDescriptionOverride` | tool.execute.before | Override todo item descriptions | | `webfetchRedirectGuard` | tool.execute.before | Guard webfetch redirect behavior | | `hashlineReadEnhancer` | tool.execute.after | Tag every Read output with `LINE#ID` content hashes | | `jsonErrorRecovery` | tool.execute.after | Detect JSON parse errors, inject correction reminder | | `fsyncSkipWarning` | tool.execute.after | Warn when fsync is skipped for atomic writes | ### Tier 3: Transform Hooks (5) | Hook | Event | Purpose | |------|-------|---------| | `claudeCodeHooks` | messages.transform | Claude Code settings.json compatibility | | `keywordDetector` | messages.transform | Detect ultrawork/search/analyze/team modes; inject mode-specific prompt | | `contextInjectorMessagesTransform` | messages.transform | Inject AGENTS.md/README.md into context | | `thinkingBlockValidator` | messages.transform | Validate thinking block structure | | `toolPairValidator` | messages.transform | Validate tool call/result pairing | ### Tier 4: Continuation Hooks (7) | Hook | Event | Purpose | |------|-------|---------| | `stopContinuationGuard` | chat.message | `/stop-continuation` command handler | | `compactionContextInjector` | session.compacted | Re-inject context after compaction | | `compactionTodoPreserver` | session.compacted | Preserve todos through compaction | | `todoContinuationEnforcer` | session.idle | **Boulder** — force continuation on incomplete todos | | `unstableAgentBabysitter` | session.idle | Monitor unstable agent behavior | | `backgroundNotificationHook` | event | Background task completion notifications | | `atlasHook` | event | Master orchestrator for boulder/background sessions | ### Tier 5: Skill Hooks (2) | Hook | Event | Purpose | |------|-------|---------| | `categorySkillReminder` | chat.message | Hint to load skills before invoking categories | | `autoSlashCommand` | chat.message | Auto-execute matching `/command` from user message | ### Team-mode Hooks (conditional, only when `team_mode.enabled: true`) | Hook | Tier | Registered In | Purpose | |------|------|---------------|---------| | `team-mode-status-injector` | Transform | [`create-transform-hooks.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/hooks/create-transform-hooks.ts) | Inject `` block into messages | | `team-mailbox-injector` | Transform | [`create-transform-hooks.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/hooks/create-transform-hooks.ts) | Pull pending team mailbox messages into agent context | | `team-tool-gating` | Tool Guard | [`create-tool-guard-hooks.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/hooks/create-tool-guard-hooks.ts) | Restrict `team_*` tools based on member role + permissions | | `team-idle-wake-hint` | event handler | [`src/plugin/event.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/event.ts) | Nudge idle team members back to work | | `team-lead-orphan-handler` | event handler | [`src/plugin/event.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/event.ts) | Detect lead departure → orphan members | | `team-member-error-handler` | event handler | [`src/plugin/event.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/event.ts) | React to member session errors | | `team-member-status-handler` | event handler | [`src/plugin/event.ts`](file:///Users/yeongyu/local-workspaces/omo/src/plugin/event.ts) | Track member status transitions | The 4 `team-session-events/` handlers live in `src/hooks/team-session-events/` (separate files: `team-idle-wake-hint.ts`, `team-lead-orphan-handler.ts`, `team-member-error-handler.ts`, `team-member-status-handler.ts`) and are wired into `src/plugin/event.ts` directly, not through a tier composer. ## STRUCTURE ``` hooks/ ├── shared/ # Cross-hook helpers (timing, prompt builders, etc.) ├── (52 hook directories — see tier tables above) ├── zauc-mocks-{bg,cache,hook,ws}, zauc-sync-mocks # 5 test mocks (NOT hooks; named for sort-order isolation) └── (each hook dir)/ ├── index.ts # createXXXHook factory + barrel ├── *.ts # implementation └── *.test.ts # bun:test ``` ## ADDING A NEW HOOK 1. `mkdir src/hooks/{name}` + `index.ts` exporting `createXXXHook(deps)` 2. Pick the right tier: - Session lifecycle? → `create-session-hooks.ts` - Pre/post tool? → `create-tool-guard-hooks.ts` - Message transform? → `create-transform-hooks.ts` - Continuation/idle? → `create-continuation-hooks.ts` - Skill awareness? → `create-skill-hooks.ts` - Team-mode-only? → register inside the team-mode conditional block 3. Add hook name to [`config/schema/hooks.ts`](file:///Users/yeongyu/local-workspaces/omo/src/config/schema/hooks.ts) `HookNameSchema` 4. Cover with co-located `*.test.ts` (given/when/then style) ## NOTES - **Tier order matters within a phase:** within Session tier the registration order in `create-session-hooks.ts` determines invocation order — earlier hooks see un-mutated input, later hooks see accumulated output. - **Mock files** (`zauc-mocks-*`, `zauc-sync-mocks`) are NOT hooks. They are placed inside `src/hooks/` purely so `bun:test` discovers them with the hook test fixtures. - **`atlasHook` vs `todoContinuationEnforcer`:** atlas handles boulder/ralph/subagent sessions, todoContinuationEnforcer handles the main Sisyphus session. Both fire on `session.idle` but check session type first. - **`runtime-fallback` vs `model-fallback`:** runtime-fallback is reactive (after error); model-fallback is proactive (chat.params). They operate independently.