- Run cargo fmt on diff_cmd.rs and init.rs (line length violations)
- Add Unreleased section to CHANGELOG.md with the two bug fixes
from this PR (required by doc review CI gate)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Signed-off-by: Florian BRUNIAUX <florian@bruniaux.com>
rake test ignores positional file arguments and only supports TEST=path
for single-file runs. When users pass positional test files (e.g.,
`rtk rake test file1.rb file2.rb` or `rtk rake test file.rb:15`),
select_runner() now switches to `rails test` which handles single files,
multiple files, and line-number syntax natively.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Nicholas Lee <nicholas.lee@appfolio.com>
* docs: bump version refs to 0.27.1, fix module count to 59
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(filters): split monolithic builtin_filters.toml into 28 individual files
Replace the 848-line src/builtin_filters.toml monolith with individual
files under src/filters/<name>.toml (1 filter + tests per file).
Files are concatenated alphabetically by build.rs at compile time into
OUT_DIR/builtin_filters.toml, then embedded via a BUILTIN_TOML constant
in toml_filter.rs. The build step validates TOML syntax and detects
duplicate filter names across files.
Benefits:
- Zero merge conflicts when multiple PRs add filters (different files)
- Clear PR diffs: "+1 file of 30 lines" vs "+30 lines in 850-line file"
- Easy onboarding: copy any .toml, rename, edit 3 fields — done
- Build-time TOML validation catches syntax errors before tests run
Changes:
- build.rs (new): concat + validate src/filters/*.toml
- Cargo.toml: add [build-dependencies] toml = "0.8"
- src/filters/*.toml (28 files): split from monolith
- src/filters/README.md (new): contributor guide
- src/toml_filter.rs: BUILTIN_TOML const + 5 include_str replacements
- src/builtin_filters.toml: deleted
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(filters): add 15 TOML filters (ping, rsync, dotnet, swift, shellcheck, hadolint, poetry, composer, brew, df, ps, systemctl, yamllint, markdownlint, uv)
Tier 1 (high value):
- ping: strip per-packet lines, tail_lines=4 keeps summary only (70%+)
- rsync: match_output on "total size is" → "ok (synced)" (80%+)
- dotnet-build: match_output on "Build succeeded" → "ok (build succeeded)" (70%+)
- swift-build: match_output on "Build complete!" → "ok (build complete)" (70%+)
- shellcheck: strip blank lines + caret indicator lines, max_lines=50 (60%+)
- hadolint: strip blank lines, truncate_lines_at=120, max_lines=40 (60%+)
- poetry-install: strip downloads, match_output on "No dependencies to install" (75%+)
- composer-install: strip downloads, match_output on "Nothing to install" (75%+)
- brew-install: strip Downloading/Pouring, match_output on "already installed" (70%+)
Tier 2 (medium value):
- df: truncate_lines_at=80, max_lines=20 (60%+)
- ps-aux: truncate_lines_at=120, max_lines=30 (60%+)
- systemctl-status: strip blank lines, max_lines=20 (65%+)
- yamllint: strip blank lines, max_lines=50, truncate_lines_at=120 (60%+)
- markdownlint: strip blank lines, max_lines=50, truncate_lines_at=120 (60%+)
- uv-sync: strip downloads, match_output on "Audited"/"Resolved" (70%+)
Updates test guards: count 29→44, expected list +15 names, concat test 30→45.
All 90 inline TOML tests pass. 720 unit tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix(filters): address PR #386 code review (P1 + P2 items)
P1-2: rsync.toml — remove dead strip rule "^total size is \d"
match_output fires at stage 3, strip at stage 4 — the rule was
unreachable on success and removed a useful line on error paths.
P2-1: ps-aux.toml renamed to ps.toml, match_command ^ps\b → ^ps(\s|$)
Prevents false matches on pstree, psql, pstack. Filter name and
test sections updated from ps-aux to ps. Guard list updated.
P2-2: df.toml match_command ^df\b → ^df(\s|$)
Prevents false matches on dfs, dfx.
P2-4: shellcheck.toml — keep caret indicator lines (^-- SC...)
Stripping them lost the exact character position of the error.
Only blank lines are now stripped.
P2-7: swift-build.toml — remove dead strip rule "^Build complete!"
match_output short-circuits before strip runs on success paths.
The rule was unreachable and confusing.
All 90 inline tests pass. 720 unit tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address PR #386 code review — 4 filter fixes
- uv-sync: remove Resolved short-circuit (fires before package list)
- dotnet-build: short-circuit only on 0 warnings + 0 errors (not on
any "Build succeeded" — catches builds with warnings too)
- poetry-install: support Poetry 2.x bullet syntax (• vs -) and
"No changes." message; add Poetry 2.x test
- ping: add Windows format support (Pinging + Reply from patterns);
add Windows test
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(changelog): document round 2 fixes (PR #351 + #386)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: rebase on updated PR #351 — remove 7 dead filters re-added by rebase
Rebase on feat/toml-dsl-pr2 (post-PR-#349 fixes) caused the refactor
commit to re-add dead filters that PR #351 had already removed:
cargo-run, docker-compose-ps, docker-inspect, git-checkout,
git-merge, git-remote, pnpm-build.
These 7 files are removed to maintain the expected 36 filter count.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(dsl): add 'unless' to match_output — prevent short-circuit when errors present
Fixes two match_output bugs where success short-circuit fires before
strip_lines_matching can surface errors in the full output blob.
Changes:
- Add optional `unless` field to MatchOutputRule (serde default = None)
- Compile `unless` into Option<Regex> in CompiledMatchOutputRule
- In apply_filter stage 3: if `unless` matches the blob, skip the rule
and continue to the next match_output rule (or fall through to the pipeline)
- rsync.toml: add `unless = "error|failed|No such file"` to the
"total size is" rule; add test verifying errors are not swallowed
- swift-build.toml: add `unless = "warning:|error:"` to the
"Build complete!" rule; add test verifying warnings are not swallowed
- CONTRIBUTING.md: document match_output[].unless in the field reference table
- Unit tests: 5 new tests covering unless blocks/allows/falls-through/
no-regression/invalid-regex
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: fix README version string 0.27.1 → 0.27.2 after rebase
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: fix version 0.27.1→0.27.2 and module count 59→60 in ARCHITECTURE.md + CLAUDE.md
Version strings and module count were stale after rebase onto
updated feat/toml-dsl-pr2. validate-docs.sh now passes clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: TOML filter DSL + 14 built-in filters (#299)
Add a declarative TOML-based filter engine for zero-Rust command
filtering, plus 14 built-in filters targeting open issues.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update module count to 58 (toml_filter + verify_cmd)
* docs(changelog): add TOML DSL + 14 built-in filters entry
* chore: ignore worktrees directory
* fix: address PR #349 code review (P1 + P2)
P1-1: println! — add trailing newline to TOML-filtered output
print!("{}", filtered) → println!("{}", filtered). Fixes broken
pipe compatibility (rtk make all | wc -l returned 0 for 1 line).
P1-2: remove dead filters — git-checkout, git-remote, git-merge, cargo-run
Clap routes these commands to git.rs/cargo_cmd.rs before run_fallback
is reached, so these TOML filters never activate. Tests passed but
gave false confidence. Removed filter definitions and test sections.
P1-3: map_or instead of is_none_or (MSRV compatibility)
is_none_or requires Rust 1.82+. Replaced with map_or(true, ...) which
compiles on all supported toolchain versions.
P1-4: print tee hint for TOML-filtered commands on failure
let _ = tee::tee_and_hint(...) silently discarded the hint path.
Now follows the same pattern as runner.rs: if let Some(hint) = ... {
println!("{}", hint); }. LLMs can now find the full output file.
P1-5: --require-all now runs integrity check
Previously, `rtk verify --require-all` branched away from
integrity::run_verify(). Now: filter-specific mode (--filter foo)
skips integrity, but default and --require-all always run it first.
P2-1: RTK_NO_TOML checks value not presence
.is_ok() matched any value including RTK_NO_TOML=0.
Replaced with .ok().as_deref() == Some("1").
P2-5: terraform-plan regex — add word boundary after "plan"
^terraform\s+plan → ^terraform\s+plan(\s|$) to prevent matching
"terraform planning" or other plan-prefixed subcommands.
P2-6: iptables regex — \b → (\s|$)
^iptables\b matched iptables-save and iptables-restore (\b fires at
hyphen). Fixed to ^iptables(\s|$).
All 715 tests pass.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add word-boundary anchors to 6 tofu/mix match_command regexes
Prevents overmatch on subcommands (e.g. `tofu planet` or `mix formats`).
Mirrors the same `(\s|$)` pattern already used by terraform-plan.
Fixes: tofu-plan, tofu-init, tofu-validate, tofu-fmt, mix-format, mix-compile
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(changelog): add round 2 fix for tofu/mix regex overmatch (PR #349)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address PR #349 round 3 review (BUG-1 + BUG-2 + P2 items)
- println! trailing newline on TOML-filtered output (BUG-1)
- RTK_NO_TOML check presence→value (as_deref==Ok("1")) (BUG-2)
- make filter: add on_empty="make: ok" + test (P2-3)
- TOML lookup: use basename of args[0] for absolute path support (P2-8)
- utils.rs: translate French docstrings to English (P2-9)
- main.rs: single error message on command-not-found, exit(127) (NEW)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: bump version refs to 0.27.2 and module count to 60
Fixes pre-push validation: README, CLAUDE.md, ARCHITECTURE.md were
still referencing 0.27.1 (Cargo.toml is 0.27.2). Also updates module
count from 59 to 60 to match main.rs (toml_filter + verify_cmd).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: curl size guard (#297) + exclude_commands config (#243)
**#297 — curl JSON inflation guard**
- `rtk curl` no longer replaces small JSON responses with a type schema
that is larger than the original payload
- Size guard in `filter_curl_output()`: only return schema when
`schema.len() <= original.len()`
- Inspired by PR #324 (@rursache)
**#243 — exclude_commands config**
- Add `[hooks] exclude_commands = ["curl"]` in config.toml to prevent
the auto-rewrite hook from rewriting specific commands
- Survives `rtk init -g` re-runs (config.toml is user-owned)
- Implemented in `rewrite_cmd::run()` via `HooksConfig` struct
- Threaded through `rewrite_command()` → `rewrite_compound()` → `rewrite_segment()`
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: add #297 and #243 to CHANGELOG + exclude_commands in README
* docs: fix version references 0.24.0 → 0.25.0 across all docs
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: add `rtk rewrite` command — single source of truth for hook rewrites
Implements `rtk rewrite <cmd>` as the canonical rewrite engine for all
LLM hook integrations (Claude Code, Gemini CLI, future tools).
- Add `rewrite_prefixes` field to `RtkRule` in discover/registry.rs
- Add public `rewrite_command()` with compound command support (&&, ||, ;, |)
- Add `rewrite_segment()`, `rewrite_compound()`, `strip_word_prefix()` helpers
- Handle already-rtk commands (exit 0, identical output)
- Handle unsupported/ignored commands (exit 1, no output)
- Add 20 unit tests covering all branches and edge cases
- Create `src/rewrite_cmd.rs` thin CLI wrapper
- Register `Commands::Rewrite` in main.rs
- Simplify `.claude/hooks/rtk-rewrite.sh` from 357 → 60 lines
Hooks no longer need duplicate mapping logic — a single
`REWRITTEN=$(rtk rewrite "$CMD") || exit 0` handles everything.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: fix version references and module count for 0.22.2
- README.md, CLAUDE.md, ARCHITECTURE.md: 0.20.1 → 0.22.2
- ARCHITECTURE.md: module count 48 → 51 (added rewrite_cmd + 2 from master)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: update hook coverage check to verify registry (not hook script)
Since PR #241, the hook delegates to `rtk rewrite` — command mappings
live in src/discover/registry.rs, not the bash hook script.
Update the "Verify hook coverage" CI step to:
- Check that the hook calls `rtk rewrite` (new architecture)
- Check that registry.rs has rewrite_prefixes for all Python/Go commands
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: support single `&` background operator in compound rewrites
Per feedback from @xDelph: AI agents increasingly use `cmd1 & cmd2`
for parallel execution. This commit adds support alongside existing
`&&`, `||`, `;`, and `|` operators.
Changes:
- rewrite_compound: add match arm for single `&` (after `&&` check)
- rewrite_command: add `" & "` to has_compound detection
- init: show "installed/updated" vs "already up to date" so users
know whether rtk init changed the hook on re-run
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: add Python/Go commands to rewrite registry
Add `ruff`, `pytest`, `pip`, `go`, and `golangci-lint` to both
PATTERNS and RULES in registry.rs so the CI coverage check passes
and `rtk rewrite` correctly identifies these commands.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update version reference to 0.23.0 in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update version reference to 0.23.0 in CLAUDE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update version and module count to 0.23.0/52 in ARCHITECTURE.md
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address P0 review feedback on rtk-rewrite PR
P0.1 - Replace 218-line bash hook with 58-line thin delegating hook.
All rewrite logic now lives exclusively in `rtk rewrite` (Rust registry).
`rtk init` installs the thin hook via include_str!.
P0.2 - Fix `head -20 file` crash at runtime.
Generic prefix replacement produced `rtk read -20 file` (invalid clap args).
Now translates `head -N file` → `rtk read file --max-lines N` and skips
unsupported head flags (e.g. -c) by returning exit 1.
P0.3 - Add version guard in hook for rtk < 0.23.0.
Prints a warning to stderr instead of silently doing nothing.
Also adds missing registry entries vs old hook:
- gh release, cargo install
- docker run/exec/build, kubectl describe/apply
- tree, diff
474 tests pass, 0 clippy warnings.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* style: cargo fmt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* refactor(discover): extract rules/patterns into rules.rs
Per aeppling review: registry.rs was too large (~1500 lines).
Extract all static data into src/discover/rules.rs:
- RtkRule struct (with pub fields)
- PATTERNS const array
- RULES const array
- IGNORED_PREFIXES and IGNORED_EXACT const arrays
registry.rs now contains only logic + tests.
rules.rs is the single place to add a new command mapping.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update version reference to 0.24.0 in README
* docs: update version references to 0.24.0 in CLAUDE.md and ARCHITECTURE.md
* fix(discover): add aws and psql to rewrite registry
rtk aws and rtk psql modules exist since PR #216 but were missing
from the registry rules — rewrite was silently skipping them.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* test(discover): exhaustive regression tests for rewrite registry
Cover all command categories added in PR #241 and missing from
the previous test suite:
- aws / psql (PR #216 modules, gap detected during non-reg run)
- Python: ruff, pytest, python -m pytest, pip, uv pip
- Go: go test/build/vet, golangci-lint
- JS/TS: vitest, pnpm vitest, prisma, prettier, pnpm list
- Compound operators: || and ; rewrites, 4-segment chains,
mixed supported/unsupported segments, all-unsupported → None
- sudo prefix rewrite, env var prefix rewrite
- find with native flags
- Registry invariants: PATTERNS/RULES aligned, all rules valid,
all patterns are valid regex
Before: 559 tests. After: 607 tests.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(migration): document breaking change + detect outdated hook in rtk config
CHANGELOG [Unreleased]:
- Migration required: rtk init --global needed after upgrade
- Documents upgrade path and explains no immediate breakage
rtk init --show / rtk config now detects old hook (inline if-else)
vs new thin delegator (rtk rewrite) and prints actionable warning:
⚠️ Hook: ~/.claude/hooks/rtk-rewrite.sh (outdated — inline logic, not thin delegator)
→ Run `rtk init --global` to upgrade to the single source of truth hook
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs: update hook references for thin delegator (0.24.0)
- ARCHITECTURE.md: fix hook description (shell script → thin delegator)
- INSTALL.md: add upgrade path for old hook users (pre-0.24 breaking change)
- SECURITY.md: add registry.rs + hook files to Tier 1 critical files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* docs(arch): fix module count 52→55, add aws/psql/rewrite to module map
Adds aws_cmd, psql_cmd (PR #216) and rewrite_cmd (this PR) to the
Complete Module Map table. Updates total from 52 to 55 to match main.rs.
Fixes CI validate-docs.sh module count mismatch.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* ci: fix hook coverage check to search rules.rs instead of registry.rs
After the PR #241 refactor, rewrite_prefixes constants moved from
registry.rs to rules.rs. Update grep to search the whole discover/
directory so it finds them in the right file.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>