fix(hooks): RepeatDetection injects warning via stdout, not stderr+exit 2#1291
fix(hooks): RepeatDetection injects warning via stdout, not stderr+exit 2#1291brycemagera wants to merge 1 commit into
Conversation
…it 2 RepeatDetection.hook.ts is a UserPromptSubmit hook. On a >=60% similarity match it called process.stderr.write() then process.exit(2). For UserPromptSubmit hooks, exit code 2 blocks AND erases the prompt, and routes stderr to the user only -- the model never receives the message. So the repeat-detection warning the hook is meant to inject into the model's context never arrives, and the user's resent message is silently dropped. The correct channel for UserPromptSubmit context injection is exit 0 with the text on stdout: stdout is appended to the model's context and the prompt proceeds normally. Swaps process.stderr.write -> process.stdout.write and process.exit(2) -> process.exit(0) in the trigger branch. Detection logic (threshold, tokenizer, trigrams) is unchanged.
|
Production confirmation, plus the full causal chain for why this PR matters well beyond restoring the hook's intent: #1143 (comment) Short version: in SDK channels (the v5 Telegram/iMessage Pulse modules), this hook's exit-2 doesn't just fail to warn — it silently erases the user's prompt (
|
Problem
RepeatDetection.hook.tsis aUserPromptSubmithook. On a >=60% similarity match it callsprocess.stderr.write(...)thenprocess.exit(2).For
UserPromptSubmithooks, exit code 2 blocks and erases the prompt, and routes stderr to the user only -- the model never receives the message. Two consequences:The file header comment states the hook "injects a high-priority WARNING into the model's context forcing re-reading of the user's message." The implementation does the opposite of that intent.
Fix
The correct channel for
UserPromptSubmitcontext injection is exit 0 with the text on stdout -- stdout from aUserPromptSubmithook is appended to the model's context, and the prompt proceeds normally.This swaps
process.stderr.write->process.stdout.writeandprocess.exit(2)->process.exit(0)in the trigger branch. Detection logic (the 0.6 threshold, tokenizer, and trigram similarity) is unchanged. 7 insertions, 4 deletions, one file.Testing
bun buildon the hook: clean.UserPromptSubmitstdin and live in a Claude Code session. Before the fix, the same message was erased before reaching the model.Reference
Claude Code hooks documentation,
UserPromptSubmitexit-code semantics: exit 2 blocks/erases the prompt with stderr shown to the user only; exit 0 stdout is added to the model's context.