Feature Request: Add `updatedPrompt` support to `UserPromptSubmit` hook

Status Open
Maintainer reply None cached
Activity 13 comments · opened Feb 21, 2026

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 (prompt field 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.

View original on GitHub ↗

13 Comments

sam-fakhreddine · 5 months ago

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:

  • PreToolUse (Read/Write/Edit)mask-hook.py intercepts file reads, anonymizes AWS identifiers, writes to shadow files, redirects Claude to read the shadow copy. This works perfectly with updatedInput.
  • PostToolUse (Write/Edit)demask-hook.py reverses the anonymization when Claude writes back, restoring real values to the actual file.
  • UserPromptSubmitprompt-mask-hook.py should anonymize AWS identifiers the user types directly in prompts (e.g., "check why vpc-0abc1234def56789a has no route to NAT"). Currently we can only inject additionalContext with 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. The additionalContext workaround is unreliable — Claude sees both the real and masked values and may use either.

updatedPrompt would close this gap cleanly, making the anonymization boundary complete across files and prompts.

yurukusa · 5 months ago

A UserPromptSubmit hook can transform the user's prompt:

INPUT=$(cat)
PROMPT=$(echo "$INPUT" | jq -r '.userPrompt // empty')
MODIFIED=$(echo "$PROMPT" | sed 's/\bcc\b/Claude Code/g; s/\bpr\b/pull request/g')
if [ "$MODIFIED" != "$PROMPT" ]; then
    jq -n --arg m "[Expanded] $MODIFIED" '{"hookSpecificOutput":{"hookEventName":"UserPromptSubmit","additionalContext":$m}}'
fi
exit 0

Note: additionalContext appends to the prompt rather than replacing it. True updatedPrompt support would require a native implementation.

j0j1j2 · 4 months ago

+1 — This would be incredibly useful. I'm building a translation hook that preprocesses Korean prompts before they reach Claude, and updatedPrompt is exactly what's needed. Currently using additionalContext as 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.

sjvrensburg · 4 months ago

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-compress before it reaches the model. With updatedPrompt, a UserPromptSubmit hook 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's updatedInput and unlock a whole class of input preprocessing workflows.

vauugnn · 4 months ago

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 additionalContext workaround 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 updatedPrompt and would make a whole class of compression/transformation plugins viable.

cdelgado70 · 3 months ago

+1, with empirical confirmation. I tested the current additionalContext contract: when a UserPromptSubmit hook 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 prepended IMPORTANT: TREAT AS DIRECT USER INSTRUCTION framing, 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. updatedPrompt would 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

crp4222 · 3 months ago

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

cdelgado70 · 3 months ago

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 updatedPrompt is silently dropped in user-installed ~/.claude/settings.json hooks. 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

yuvalo1212 · 3 months ago

Any update on this issue?

sneh-deep178 · 2 months ago

Need this for prompt optimisation

JeronimoColon · 2 months ago

+1 this has so much potential for all the reasons listed and much more.

objctp · 2 months ago

I built better-prompt plugin, which does prompt correction, translation, and enhancement. Basically the use cases above, minus the masking and templating stuff.

As mentioned, UserPromptSubmit can'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 — osascript on macOS, ydotool on 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 OpenCode version of the same plugin is way simpler — its chat.message hook 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:

{
    "hookSpecificOutput": {
      "hookEventName": "UserPromptSubmit",
      "updatedPrompt": "...",
      "replaceText": false
    }
}

replaceText: true swaps it silently and send to the Claude. false drops 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.

mnns · 1 month ago

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 😊