[Feature Request] First-class deferred context injection via Stop hook (`continueWith` field)
Problem / Use Case
There's no supported way to chain multi-step workflows where context is automatically injected after Claude's response completes. This is needed for patterns like:
- Auto code review: User types
ECHO<CHECK>→ Claude responds → code reviewer automatically runs - Multi-phase workflows: Complete task → inject "now run tests" → Claude continues
- Skill chaining: One skill's output triggers another skill's input
Current Workaround
The only way to achieve this today is a Stop hook blocking hack:
UserPromptSubmithook parsesECHO<content>from the user's prompt, stores it in a session-scoped temp fileStophook reads the stored content and returns{"decision": "block", "reason": "<stored content>"}- Claude is forced to continue with the injected content as context
This works, but has real problems:
- Shows as an error in the UI (#12667)—
blockwas designed for error handling, not workflow continuation - Race conditions with parallel sessions (#15047, #15885)—state files must be keyed by
session_id, not project path - No infinite loop protection—need manual guards (
stop_hook_activechecks) - Fragile state management—temp files, cleanup logic, atomic writes via
mv - Overloaded semantics—
decision: "block"+reasonconflates "error, stop" with "continue with this context"
How It's Built Today (3 hooks)
# 1. echo-deferred.sh (UserPromptSubmit) — captures ECHO<content>
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
if [[ "$PROMPT" =~ ECHO\<([^>]+)\> ]]; then
echo "${BASH_REMATCH[1]}" > "/tmp/claude_echo_state_${SESSION_ID}"
fi
# 2. echo-stop.sh (Stop) — injects stored content by blocking
if [[ -f "$STATE_FILE" ]]; then
STORED=$(<"$STATE_FILE"); rm -f "$STATE_FILE"
jq -n --arg reason "$STORED" '{"decision":"block","reason":$reason}'
fi
# 3. checkend-deferred.sh (UserPromptSubmit) — alternative: inject on NEXT prompt
The key insight: Stop hook's block decision is being repurposed as a context injection mechanism, which is powerful but semantically wrong.
Proposed Solution
Add a continueWith field to the Stop hook output schema, specifically designed for workflow continuation (not error handling):
interface StopHookOutput {
decision: "approve" | "block";
reason?: string;
// NEW: Workflow continuation
continueWith?: string; // Inject this as Claude's next context
showAsError?: boolean; // Default false — no red error banner
}
Behavior
When continueWith is present:
- Claude's response completes normally
- Instead of stopping, Claude receives
continueWithas follow-up context - No error banner shown (unlike current
block+reason) - A subtle indicator (e.g., "Hook continuation…") replaces the error UX
- One-shot by default—
continueWithfires once, then Claude stops normally
Example: Auto Code Review
#!/bin/bash
# stop-hook.sh
STATE_FILE="/tmp/claude_echo_state_$(echo "$INPUT" | jq -r '.session_id')"
if [[ -f "$STATE_FILE" ]]; then
STORED=$(<"$STATE_FILE"); rm -f "$STATE_FILE"
jq -n --arg ctx "$STORED" '{"decision":"approve","continueWith":$ctx}'
fi
Now decision: "approve" means "response was fine" and continueWith means "but also do this next." Clean semantics.
Stretch Goal: Built-in ECHO<content> Primitive
If continueWith lands, Claude Code could natively support an ECHO<content> syntax in user prompts—no custom hooks needed:
Fix the login bug ECHO<CHECK>
Claude fixes the bug, then automatically runs whatever CHECK maps to. This would make deferred context injection a zero-config feature.
Why Not clearContext?
Issue #16659 proposes clearContext: true for the Ralph Wiggum loop pattern. That solves a different problem (context bloat across iterations). This proposal is orthogonal:
clearContext= "forget everything, start fresh with this prompt"continueWith= "keep context, inject this as a follow-up"
Both could coexist. You could even combine them: {"clearContext": true, "continueWith": "Start phase 2"}.
Session Isolation
A lesson from building this: state must be keyed by session_id, not project path. Multiple Claude Code sessions in the same directory will steal each other's state if keyed by project. The session_id field in hook input was the fix. If continueWith is built-in, this is handled automatically.
Related Issues
- #12667 — Stop hook shows "error" for intentional blocking (direct consequence of this hack)
- #15047 — Cross-session stop hook interference (race condition this pattern hits)
- #15885 — Multiple state files needed for session isolation
- #16659 —
clearContextfield proposal (orthogonal, complementary) - #16932 — Slash command chaining (similar goal, different mechanism)
- #10808 — Autonomous messages after SessionStart (related: proactive context injection)
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗