Skip to content

discover over-reports missed savings when PreToolUse hook is active #789

@wkup

Description

@wkup

Context

Using rtk v0.31.0 with the official Claude Code PreToolUse hook (rtk rewrite).
The hook is correctly rewriting commands — rtk gain confirms 567 commands processed with 51.8K tokens saved (54.5%).

Problem 1

rtk discover scans Claude Code session history and matches against original command text, not the actually-executed commands. When the PreToolUse hook rewrites find ...rtk find ..., the session log still records the original find command.

Result: discover reports these already-rewritten commands as "missed savings":

Scanned: 125 sessions (last 30 days), 1841 Bash commands
Already using RTK: 6 commands (0%)

MISSED SAVINGS:                                                                                                                                                    
find       471    rtk find     ~137.0K tokens
git add    181    rtk git      ~33.2K tokens                                                                                                                       
grep -r    169    rtk grep     ~19.4K tokens                                                                                                                     
ls -la     189    rtk ls       ~4.4K tokens
composer     8    rtk composer ~4.1K tokens                                                                                                                        
cat         38    rtk read     ~2.8K tokens
tree         9    rtk tree     ~1.4K tokens                                                                                                                        
Total: 1066 commands -> ~202.4K tokens saveable                                                                                                                    

Most of these 1066 "missed" commands were actually handled by the hook. The 6 "already using RTK" only counts commands where the LLM explicitly wrote rtk ... in the Bash call.

I confirmed that rtk rewrite does handle all the "missed" commands correctly:

cat /tmp/file.txt     → rtk read /tmp/file.txt     ✅                                                                                                             
composer update       → rtk composer update        ✅                                                                                                              
tree -L 2             → rtk tree -L 2              ✅
git status            → rtk git status             ✅                                                                                                              
git log --oneline -5  → rtk git log --oneline -5   ✅                                                                                                              
git diff              → rtk git diff               ✅
find ... -name *.php  → rtk find ...               ✅                                                                                                              
grep -r 'pattern' src → rtk grep ...               ✅                                                                                                              
ls -la                → rtk ls -la                 ✅                                                                                                              

Expected behavior

discover should account for hook-based rewrites. Possible approaches:

  • Cross-reference with rtk gain data (which logs all actually-processed commands)
  • Detect that the hook is configured (check ~/.claude/settings.json for rtk-rewrite in PreToolUse hooks) and adjust the report
  • Show a separate "likely handled by hook" category instead of "missed"

Problem 2: high-volume commands not handled by rtk rewrite

These commands fail rtk rewrite (exit 1) but could be mapped to existing generic handlers:

Command Count Existing handler Notes
php vendor/bin/pest ... 276 rtk test Test runner — verbose output with stack traces
./vendor/bin/pest ... 14 rtk test Direct invocation
vendor/bin/phpstan analyse ... 12 rtk err Static analysis — can output hundreds of lines
composer show ... 3 rtk composer composer update works, but not show

Local workaround

Until native support is added, I worked around this with a second PreToolUse hook (local-rewrite.sh) placed before rtk-rewrite.sh in the Claude Code
hook chain. It doesn't modify rtk-rewrite.sh (no integrity conflict):

#!/usr/bin/env bash
# local-rewrite.sh — maps unhandled commands to generic rtk handlers
# Runs BEFORE rtk-rewrite.sh in the PreToolUse hook chain.

command -v jq &>/dev/null || exit 0                                                                                                                                

INPUT=$(cat)                                                                                                                                                       
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

[ -z "$CMD" ] && exit 0

case "$CMD" in
  "php vendor/bin/pest "*)              REWRITTEN="rtk test $CMD" ;;
  "./vendor/bin/pest "*)                REWRITTEN="rtk test $CMD" ;;                                                                                               
  "vendor/bin/pest "*)                  REWRITTEN="rtk test $CMD" ;;
  "vendor/bin/phpstan analyse"*)        REWRITTEN="rtk err $CMD" ;;                                                                                                
  "./vendor/bin/phpstan analyse"*)      REWRITTEN="rtk err $CMD" ;;                                                                                                
  "composer show"*)                     REWRITTEN="rtk composer ${CMD#composer }" ;;
  *) exit 0 ;;                                                                                                                                                     
esac                                                      
                                                                                                                                                                   
ORIGINAL_INPUT=$(echo "$INPUT" | jq -c '.tool_input')     
UPDATED_INPUT=$(echo "$ORIGINAL_INPUT" | jq --arg cmd "$REWRITTEN" '.command = $cmd')
                                                                                                                                                                   
jq -n --argjson updated "$UPDATED_INPUT" '{
  "hookSpecificOutput": {                                                                                                                                          
    "hookEventName": "PreToolUse",                        
    "permissionDecision": "allow",
    "permissionDecisionReason": "Local rtk rewrite",
    "updatedInput": $updated                                                                                                                                       
  }
}'                                                                                                                                                                 

settings.json config (local hook before rtk hook):

"PreToolUse": [                                           
  { "hooks": [{ "command": "~/.claude/hooks/local-rewrite.sh", "type": "command" }], "matcher": "Bash" },
  { "hooks": [{ "command": "~/.claude/hooks/rtk-rewrite.sh", "type": "command" }], "matcher": "Bash" }
]                                                                                                                                                                  

Ideally, rtk rewrite could handle these mappings natively (pest/phpstan → rtk test/rtk err) so this workaround wouldn't be needed.

Environment

  • rtk 0.31.0
  • Claude Code with PreToolUse hook via rtk rewrite
  • Linux (Ubuntu)

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingeffort-medium1-2 jours, quelques fichiersenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions