Conversation
Co-Authored-By: ahundt <ATHundt@gmail.com>
+ remove unused import
What changed:
- Add `run_claude()` with permissions check, audit logging, tool_input
preservation, and Ask/Allow/Deny support
- Add `run_cursor()` with flat JSON format (`permission`/`updated_input`)
- Add `audit_log()` (best-effort append when RTK_HOOK_AUDIT=1)
- Fix `run_gemini()` to load exclude_commands from config
- Convert all hook stdout to `writeln!` with `#[deny(clippy::print_stdout)]`
to prevent JSON protocol corruption (Claude Code bug #4669)
- Replace string-based heredoc detection with lexer-based `has_heredoc()`
(quote-aware: `<<` inside quotes no longer false-positives)
- Add shell prefix peeling (noglob, command, builtin, exec, nocorrect)
to `rewrite_segment()` in registry.rs
- Fix python3 -m pytest pattern, add pip show, add gt (Graphite) to RULES
- Remove `command ` from IGNORED_PREFIXES (was blocking `command git status`)
- Register `rtk hook claude`/`rtk hook cursor` binary commands in
settings.json instead of writing bash script files
- Add legacy script migration (deletes old rtk-rewrite.sh on `rtk init`)
- Simplify hook_check and integrity for script-free model
Integrates ~30 develop commits (PR #997): AWS expansion (8→25 cmds), SSH signing for git commit/push, go test context, grep stdin leak fix, default-to-ask permissions, gh pr merge passthrough. Conflict resolution (4 files): - git.rs: kept .output()+stdin(inherit) for commit/push (SSH/GPG signing) - go_cmd.rs: accepted incoming + added pub(crate) visibility - hook_check.rs: merged binary_hook_registered + other_integration_installed - hook_cmd.rs: fixed permissions path, println→writeln for Gemini deny Verified: 1445 tests pass, 0 clippy errors, all manual integration tests pass. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…r, signal guard, pipe rewrite
- pipe_cmd: fix panic on multi-byte UTF-8 at 1024 byte boundary (floor_char_boundary in auto_detect_filter)
- pipe_cmd: cap stdin at 10 MiB to prevent OOM (reuses RAW_CAP)
- stream: hoist RAW_CAP to pub const at module level
- hook_cmd: check deny before get_rewritten in handle_vscode
(matches handle_copilot_cli and run_claude order)
- hook_cmd: escape backslash and pipe in audit log sanitizer
- tsc_cmd: hoist duplicate TSC_ERROR regex to single module-level
lazy_static
+ trigger feat release tag
4 fixes applied (all confirmed introduced by PR #956, all tests pass): - P0 NEW-passthrough — pipe_cmd.rs: passthrough before cap read - P1 BUFFERED-panic — stream.rs: catch_unwind on Buffered filter - P1 STREAM-postcap — stream.rs: stop feeding filter after cap - P2 OFFBYONE-rawcap — stream.rs: 5 cap boundary checks fixed 5 findings dropped (not introduced by PR or not bugs): - DENY-claude: pre-existing on master - AUDIT-asymmetry: intentional scope choice, not a bug - GEMINI-test: pre-existing test pattern from master - SAVINGS-threshold: 40% is correct (filters achieve ~46%) - STDERR-test: cosmetic CI, not correctness
- handle npm exec|run and their aliases - handle pnpm exec|run and their aliases like npm - handle pnpx and its alias like npx - handle all forms of js script/package execution Signed-off-by: Nicolas Le Cam <niko.lecam@gmail.com>
Signed-off-by: Nicolas Le Cam <niko.lecam@gmail.com>
Signed-off-by: Nicolas Le Cam <niko.lecam@gmail.com>
…npm test` commands as we don't know which test framework is used under the hood Signed-off-by: Nicolas Le Cam <niko.lecam@gmail.com>
…jest Also remove duration computation as there's no endTime attribute in json output Signed-off-by: Nicolas Le Cam <niko.lecam@gmail.com>
This reverts commit 94a3532. Build is no longer a pnpm command with specific handling. Signed-off-by: Nicolas Le Cam <niko.lecam@gmail.com>
feat(discover): handle more npm/npx/pnpm/pnpx patterns
📊 Automated PR Analysis
SummaryThis PR reworks the vitest command interface to use Review Checklist
Analyzed automatically by wshm · This is an automated analysis, not a human review. |
fix(docs): use release please changelog no manual
feat(refacto-core): binary hook w/ native cmd exec + streaming
RTK v0.37.0-rc.153 — Pre-Release Auto Test Report (ai)Date: 2026-04-15 Summary
Layer 0 — Build Quality Gate
Layer 1 — Artifact Install Cycle
Layer 2 — Pipe Command (New Feature)
Layer 3 — Binary Hook Engine (New Feature)
Layer 4 — Rewrite Engine
Layer 5 — Core Filters (E2E with Real Commands)
Layer 5.5 — Specific Bug Fixes
Layer 6 — Discover Registry
Layer 7 — Meta Commands
Layer 8 — Streaming Infrastructure
Layer 9 — Complex Commands & Chained Commands9A — Single Command Compression Baseline
9B — Chained Command Compression (each segment filtered)
9C — Redirect Preservation (filter applied before redirect to file)
9D — Pipe Stdin Filtering (
|
| # | Test | Raw | Filtered | Savings | Result |
|---|---|---|---|---|---|
| 9.11 | git log -20 | rtk pipe --filter git-log |
4,602c | 226c | 95% | PASS |
| 9.12 | git diff HEAD~3 | rtk pipe --filter git-diff |
455,337c | 5,845c | 99% | PASS |
| 9.13 | cargo test | rtk pipe --filter cargo-test |
115,861c | 51c | 99.96% | PASS |
| 9.14 | git status | rtk pipe (auto-detect, no --filter) |
306c | 306c | 0% | NOTE — auto-detect guessed wrong filter |
9E — Hook Engine with Complex Commands (rewrite correctness)
| # | Test | Rewrite Output | Result |
|---|---|---|---|
| 9.15 | Chain: git status && git log -20 |
rtk git status && rtk git log -20 |
PASS |
| 9.16 | Redirect: cargo check 2>&1 |
rtk cargo check 2>&1 |
PASS |
| 9.17 | Env prefix: RUST_LOG=debug cargo test |
RUST_LOG=debug rtk cargo test |
PASS |
| 9.18 | Pipe: git log -5 | head -20 |
rtk git log -5 | head -20 |
PASS |
| 9.19 | Semicolons: git status; git branch; git log -3 |
rtk git status; rtk git branch; rtk git log -3 |
PASS |
| 9.20 | Real-world: git stash && git pull && git stash pop |
rtk git stash && rtk git pull && rtk git stash pop |
PASS |
| 9.21 | Mixed HIT/MISS: git rebase main && git push |
git rebase main && rtk git push |
PASS — MISS preserved unchanged |
| 9.22 | Mixed: npm ci && npm run build |
npm ci && rtk npm run build |
PASS — only known commands rewritten |
9F — Rewrite Correctness for Complex Patterns
| # | Test | Result | Notes |
|---|---|---|---|
| 9.23 | Env vars: RUST_LOG=debug cargo test → RUST_LOG=debug rtk cargo test |
PASS | |
| 9.24 | Env vars: CI=true FORCE_COLOR=0 cargo test → CI=true FORCE_COLOR=0 rtk cargo test |
PASS | Multi env vars |
| 9.25 | Quotes: git log --format="%H %s" → rtk git log --format="%H %s" |
PASS | Quotes preserved |
| 9.26 | Quotes: git commit -m 'fix: handle edge case' → rtk git commit -m 'fix: handle edge case' |
PASS | |
| 9.27 | Full chain: cargo fmt && cargo clippy && cargo test && cargo build → all rewritten |
PASS | |
| 9.28 | Mixed chain: git pull && cargo build && cargo test → all rewritten |
PASS |
9G — Rewrite Registry Coverage Audit
| # | Command | Rewritten? | Result |
|---|---|---|---|
| 9.29 | pnpm install |
YES → rtk pnpm install |
PASS |
| 9.30 | pnpm build |
NO | NOTE — not in registry (bare pnpm build vs pnpm run build) |
| 9.31 | pnpm test |
NO | NOTE — same as above |
| 9.32 | pnpm run build |
YES → rtk pnpm run build |
PASS |
| 9.33 | pnpm list / pnpm outdated |
YES | PASS |
| 9.34 | npm run build / npm run test |
YES | PASS |
| 9.35 | npm install / npm test / npm ci |
NO | NOTE — bare npm subcommands not in registry |
| 9.36 | npx vitest → rtk vitest |
YES | PASS — aliased |
| 9.37 | docker ps / docker images |
YES | PASS |
| 9.38 | kubectl get pods |
YES | PASS |
| 9.39 | pytest / ruff check . / go test ./... / go build |
YES | PASS |
| 9.40 | All 12 core git subcommands (status/log/diff/show/add/commit/push/pull/fetch/branch/stash/worktree) | YES | PASS |
| 9.41 | Git non-filter subcommands (rebase/merge/cherry-pick/tag/remote/clean) | NO | PASS — correctly not rewritten (no filter) |
9H — Passthrough for Unfiltered Commands
| # | Test | Result | Notes |
|---|---|---|---|
| 9.42 | rtk git tag --list (156 tags) |
PASS | Passthrough works, exit 0 |
| 9.43 | rtk git remote -v |
PASS | Raw output passed through |
| 9.44 | rtk git worktree list |
PASS | Filtered output |
Layer 10 — Streaming & Long-Running Commands
10A — Compression on Real Long-Running Commands
| # | Test | Raw | Filtered | Savings | Time | Result |
|---|---|---|---|---|---|---|
| 10.1 | rtk cargo test (1,584 tests) |
115,340c | 201c | 99.8% | 5.9s | PASS |
| 10.2 | rtk cargo build (incremental, clean) |
1,697c | 1,645c | 3% | — | PASS — minimal output |
| 10.3 | rtk cargo clippy --all-targets |
5,243c | 1,009c | 81% | — | PASS |
| 10.4 | rtk git log -100 |
45,951c | 19,738c | 57% | — | PASS |
| 10.5 | rtk git diff HEAD~5 |
466,347c | 22,591c | 95% | — | PASS |
| 10.6 | rtk cargo test hooks:: (subset) |
13,093c | 208c | 98% | — | PASS |
10B — Large Output Stress Tests
| # | Test | Raw | Filtered | Savings | Result |
|---|---|---|---|---|---|
| 10.7 | rtk git log --all (1,346 commits) |
876,362c | 1,890c (10 shown) | 99.8% | PASS — truncated to recent 10 |
| 10.8 | Full test suite through RTK | 115K+ | 201c | 99.8% | PASS |
10C — Output Completeness (no data loss in filtered output)
| # | Test | Result | Notes |
|---|---|---|---|
| 10.9 | rtk cargo test shows pass summary |
PASS | "1584 passed, 6 ignored" |
| 10.10 | rtk cargo clippy shows all 14 warnings grouped |
PASS | Grouped by type with counts |
| 10.11 | rtk git log -100 shows all 100 commits |
PASS | All 100 hashes present |
10D — Exit Code Propagation
| # | Test | Expected | Actual | Result |
|---|---|---|---|---|
| 10.12 | rtk cargo test nonexistent |
0 (no match, 0 run) | 0 | PASS |
| 10.13 | rtk git log nonexistent_branch |
128 | 128 | PASS |
| 10.14 | rtk cargo check --bin nonexistent |
101 | 101 | PASS |
10E — Process Cleanup (No Zombies)
| # | Test | Result | Notes |
|---|---|---|---|
| 10.15 | rtk process count stable before/after | PASS | 2 before, 2 after (no leaked processes) |
Known Issues
-
Git log negative savings for small sets: When filtering <20 commits, the enriched format (relative dates, author names, body excerpts with
[+N lines omitted]) can produce more output than raw git log. For larger sets, savings are 65%+ (gain data shows 91.1% overall). This is by design — the enriched format provides more useful information to the LLM. -
14 Clippy warnings: All cosmetic (dead code in
stream.rs,lexer.rs,constants.rs,integrity.rs). No errors.
Verdict
LGTM
Deep Review — PR #1277 (4-pass agent + Multipass VM)Method: 4 isolated worktree code-review agents (security, Rust quality, architecture, streaming engine) + full build & integration tests in Multipass VM (Ubuntu 24.04, aarch64). VM Test Results
P0 — Blockers (5)
P1 — Must-Fix (11)
Other Notes
VerdictConditional merge — fix the 5 P0s + clippy before merging to master. The architecture is sound, the streaming engine and permission system are well-designed, and 1584 tests pass. The issues are in the integration layer between the stream engine and callers, not in the core engine itself. |
Correction: P0.1 downgraded to P2After deeper VM testing, P0.1 is not a P0. In Concrete proof: P0.2 remains a real P0 — confirmed: The Updated severity table
Real blockers (2)
Additional edge case found
All 11 P1s from the original review remain valid. |
Final Verified Review — 9 Passes (4 initial + 5 verification)After 9 review passes (4 code-review agents + 5 verification agents, all in isolated worktrees) + Multipass VM integration tests, here are the verified findings. All initial findings were re-checked against the actual code; false positives have been removed. P0 — Blockers (3 verified)1. Master explicitly printed stderr before returning: // master (correct)
if opts.skip_filter_on_failure && exit_code != 0 {
if !stderr.trim().is_empty() {
eprintln!("{}", stderr.trim());
}
timer.track(...);
return Ok(exit_code);
}Develop lost this behavior during the streaming refactor: // develop (broken — no print)
if opts.skip_filter_on_failure && exit_code != 0 {
timer.track(&cmd_label, &format!("rtk {}", cmd_label), raw, raw);
return Ok(exit_code); // blank screen
}14 call sites affected: ls, tree, psql, kubectl (x3), docker logs, docker (x2), gh (x6). All show blank output on error. Fix: 2. On Clap parse errors (e.g. Fix: add 3. The bash-to-binary migration removed the Fix: either wire P1 — Must-Fix (9 verified, 4 false positives removed)
Removed false positives:
Clippy (16 warnings, must fix for CI)
Security Assessment (Pass 9)
VM Integration Results (1584 tests, all green)
Verdict3 P0s + 9 P1s + 16 clippy warnings to fix before merge. The core architecture (streaming engine, permission system, hook protocol) is well-designed and tested. The issues are in the integration layer and migration path. Priority order:
|
- runner: print captured output on non-zero exit (P0.1) - main: add hook/pipe to META_COMMANDS (P0.2) - init: store integrity hash after Gemini script install (P0.3) - hook_cmd: audit log + permission check for all agent paths (P1.1, P1.2) - runner: include failure_lines in cargo test summary (P1.5) - Cargo.toml: remove unconditional libc dep (P1.7) - init: clean stale settings.json entries during migration (P1.8)
FlorianBruniaux
left a comment
There was a problem hiding this comment.
Reviewed the full diff + local build/test run. Build: 0 errors, 9 warnings. Tests: 1584 passed. No blockers.
Two things worth fixing before shipping:
1. Format string typo — pipe_cmd.rs:70
format!("{} matches in {}F:\n\n", total, by_file.len())The F: is a leftover artifact. Should be {} files:.
2. Signal handler cast warnings — main.rs:2188-2189
The handle_signal as libc::sighandler_t cast is correct (standard pattern for libc::signal), but Rust emits "direct cast of function item into integer" twice. Fix via intermediate pointer type:
libc::signal(libc::SIGINT, handle_signal as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t);
libc::signal(libc::SIGTERM, handle_signal as unsafe extern "C" fn(libc::c_int) as libc::sighandler_t);Four dead-code warnings are trivially fixable — FilterMode::Buffered, store_hash, strip_quotes, and the three constants (OPENCODE_PLUGIN_PATH, CURSOR_DIR, GEMINI_DIR). All three constants are imported under #[cfg(test)] in hook_check.rs so they're never referenced in production. Either #[allow(dead_code)] or move them to a test-only block.
Two low-severity notes for awareness:
io::stderr().lock()is held for the entire child process lifetime in thelive_stderrpath (stream.rs:302-303). Safe now given the single-threaded design, but worth a comment as a footgun for future contributors addingeprintln!()in filter code.auto_detect_filtermisses mypy runs that produce onlynote:lines (noerror:). Those fall through to the grep heuristic. Edge case, but real.
The permission system logic (permissions.rs) is solid. The compound-command fix for #1213 (all segments must independently match for Allow) is correct and well-tested.
fix: P0+P1 fixes from pre-merge review of hook engine
Fix conflict next release
|
Merging, all maintainers aggred |
Summary
Feat
Fix
fix(vitest): rework command to handle differences between vitest and
ISSUE #154 : migrate bash hook to Rust subcommand
ISSUE #222 : proxy streaming for long-running commands
ISSUE #530 : strip trailing stderr redirects before pattern matching
ISSUE #532 : env var prefix and cd chaining in rewrite
ISSUE #897 : subprocess memory leak / zombie process prevention (ChildGuard)
Closes #968 : EAGAIN posix_spawn resource exhaustion (binary hooks)
ISSUE #886 : RTK bypasses Claude Code permissions (permission verdict system)
ISSUE #712 : rtk hook subcommands added
ISSUE #918 : env-var-prefixed exclude_commands (partial: -h flag conflict not addressed)
ISSUE #893 : updatedInput in bypassPermissions mode (partial: default/ask fixed, allow edge case remains)
ISSUE #928 : python3 -m pytest/mypy rewrite (partial: heredoc/script.py not covered)
ISSUE #361 : 5 of 6 hook bugs addressed (streaming, compound commands, conservative routing)
ISSUE #295 : uv sync/pip patterns (~5% of requested scope)