Skip to content

feat(hooks): fire all lifecycle events (was only Pre/PostToolUse)#129

Merged
oratis merged 1 commit into
mainfrom
feat/hook-lifecycle-events
May 31, 2026
Merged

feat(hooks): fire all lifecycle events (was only Pre/PostToolUse)#129
oratis merged 1 commit into
mainfrom
feat/hook-lifecycle-events

Conversation

@oratis
Copy link
Copy Markdown
Owner

@oratis oratis commented May 31, 2026

Summary

The HookDispatcher handled all 9 event types, but the agent loop only ever dispatched PreToolUse + PostToolUse. So hooks a user configured for SessionStart, SessionEnd, UserPromptSubmit, Stop, SubagentStop, PreCompact, PostCompact, or Notification silently never ran (the gap surfaced in the plan-vs-state audit). This wires all of them.

Where each fires

Agent loop (agent.ts) — works for every host (CLI / desktop / LSP):

  • UserPromptSubmit — before the prompt is processed; appends any JSON additionalContext the hook returns. Top-level only (a sub-agent's prompt isn't a user prompt).
  • Stop — when the top-level run ends (end_turn / max_turns); depth-guarded so a sub-agent's end is reported by SubagentStop instead.
  • SubagentStop — when a Task sub-agent finishes.
  • PreCompact / PostCompact — around auto-compaction.

CLI hosts:

  • repl.tsSessionStart at startup, SessionEnd at exit, Notification after each turn (control returns to the user).
  • headless.tsSessionStart before the run, SessionEnd in finally (carries exitCode).

All dispatches are wrapped so a failing hook never breaks the agent.

Tests

+4 (agent.test.ts): UserPromptSubmit+Stop fire on a normal run; UserPromptSubmit additionalContext reaches the provider's user message; SubagentStop fires for a Task; PreCompact+PostCompact fire around compaction. Core suite 601 green. BEHAVIOR_PARITY SubagentStop + Notification 🔄→✅ — all 10 events now live.

🤖 Generated with Claude Code

The HookDispatcher handled all 9 event types but the agent loop only ever
dispatched PreToolUse + PostToolUse — so SessionStart/SessionEnd/
UserPromptSubmit/Stop/SubagentStop/PreCompact/PostCompact/Notification hooks
configured by users silently never ran. Now they all fire.

agent loop (packages/core/src/agent.ts) — works for every host (CLI/desktop/lsp):
- UserPromptSubmit: before the prompt is processed; appends any JSON
  `additionalContext` the hook returns. Top-level only (not sub-agent prompts).
- Stop: when the top-level run ends (end_turn / max_turns); depth-guarded so a
  sub-agent's end is signalled by SubagentStop instead.
- SubagentStop: when a Task sub-agent finishes.
- PreCompact / PostCompact: around auto-compaction.

CLI hosts:
- repl.ts: SessionStart at startup, SessionEnd at exit, Notification after each
  turn (control returns to the user).
- headless.ts: SessionStart before the run, SessionEnd in finally (with exitCode).

Tests: +4 (agent.test.ts) — UserPromptSubmit+Stop fire; UserPromptSubmit
additionalContext injection reaches the provider's user message; SubagentStop
fires for a Task; PreCompact+PostCompact fire around compaction. Core 601 green.
BEHAVIOR_PARITY: SubagentStop + Notification 🔄→✅ (all 10 events now live).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@oratis oratis merged commit 73713f6 into main May 31, 2026
3 checks passed
@oratis oratis deleted the feat/hook-lifecycle-events branch May 31, 2026 14:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant