[FEATURE] Stop/PreCompact/PostCompact hooks need invisible model-context injection (parity with SessionStart/PreToolUse)

Resolved 💬 3 comments Opened Apr 19, 2026 by sstraus Closed May 26, 2026

Summary

Stop, PreCompact, and PostCompact hooks have no way to inject model-only context invisible to the user. The Stop hook can only emit decision: 'block', reason: '...', where reason is rendered in the user's chat. PreCompact/PostCompact have similarly limited output. This forces plugin authors into a bad trade-off:

  • Inject scaffolding (recap requests, continuation prompts, memory synthesis cues) → spam the user's chat with prompt-engineering text intended only for Claude
  • Drop the feature entirely

In contrast, SessionStart, PreToolUse, PostToolUse, and UserPromptSubmit all support hookSpecificOutput.additionalContext, which is added to Claude's context invisibly.

Verified schema (CC 2.1.114, docs scraped 2026-04-19)

| Hook | hookSpecificOutput.additionalContext |
|------|----------------------------------------|
| PreToolUse | ✅ supported |
| PostToolUse | ✅ supported |
| UserPromptSubmit | ✅ supported |
| SessionStart | ✅ supported |
| Stop | ❌ rejected by validator |
| PreCompact | ❌ rejected |
| PostCompact | ❌ no event-specific output at all |

Submitting {decision: 'block', hookSpecificOutput: {hookEventName: 'Stop', additionalContext: '...'}} produces:

Stop hook error: Hook JSON output validation failed — (root): Invalid input

Why this matters

Plugin authors building on top of CC (wiz/lansweeper, swarms, custom workflows) routinely need to:

  1. End-of-turn recap requests: ask the model to summarize what changed without spamming the user's chat with the prompt itself
  2. Continuation prompts in unattended mode: re-inject task queue/story state to keep the model on track when it tries to stop prematurely
  3. Behavior-correction nudges: remind the model of constraints it just violated (e.g., \"you marked something pre-existing without verifying\") without surfacing the rule text to the user
  4. Post-compact context restoration: re-inject active story/plan after compaction destroys it

Today every one of these either (a) becomes visible noise in the user's chat, (b) requires deferring injection to UserPromptSubmit on the next turn (adds latency, breaks immediacy), or (c) is silently dropped.

Proposed fix

Add hookSpecificOutput.additionalContext: string (optional) support to:

  • Stop (with hookSpecificOutput.hookEventName: 'Stop')
  • PreCompact (with hookSpecificOutput.hookEventName: 'PreCompact')
  • PostCompact (with hookSpecificOutput.hookEventName: 'PostCompact')

Semantics identical to existing supported events: text is appended to Claude's next-turn context, not rendered in the user's transcript. The schema validator should accept this shape and the renderer should suppress display.

For Stop specifically: keep decision: 'block', reason: '...' as the visible-to-user path (unchanged). additionalContext would be the invisible-to-user path. Both can coexist as they do for PostToolUse.

Bigger picture

CC is a powerful base for plugins, but the current Stop/PreCompact/PostCompact constraints push every plugin author into the same bad workaround. Plugin authors who want to customize the workflow without sharing Anthropic's default UX assumptions need a clean way to inject model-only context across all hook events. Otherwise CC ends up looking like \"a container of constant on-screen errors\" from the user's perspective — every turn ends with a Stop hook error: ... block, every customization leaks prompt-engineering text into the chat.

The asymmetry across hook events also makes the API harder to learn (\"why does it work in SessionStart but fail in Stop?\"). Adding additionalContext uniformly removes that wart.

Workaround status

For wiz I am currently downgrading to top-level reason (visible to user) and accepting the cost, while documenting the constraint in the plugin's CLAUDE.md. Reproducible diff and tests on request.

Repro

echo '{}' | hook_script_that_emits_stop_with_additionalContext

Output:

Stop hook error: Hook JSON output validation failed — (root): Invalid input

Expected: text is appended to model context for next turn, no user-visible message.

View original on GitHub ↗

This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗