Feature Request: Add `updatedPrompt` support to `UserPromptSubmit` hook
Summary
PreToolUse hooks support updatedInput to modify tool arguments before execution, but UserPromptSubmit hooks have no equivalent mechanism to modify the user's prompt text before it reaches Claude. This creates an asymmetry in the hook system.
Current Behavior
UserPromptSubmit hooks can:
- ✅ Read the user's prompt (
promptfield in stdin JSON) - ✅ Block the prompt entirely (exit code 2)
- ✅ Add supplementary context (stdout text is appended)
- ❌ Modify/replace the prompt text itself
Meanwhile, PreToolUse hooks can modify tool inputs via updatedInput:
{
"hookSpecificOutput": {
"hookEventName": "PreToolUse",
"permissionDecision": "allow",
"updatedInput": { "command": "modified command here" }
}
}
Proposed Behavior
Add an updatedPrompt field to UserPromptSubmit hook output, following the same pattern as PreToolUse's updatedInput:
{
"hookSpecificOutput": {
"hookEventName": "UserPromptSubmit",
"updatedPrompt": "transformed prompt text here"
}
}
Use Cases
- Automatic context injection: Prepend project-specific context to every prompt
- Abbreviation/alias expansion: Expand shorthand commands into full instructions
- Prompt templating: Transform template syntax into full prompts
- Sensitive data masking: Strip or mask secrets before they reach the LLM
- Localization/translation: Auto-translate prompts for better model performance
- Prompt enhancement: Add structured formatting or constraints automatically
Example
#!/bin/bash
INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.prompt')
# Expand project-specific aliases and add context
TRANSFORMED="[Project: Node.js/TypeScript, DB: PostgreSQL] $PROMPT"
jq -n --arg p "$TRANSFORMED" '{
hookSpecificOutput: {
hookEventName: "UserPromptSubmit",
updatedPrompt: $p
}
}'
Rationale
Both PreToolUse and UserPromptSubmit follow the same "intercept before execution" pattern. Since PreToolUse already supports input modification via updatedInput, extending this pattern to UserPromptSubmit with updatedPrompt would be a natural and consistent addition to the hook system.
This is a capability that other AI coding tools (e.g., OpenCode) already support through their plugin systems (chat.message hook, chat.messages.transform), and it would significantly expand what users can build with Claude Code hooks.
13 Comments
Use case: CloudMask — real-time AWS identifier anonymization for Claude Code hooks
We built CloudMask, a Python library that anonymizes AWS infrastructure identifiers (resource IDs, account IDs, ARNs, IPs) using deterministic HMAC-SHA256 hashing before they reach Claude. It works as a Claude Code hook system:
mask-hook.pyintercepts file reads, anonymizes AWS identifiers, writes to shadow files, redirects Claude to read the shadow copy. This works perfectly withupdatedInput.demask-hook.pyreverses the anonymization when Claude writes back, restoring real values to the actual file.prompt-mask-hook.pyshould anonymize AWS identifiers the user types directly in prompts (e.g., "check whyvpc-0abc1234def56789ahas no route to NAT"). Currently we can only injectadditionalContextwith the masked version, but Claude still sees the original identifiers in the prompt.Without
updatedPrompt, there's a gap: files are fully anonymized via shadow copies, but anything the user pastes into the prompt reaches Claude in plaintext. TheadditionalContextworkaround is unreliable — Claude sees both the real and masked values and may use either.updatedPromptwould close this gap cleanly, making the anonymization boundary complete across files and prompts.A
UserPromptSubmithook can transform the user's prompt:Note:
additionalContextappends to the prompt rather than replacing it. TrueupdatedPromptsupport would require a native implementation.+1 — This would be incredibly useful. I'm building a translation hook that preprocesses Korean prompts before they reach Claude, and
updatedPromptis exactly what's needed. Currently usingadditionalContextas a workaround, but it's clunky — the original untransformed text stays visible and the model sometimes ignores the context. Native prompt substitution would make hook-based input pipelines first-class.Another use case: automatic input compression for token savings.
The caveman-talk plugin provides a skill that compresses Claude's output via prompt engineering (dropping articles, filler, hedging — three intensity levels). It also ships CLI tools (
caveman-compress/caveman-decompress) that compress input text through an OpenAI-compatible endpoint.The missing piece is connecting the two: there's no way to automatically pipe user input through
caveman-compressbefore it reaches the model. WithupdatedPrompt, aUserPromptSubmithook could transparently compress verbose prompts, reducing input token costs without the user needing to manually run the CLI tool and copy-paste the result.This would make the hook system symmetrical with
PreToolUse'supdatedInputand unlock a whole class of input preprocessing workflows.Another concrete use case: token-efficient translation plugin (TRmnl).
The plugin intercepts English prompts, translates to Chinese via DeepL, and wants Claude to receive only the Chinese — which expresses the same content in fewer tokens for longer prompts. Without prompt replacement, the English still occupies the user turn. The
additionalContextworkaround adds overhead instead of saving tokens, negating the purpose entirely.This pattern (input → compact representation → Claude sees only compact form) is a natural fit for
updatedPromptand would make a whole class of compression/transformation plugins viable.+1, with empirical confirmation. I tested the current
additionalContextcontract: when aUserPromptSubmithook returns content, Claude Code wraps it with a label that reads roughly"UserPromptSubmit hook additional context:". The model then treats the content as advisory rather than authoritative — even with prependedIMPORTANT: TREAT AS DIRECT USER INSTRUCTIONframing, the wrapper wins.The side-by-side test that isolates the wrapper as the variable: identical content delivered as the user's own typed prompt gets acted on; same content via the hook gets acknowledged but ignored.
updatedPromptwould close that gap and make the hook system actually viable for knowledge injection. Right now the only working approach is client-side injection (i.e., outside Claude Code's process), which works but feels like a workaround for something the hook system was clearly designed to handle.Full writeup with the test setup: https://cdelgado70.github.io/2026/05/06/skills-and-the-discovery-ceiling.html
A PII proxy upstream of the API makes the hook limitation irrelevant — data is cleaned before it reaches any tool. Works with any OpenAI-compatible client: https://github.com/crp4222/PrivAiTe
Wrote up the wrapper-authority story in detail — two empirical tests against current Claude Code, the comparison across the related issues (#28158, #37550, #22309), and a direct test that confirms
updatedPromptis silently dropped in user-installed~/.claude/settings.jsonhooks. The Python SDK example syntax this issue references doesn't currently work for the use case the issue targets, so this remains the canonical fix request to monitor.https://cdelgado70.github.io/2026/05/09/hooks-and-the-wrapper-authority-problem.html
Any update on this issue?
Need this for prompt optimisation
+1 this has so much potential for all the reasons listed and much more.
I built
better-promptplugin, which does prompt correction, translation, and enhancement. Basically the use cases above, minus the masking and templating stuff.As mentioned,
UserPromptSubmitcan't replace the prompt, so the plugin works around it with a block-and-paste trick. The hook rewrites the prompt via subagents, blocks the original so Claude never sees it, then copies the result to the clipboard and re-submits it after Claude replies —osascripton macOS,ydotoolon Linux.It works, but it's held together with tape. The clipboard gets clobbered, tmux and embedded terminals don't cooperate, and the timing is fiddly. All of that disappears with a native
updatedPrompt.Worth noting the
OpenCodeversion of the same plugin is way simpler — itschat.messagehook just transforms the prompt directly. No clipboard, no keystroke hacks.One thing I'd want alongside
updatedPrompt: a flag so the rewritten prompt can be shown for review instead of being swapped in and sent silently:replaceText: trueswaps it silently and send to the Claude.falsedrops it back into the prompt input box so the user can review or tweak it before sending. That would need a sentinel, though, so the reviewed prompt doesn't get reprocessed in a loop when it's resubmitted.We need this for enterprise DLP. Proxies are out of question. Unfortunately until claude implements this 2 line feature some orgs just won't be able to use claude... But I guess this has something to do with token compression 😊