Hook Engine + Chained Command Rewriting (PR #131 Part 1)#156
Hook Engine + Chained Command Rewriting (PR #131 Part 1)#156ahundt wants to merge 232 commits intortk-ai:masterfrom
Conversation
feat: add pnpm support + fix git argument parsing for modern stacks
Add Vitest test runner support with 99.6% token reduction
adding benchmark script for local performance monitoring in dev
Release v0.2.1
Add installation guide for AI coding assistants
Add utils.rs with common utilities used across modern JavaScript tooling commands: - truncate(): Smart string truncation with ellipsis - strip_ansi(): Remove ANSI escape codes from output - execute_command(): Centralized command execution with error handling These utilities enable consistent output formatting and filtering across multiple command modules. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add comprehensive support for modern JS/TS development stack: Commands added: - rtk lint: ESLint/Biome output with grouped rule violations (84% reduction) - rtk tsc: TypeScript compiler errors grouped by file (83% reduction) - rtk next: Next.js build output with route/bundle metrics (87% reduction) - rtk prettier: Format checker showing only files needing changes (70% reduction) - rtk playwright: E2E test results showing failures only (94% reduction) - rtk prisma: Prisma CLI without ASCII art (88% reduction) Features: - Auto-detects package managers (pnpm/yarn/npm/npx) - Preserves exit codes for CI/CD compatibility - Groups errors by file and error code for quick navigation - Strips verbose output while retaining critical information Total: 6 new commands, ~2,000 LOC Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Document the 6 new commands and shared utils module in CHANGELOG.md. Focuses on token reduction metrics and CI/CD compatibility. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add benchmarks for the 6 new commands in scripts/benchmark.sh: - tsc: TypeScript compiler error grouping - prettier: Format checker with file filtering - lint: ESLint/Biome grouped violations - next: Next.js build metrics extraction - playwright: E2E test failure filtering - prisma: Prisma CLI without ASCII art All benchmarks are conditional (skip if tools not available or not applicable to current project). Tests only run on projects with package.json and relevant configuration files. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implements heuristic calculation of monthly quota savings percentage with support for Pro, Max 5x, and Max 20x subscription tiers. Features: - --quota flag displays monthly quota analysis - --tier <pro|5x|20x> selects subscription tier (default: 20x) - Heuristic based on ~44K tokens/5h Pro baseline - Estimates: Pro=6M, 5x=30M, 20x=120M tokens/month - Clear disclaimer about rolling 5-hour windows vs monthly caps Example output for Max 20x: Subscription tier: Max 20x ($200/mo) Estimated monthly quota: 120.0M Tokens saved (lifetime): 356.7K Quota preserved: 0.3% Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add modern JavaScript tooling support (lint, tsc, next, prettier, playwright, prisma)
Add rtk gh command for GitHub CLI operations with intelligent output filtering: Commands: - rtk gh pr list/view/checks/status: PR management (53-87% reduction) - rtk gh issue list/view: Issue tracking (26% reduction) - rtk gh run list/view: Workflow monitoring (82% reduction) - rtk gh repo view: Repository info (29% reduction) Features: - Level 1 optimizations (default): Remove header counts, @ prefix, compact mergeable status (+12-18% savings, zero UX loss) - Level 2 optimizations (--ultra-compact flag): ASCII icons, inline checks format (+22% total savings on PR view) - GraphQL response parsing and grouping - Preserves all critical information for code review Token Savings (validated on production repo): - rtk gh pr view: 87% (24.7K → 3.2K chars) - rtk gh pr checks: 79% (8.9K → 1.8K chars) - rtk gh run list: 82% (10.2K → 1.8K chars) Global --ultra-compact flag added to enable Level 2 optimizations across all GitHub commands. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add utils.rs as Key Architectural Component - Expand Module Responsibilities table (7→17 modules) - Document PR rtk-ai#9 in Fork-Specific Features section - Include token reduction metrics for all new commands
Change dtolnay/rust-action to dtolnay/rust-toolchain (correct name)
feat: add GitHub CLI integration (depends on rtk-ai#9)
feat: add quota analysis with multi-tier support
## Release automation - Add release-please workflow for automatic semantic versioning - Configure release.yml to only trigger on tags (avoid double-release) ## Benchmark automation - Extend benchmark.yml with README auto-update - Add permissions for contents and pull-requests writes - Auto-create PR with updated metrics via peter-evans/create-pull-request - Add scripts/update-readme-metrics.sh for CI integration ## Verification - ✅ Workflows ready for CI/CD pipeline - ✅ No breaking changes to existing functionality Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: CI/CD automation (versioning, benchmarks, README auto-update)
…s--master--components--rtk chore(master): release 0.3.0
## Fixes ### Lint crash handling - Add graceful error handling for linter crashes (SIGABRT, OOM) - Display warning message when process terminates abnormally - Show first 5 lines of stderr for debugging context ### Grep command - Add --type/-t flag for file type filtering (e.g., --type ts, --type py) - Passes --type argument to ripgrep for efficient filtering ### Find command - Add --type/-t flag for file/directory filtering - Default: "f" (files only) - Options: "f" (file), "d" (directory) ## Testing - ✅ cargo check passes - ✅ cargo build --release succeeds - ✅ rtk grep --help shows --file-type flag - ✅ rtk find --help shows --file-type flag with default ## Breaking Changes None - all changes are backwards compatible additions Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…d-bugs fix: improve command robustness and flag support
…s--master--components--rtk chore(master): release 0.3.1
## Overview Complete architectural documentation (1133 lines) covering all 30 modules, design patterns, and extensibility guidelines. ## Critical Fixes (🔴) - ✅ Module count: 30 documented (not 27) - added deps, env_cmd, find_cmd, local_llm, summary, wget_cmd - ✅ Language: Fully translated to English for consistency with README.md - ✅ Shared Infrastructure: New section documenting utils.rs and package manager detection - ✅ Exit codes: Correct documentation (git.rs preserves exit codes for CI/CD) - ✅ Database: Correct path ~/.local/share/rtk/history.db (not tracking.db) ## Important Additions (🟡) - ✅ Global Flags Architecture: Verbosity (-v/-vv/-vvv) and ultra-compact (-u) - ✅ Complete patterns: Package manager detection, exit code preservation, lazy static regex - ✅ Config system: TOML format documented - ✅ Performance: Verified binary size (4.1 MB) and estimated overhead - ✅ Filter levels: Before/after examples with Rust code ## Bonus Improvements (🟢) - ✅ Table of Contents (12 sections) - ✅ Extensibility Guide (7-step process for adding commands) - ✅ Architecture Decision Records (Why Rust? Why SQLite?) - ✅ Glossary (7 technical terms) - ✅ Module Development Pattern (template + 3 common patterns) - ✅ 15+ ASCII diagrams for visual clarity ## Stats - Lines: 1133 (+118% vs original 520) - Sections: 12 main + subsections - Code examples: 10+ Rust/bash snippets - Accuracy: 100% verified against source code Production-ready for new contributors, experienced developers, and LLM teams. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Rust binary replaces 204-line bash script as Claude Code PreToolUse hook. Adds rtk hook claude, rtk run -c, and Windows support via cfg!(windows). Closes rtk-ai#112 (chained commands missed). Based on updated master (0b0071d) which includes: - Hook audit mode (rtk-ai#151) - Claude Code agents and skills (d8b5bb0) - tee raw output feature (rtk-ai#134) Migrated from feat/rust-hooks (571bd86) with conflict resolution for: - src/main.rs: Commands enum (preserved both hook audit + our hook commands) - src/init.rs: Hook registration (integrated both approaches) New files (src/cmd/ module): - mod.rs: Module declarations (10 modules, excluding safety/trash/gemini for PR 1) - hook.rs: Shared hook decision logic (21 tests, 3 safety tests removed for PR 2) - claude_hook.rs: Claude Code JSON protocol handler (18 tests) - lexer.rs: Quote-aware tokenizer (28 tests) - analysis.rs: Chain parsing and shellism detection (10 tests) - builtins.rs: cd/export/pwd/echo/true/false (8 tests) - exec.rs: Command executor with recursion guard (22 tests, safety dispatch removed for PR 2) - filters.rs: Output filter registry (5 tests) - predicates.rs: Context predicates (4 tests) - test_helpers.rs: Test utilities Modified files: - src/main.rs: Added Commands::Run, Commands::Hook, HookCommands enum, routing - src/init.rs: Changed patch_settings_json to use rtk hook claude binary command - hooks/rtk-rewrite.sh: Replaced 204-line bash script with 4-line shim (exec rtk hook claude) - Cargo.toml: Added which = 7 for PATH resolution - INSTALL.md: Added Windows installation section Windows support: - exec.rs:175-176: cfg!(windows) selects cmd /C vs sh -c for shell passthrough - predicates.rs:26: USERPROFILE fallback for Windows home directory - No bash, node, or bun dependency - rtk hook claude is a compiled Rust binary Tests: All 541 tests pass
|
Thanks Andrew for the clean split from #131, and the architecture is genuinely well thought out — the lexer, fail-open design, RAII guard, and deny(clippy::print_stdout) are all However, I have a critical concern that I think is a regression: The hook wraps everything in rtk run -c '...' instead of routing to specialized filters. The current bash hook does: This PR does: The entire value of RTK is the specialized filters per command. rtk cargo test shows only failures (90% token reduction). rtk run -c 'cargo test' just runs the command and strips ANSI Did you test this with real commands and compare token savings? I'd expect rtk run -c 'cargo test' to produce significantly more output than rtk cargo test. What I'd expect instead: A few other items:
The foundation here is solid. The lexer, chain parsing, and fail-open protocol handling are exactly what we need. But the command routing needs to preserve the specialized filters — Happy to discuss the best approach. Would it make sense to have rtk run -c detect known commands and dispatch to their specialized modules internally? |
|
I tested the PR locally and found a critical issue: the hook routes everything through rtk run -c, bypassing all specialized filters. Here's what I measured:
|
…commands Replaces stub check_for_hook_inner with full tokenize+native-path dispatch. Adds route_native_command() with replace_first_word/route_pnpm/route_npx helpers to route single parsed commands to optimized RTK subcommands. Chains (&&/||/;) and shellisms still use rtk run -c. No safety integration (PR rtk-ai#157 adds that). Mirrors ~/.claude/hooks/rtk-rewrite.sh routing table. Corrects shell script vitest double-run bug for pnpm vitest run flags.
|
Thanks @pszymkowiak, i found that too independently and just pushed a fix, hopefully it should work now! also there is one potential dep that could be added optionally to robustify things and improve token reduction but i left it out in favor of a small regex at this time to minimize deps, strip-ansi-escapes strips ANSI escape sequences from byte sequences using the vte terminal parser — handles the full escape sequence space (CSI, OSC, private-mode params, two-byte sequences) that a hand-rolled regex misses. https://crates.io/crates/strip-ansi-escapes |
rtk has no `tail` subcommand — routing to "rtk tail" was silently broken (rtk would error "unrecognized subcommand"). Remove the Route entry so the command falls through to `rtk run -c '...'` correctly. Move the log-tailing test cases from test_routing_native_commands (which asserted the broken path) into test_routing_fallbacks_to_rtk_run where they correctly verify the rtk-run-c fallback behavior.
Port tests added during the ROUTES table integration that were missing from the v2 worktree: registry.rs: - 12 classify tests for Python/Go commands (pytest, go×4, ruff×2, pip×3, golangci-lint) that verify PATTERNS/RULES and ROUTES alignment - 11 lookup tests (test_lookup_*, test_no_duplicate_binaries_in_routes, test_lookup_is_o1_consistent) that verify O(1) HashMap routing hook.rs: - Extend test_routing_native_commands from 20 to 47 cases covering all ROUTES entries: docker, kubectl, curl, eslint, tsc, prettier, playwright, prisma, pytest, golangci-lint, ruff, pip, gh variants - Add test_routing_subcommand_filter_fallback (14 cases) verifying that Only[] subcommand filters correctly reject unmatched subcommands Total: 545 → 569 tests (+24)
|
ok i made it a much better router that goes in registry.rs so all the "add a new tool" code is in one place, it should be much more efficient than the previous two versions and easier to extend to better address that bug you mentioned. |
Three integration tests that simulate the full hook pipeline from scratch: raw command → check_for_hook (lexer + router) → rewritten rtk cmd → execute both → assert rtk output has fewer tokens than raw Tests: - test_e2e_git_status_saves_tokens: verifies ≥40% savings vs raw git status - test_e2e_ls_saves_tokens: verifies ≥40% savings vs raw ls -la - test_e2e_git_log_saves_tokens: verifies ≤5% overhead (already-compact input) Each test first asserts the lexer+router produced the correct rewrite, then executes both commands and compares whitespace-delimited token counts. Run with: cargo test e2e -- --ignored Requires: cargo install --path . (rtk on PATH) + git repo
|
@pszymkowiak I also added a few small e2e tests to confirm the behavior actually uses the rtk internals and those pass. |
Port all streaming and pipe-as-filter infrastructure from
feat/multi-platform-hooks onto feat/rust-hooks-v2:
- src/stream.rs (new): StreamFilter trait, LineFilter<F>, FilterMode,
StdinMode, StreamResult, run_streaming(), status_to_exit_code(),
ChildGuard RAII — bidirectional subprocess shim with correct
exit-code propagation (0–254, 128+N for signal kills), SIGPIPE
handling, 1MB raw cap, and thread-safe stdin/stdout/stderr pipes
- src/pipe_cmd.rs (new): rtk pipe --filter <name> — resolve_filter()
maps cargo-test/pytest/go-test/go-build/tsc/vitest/grep/rg/find/fd/
git-log/git-diff/git-status to their filter fns; auto_detect_filter()
heuristically picks filter from stdin content; find/fd path detection
requires ≥3 path-like lines to avoid false positives
- src/cmd/exec.rs: migrate execute()/execute_inner()/run_native()/
spawn_with_filter()/run_passthrough() from Result<bool> → Result<i32>;
spawn_with_filter now uses run_streaming + get_filter_mode instead of
.output() + apply_to_string; run_passthrough streams per-line ANSI
strip instead of buffering; exit code 127 for command-not-found;
all tests updated (assert!(r) → assert_eq!(r, 0))
- src/cmd/filters.rs: add get_filter_mode() mapping binaries to
FilterMode (Streaming with ANSI+truncate for ls/find/grep/rg/fd;
Buffered for cargo/go/pytest/git; Passthrough for unknown)
- src/main.rs: add mod stream; mod pipe_cmd; Commands::Pipe{filter,
passthrough}; --passthrough global flag; Run exit code fixed
(success → 0, if code != 0 { exit(code) }); no Gemini (separate PR)
- src/grep_cmd.rs: extract pub(crate) filter_grep_raw() (handles both
3-part file:line:content and 2-part file:content formats) and
pub(crate) filter_find_output() (groups paths by dir + ext counts)
- src/cargo_cmd.rs, src/go_cmd.rs, src/pytest_cmd.rs: add
CargoTestStreamFilter, GoTestStreamFilter, PyTestStreamFilter
implementing StreamFilter for progressive line-by-line filtering
- src/git.rs, src/tsc_cmd.rs, src/vitest_cmd.rs: pub(crate) visibility
on filter fns needed by pipe_cmd; no logic changes
- src/cmd/analysis.rs: 8 new edge-case tests for piped find/grep/rg
routing; confirms quoted pipes are not treated as shell operators
667 tests pass; 0 failures. cargo fmt + clippy clean.
Lock in tokenisation and routing behaviour for common Claude Code shell
patterns that must reliably go through shell passthrough:
analysis.rs (18 new tests):
- stdout/stderr redirects: > /dev/null, 2>/dev/null, 2> /dev/null
- compound FD redirects: 2>&1, 1>&2 (& → Shellism triggers needs_shell)
- combined redirect chains: >/dev/null 2>&1
- append redirect: >> /tmp/output.txt
- pipe suffixes: | tail, | cat, | tee, | wc
- pipe-find/grep: find | grep, rg | head, grep > file
- unquoted glob: find . -name *.rs (Shellism triggers shell)
- quoted pipe in grep arg must NOT trigger shell
- operators &&, ||, ; must NOT trigger shell alone (handled natively)
- RTK e2e: cargo test 2>&1 | tee → complex redirect+pipe uses passthrough
lexer.rs (7 new tests):
- verify 2>/dev/null → Arg("2") + Redirect(">") (not Redirect("2>"))
- verify 2>&1 → Arg("2") + Redirect(">") + Shellism("&") + Arg("1")
- verify >> produces Redirect token
- verify | produces Pipe token (not Arg)
- verify > produces Redirect token (not Arg)
- verify 2> /dev/null with space also produces Redirect
These tests lock in "accidentally correct" compound FD redirect behaviour:
2>&1 works via Shellism("&") detection, not explicit FD redirect parsing.
Future refactors cannot silently regress this without test failure.
Two new capabilities that expand RTK native filtering:
1. Env-prefix routing (VAR=val cmd → VAR=val rtk cmd)
Previously: GIT_PAGER=cat git status → rtk run -c '...' (passthrough)
Now: GIT_PAGER=cat git status → GIT_PAGER=cat rtk git status (RTK filter)
Shell sets the env var for the rtk subprocess; RTK filter applies.
Also routes: RUST_LOG=debug cargo test → RUST_LOG=debug rtk cargo test
Multi-var: NODE_ENV=test CI=1 npx vitest run → ... rtk vitest run
Unknown commands still fall back to rtk run -c (safe).
2. Suffix-aware routing (cargo test 2>&1 → rtk cargo test 2>&1)
Previously: any redirect/pipe suffix forced full shell passthrough
Now: strip safe suffix, route core through RTK filter, re-attach suffix
Shell applies the suffix to rtk's filtered output — both work correctly.
Safe suffixes: 2>&1, 2>/dev/null, > /dev/null, >> file,
| tee file, | head -N, | tail -N, | cat
3. npx vitest → rtk vitest (new route in route_npx)
Mirrors pnpm vitest handling with double-"run" deduplication.
New functions:
- analysis::split_safe_suffix: strips known safe suffixes from token list
- hook::is_env_assign: detects VAR=val patterns
- Env-prefix stripping logic at top of route_native_command
- Suffix-aware routing in check_for_hook_inner
TDD: 16 new tests (11 analysis, 5 hook suffix, 5 hook env-prefix).
All 710 tests passing.
Simple \$IDENT variable references (e.g. \$HOME, \$BRANCH) are now emitted
as Arg tokens instead of Shellism. The shell expands them when executing the
rewritten "rtk cmd \$VAR" command — RTK never expands variables itself.
Complex forms that still require the real shell remain Shellism:
\$() command substitution
\${} parameter expansion
\$? last exit code
\$\$ current PID
\$! last background PID
\$0–\$9 positional parameters
Effect: "git log \$BRANCH" now routes natively to "rtk git log \$BRANCH"
instead of falling through to "rtk run -c 'git log \$BRANCH'".
TDD: 3 failing tests (test_simple_var_is_arg, test_simple_var_enables_native_routing,
test_dollar_var_routes_natively) → all green after this commit.
Also removes the overly-broad test_variable_detection, replaced by 6 targeted tests.
Multi-command chains wrapped in "rtk run -c '...'" now have each known command replaced with its RTK equivalent, maximising token savings inside shell-wrapped commands. Before: rtk run -c 'cargo test && git log \$BRANCH' After: rtk run -c 'rtk cargo test && rtk git log \$BRANCH' Algorithm: reconstruct_with_rtk() iterates each parsed command in the chain. If the command is in the RTK routing table, it is substituted; otherwise it is kept verbatim (no nested rtk run -c). try_route_native_command() detects the "would be passthrough" case by checking if route_native_command() returns a string starting with "rtk run -c". Safety: only &&/||/; chains reach this path — pipe tokens trigger needs_shell() first, so no cross-command format issues can occur. TDD: 5 failing tests (test_chain_both_commands_substituted, test_chain_with_dollar_var_substituted, test_chain_unknown_command_not_substituted, test_semicolon_chain_substituted, test_or_chain_substituted) → all green. Total: 722 passing, 0 failing.
…n consts Adds two const slices that classify commands by their output format compatibility for safe pipe-left substitution: FORMAT_PRESERVING: commands whose RTK output matches raw output line-for-line (tail, echo, cat, find, fd) — safe as left side of any pipe without breaking the right-side consumer. TRANSPARENT_SINKS: right-side commands that accept any input format (tee, head, tail, cat) — already handled by split_safe_suffix at routing time. These consts document the safety policy and provide a lookup table for future pipe-left substitution logic. Format-changing commands (cargo, git, pytest, go) are explicitly excluded from FORMAT_PRESERVING since substituting them would break semantic sinks (grep, jq, awk, patch, xargs) on the right side. TDD: 3 failing tests (test_format_preserving_contains_expected, test_format_changing_not_in_format_preserving, test_transparent_sinks_contains_expected) → all green after this commit. Total: 725 passing, 0 failing.
|
I found and fixed a few additional bugs where too many commands would be excluded and the fix included adding streaming support which allows pipe commands to be used and support for some redirects. Hopefully performance at token reduction will be much improved now. |
- Add `pnpm eslint` → `rtk lint` routing in `route_pnpm` (TDD Red→Green: three test cases added to test_routing_native_commands before implementation) - Remove hardcoded personal file path from claude_hook.rs doc comment; replace with canonical Claude Code hooks documentation URL - Fix misleading "maintains session state across hook calls" comment in builtins.rs: each Claude Code hook call is a fresh process, state does not persist across invocations Fixes PR rtk-ai#156 reviewer items R3 (hardcoded path), R4 (cd comment), B4 (pnpm eslint gap). 725 tests pass; cargo clippy clean.
e5da75d to
9294bb1
Compare
PR 131 Part 1: Hook Engine + Chained Command Rewriting
Branch:
feat/rust-hooks-v2| Base:master| Tests: 541 passCloses: #112 | Split from: PR #131
New dep:
which = "7"PR: #156
Context
FlorianBruniaux requested splitting PR #131 (52 files, 8K+ additions) into separate PRs:
This PR combines items 3 + 4 because they are architecturally inseparable: the hook protocol handler calls
lexer::tokenize()thenanalysis::parse_chain()to process chained commands. Separating them would require duplicating the lexer.Coordination with PR #141: FlorianBruniaux noted overlap with #141's JS-based hook for Windows. This PR achieves Windows support via compiled Rust binary instead -- no bash, node, or bun required. CI/CD already builds Windows binaries.
exec.rsusescfg!(windows)for shell selection.Merge Sequence
Summary
Replaces the 204-line bash hook with a native Rust binary that provides quote-aware chained command rewriting. Closes #112 where
cd /path && git statusonly rewrotecd.Impact: Captures ~12-20M tokens/month in previously-missed optimizations across chained commands.
Why Rust over bash:
cd && git statusrewrites both)rtk hook checkshows exact rewrites)rtk hook claude-- Claude Code PreToolUse handlerReads JSON from stdin, applies rewriting, outputs JSON to stdout. Fail-open: malformed input exits 0 with no output so Claude proceeds unchanged.
Chained command rewriting (closes #112)
Before:
cd /tmp && git status-- hook only sawcd, missedgit statusAfter: lexer splits on
&&/||/;respecting quotes, each command wrapped independentlygit commit -m "Fix && Bug"is NOT split (quote-aware).rtk run -c <command>-- Command executorParses chains, detects shellisms (globs/pipes/subshells -> passthrough to sh/cmd), handles builtins (
cd/export/pwd), applies output filters, prevents recursion viaRTK_ACTIVEenv guard.rtk hook check-- DebuggerChanges
16 files changed (+2969, -221)
New (
src/cmd/):mod.rs,hook.rs,claude_hook.rs,lexer.rs,analysis.rs,builtins.rs,exec.rs,filters.rs,predicates.rs,test_helpers.rsModified:
src/main.rs(+Commands::Run, +Commands::Hook),src/init.rs(register binary hook),hooks/rtk-rewrite.sh(204-line script -> 4-line shim),Cargo.toml(+which),INSTALL.md(+Windows section)Intentionally excluded (stacked PRs):
feat/data-safety-rules-v2)feat/gemini-support-v2)Review Guide
Focus areas:
src/cmd/lexer.rs+analysis.rs-- Chain parsing correctness (quote handling)src/cmd/claude_hook.rs-- Protocol compliance, fail-open designsrc/cmd/exec.rs-- Builtin handling, Windows shell selection (cfg!(windows))src/cmd/hook.rs-- Shared decision logic (used by Parts 2 and 3)Implementation Notes
Binary size: Compiled with LTO + stripping. Size increase from
whichdependency minimal (<0.1 MB). Full size impact measurable after all 3 parts merge (PR #131 reported 5.1 MB total, +0.3 MB from combined deps).Backward compatible: All existing RTK features work unchanged. Legacy bash hook becomes 4-line shim forwarding to
rtk hook claude.Test Plan
cargo test-- 541 tests pass (hook:22, claude_hook:18, lexer:28, analysis:10, builtins:8, exec:22, filters:5, predicates:4)echo '{"tool_input":{"command":"git status"}}' | cargo run -- hook claude-- JSON rewrite worksecho '{"tool_input":{"command":"cd /tmp && git status"}}' | cargo run -- hook claude-- chain split workscargo run -- hook check "git status"-- text debugger workscargo run -- run -c "echo hello"-- executor worksgrep 'cfg!(windows)' src/cmd/exec.rs-- Windows shell selection presentRelated PRs (Split from PR #131)
Merge order: Part 1 first → retarget Parts 2 & 3 to
master→ merge in any order