Skip to content

rtk hook claude omits permissionDecision in updatedInput responses, causing Claude Code to silently ignore command rewrites #3018

Closed

Description

@JorgeArno

Environment
RTK version: 0.42.1 and 0.42.4 (confirmed present in both)
Platform: Windows 11, Git Bash
Claude Code hooks: PreToolUse matcher Bash → rtk hook claude
Summary
When RTK's Claude Code hook rewrites a Bash command, it emits a hookSpecificOutput object containing updatedInput but without the permissionDecision field. Per Claude Code's hook schema, updatedInput is only applied when permissionDecision is explicitly set (e.g. "allow"). Without it, Claude Code silently discards updatedInput and executes the original, unrewritten command — so RTK's token-saving rewrite never actually takes effect, even though the hook itself runs without error.

Steps to reproduce
Configure the Claude Code hook: PreToolUse → matcher Bash → command rtk hook claude.
Simulate the exact payload Claude Code sends on a Bash tool call:
echo '{"session_id":"test","transcript_path":"/tmp/x","cwd":"/some/path","hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"git status","description":"test"}}' | rtk hook claude
Observe the output.
Actual output
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecisionReason":"RTK auto-rewrite","updatedInput":{"command":"rtk git status","description":"test"}}}
Expected output
{"hookSpecificOutput":{"hookEventName":"PreToolUse","permissionDecision":"allow","permissionDecisionReason":"RTK auto-rewrite","updatedInput":{"command":"rtk git status","description":"test"}}}
Impact
Every Bash command routed through the hook runs unmodified, with zero token savings.
rtk gain / rtk gain --history never record real interceptions — the local history.db only ever contains a single stale fallback entry, regardless of how many Bash commands are run or how many times the Claude Code session is restarted.
This effectively makes the "transparent, 0 tokens overhead" rewrite behavior described in RTK's docs non-functional under current Claude Code versions.
Root cause
Per the Claude Code hooks documentation, updatedInput under hookSpecificOutput is only honored when permissionDecision is also present (typical value: "allow"). RTK's hook claude subcommand does not set this field.

Reference: Claude Code Hooks reference

Suggested fix
In the hook claude (and likely cursor, gemini, copilot, droid) subcommands, add "permissionDecision": "allow" to the hookSpecificOutput object whenever updatedInput is returned.

Activity

  1. pszymkowiak commented on Jul 16, 2026

    @pszymkowiak
    Collaborator

    wshm · Automated triage by AI

    🔍 Automated Triage

    🐛 Category bug
    🟠 Priority high
    🎯 Confidence 93%

    Summary

    rtk's Claude Code hook output omits the required permissionDecision field alongside updatedInput, so Claude Code silently discards the rewritten command and executes the original — nullifying RTK's token-saving rewrite; fix by adding "permissionDecision": "allow" whenever updatedInput is emitted.

    This looks like a simple fix that could be auto-resolved. Repo maintainers can use /wshm fix to attempt it.

    📁 Relevant files
    • src/hook/claude.rs

    Triaged automatically by wshm · This is an automated analysis, not a human review.

  2. pavelbe commented on Aug 8, 2026

    @pavelbe

    Fresh rtk 0.45.0 retest (Linux x86_64 / WSL2, 2026-08-08): the original
    single-command form now includes permissionDecision: "allow", so that part
    has changed since the issue was filed.

    A residual mixed multiline case still emits updatedInput without a decision:

    printf '%s' '{"tool_name":"Bash","tool_input":{"command":"git status\ntrue"}}' \
      | rtk hook claude

    Observed (abridged):

    {"updatedInput":{"command":"rtk git status\ntrue"}}

    For comparison, a fully rewritable two-line block (git status +
    git diff --stat) includes permissionDecision: "allow".

    I am not asserting that mixed blocks should be auto-allowed. The remaining
    question is whether Claude applies updatedInput without a decision on current
    supported versions, and the dry-run currently does not expose this distinction.
    I filed #3476 as a separate diagnostic request to make hook check report the
    actual host decision, rather than overloading this bug thread with a new UX
    surface.

  3. KuSh commented on Aug 9, 2026

    @KuSh
    Collaborator

    According to Claude himself

    When a PreToolUse hook returns updatedInput without a permissionDecision field, according to the documentation:

    The tool input is modified with the updatedInput values, but the tool call then proceeds through the normal permission flow (permission rules, auto-mode classifier, or user prompt).

    So if it's silently discarded that's because automode classifier or Claude permission rules discarded it. Forcing ask bypass Claude's permission flow, notably the "remember" part that's not possible with permissionDecision "ask"

    Do you have situations where it silently dropped a rewritten command that was genuine, not blocked by Claude's configured permission rules nor automode classifier?

  4. KuSh commented on Sep 5, 2026

    @KuSh
    Collaborator

    I'm closing this as it's working as intended.

    We only emit permissionDecision when a definitive decision can be made (for example, when Claude's permissions explicitly allow or deny a command). It isn't rtk's role to unconditionally allow everything; however, you can configure Claude's behavior to achieve that if it fits your needs.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions