* fix: handle tail rewrites with read tail-lines
* feat: add 32 TOML-filtered commands to hook rewrite rules (#475)
Add rewrite rules for all TOML-filtered commands so the Claude Code
hook automatically rewrites them to `rtk proxy <cmd>`. This ensures
TOML filters apply transparently without manual `rtk` prefixing.
Commands added: ansible-playbook, brew, composer, df, dotnet, du,
fail2ban-client, gcloud, hadolint, helm, iptables, make,
markdownlint, mix, mvn, ping, pio, poetry, pre-commit, ps, quarto,
rsync, shellcheck, shopify, sops, swift, systemctl, terraform, tofu,
trunk, uv, yamllint.
Tests updated to use `htop` as unsupported command example since
terraform is now supported via TOML filter.
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: git log --oneline no longer silently truncated to 10 entries (#461) (#478)
Only inject -10 limit when RTK applies its own compact format.
When user provides --oneline/--pretty/--format, respect git's
default behavior (no limit). Also detect -n and --max-count as
user-provided limit flags.
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: gh run view --job flag loses its value (#416) (#477)
Add --job and --attempt to flags_with_value in
extract_identifier_and_extra_args() so their values are not
mistaken for the run identifier when placed before it.
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: rtk read no longer corrupts JSON files with glob patterns (#464) (#479)
Add Language::Data variant for JSON, YAML, TOML, XML, Markdown, CSV
and other data formats. These files have no comment syntax, so the
MinimalFilter skips comment stripping entirely.
Previously, `packages/*` in package.json was treated as a block
comment start (`/*`), causing everything until the next `*/` to be
stripped — corrupting the JSON structure.
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: npm routing, discover cat redirect, proxy quoted args (#480)
* fix: npm routing, discover cat redirect, proxy quoted args (#470, #315, #388)
#470: rtk npm now correctly routes npm subcommands (install, list,
audit, etc.) without injecting "run". Previously, `rtk npm install`
was executed as `npm run install`.
#315: discover no longer counts `cat >`, `cat >>`, `cat |` as missed
savings. These are write/pipe operations with no terminal output to
compress.
#388: rtk proxy now auto-splits a single quoted argument containing
spaces. `rtk proxy 'head -50 file.php'` now works like
`rtk proxy head -50 file.php`.
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: proxy quote-aware split, redirect detection scan all tokens, npm test routing
- Proxy: replace split_whitespace with shell_split() that respects quotes (#388)
e.g. 'git log --format="%H %s"' no longer splits on space inside quotes
- Discover: scan all tokens for redirect operators, not just nth(1) (#315)
e.g. 'cat file.txt > output.txt' now correctly detected as write
- npm: replace tautological test with actual routing logic verification
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
---------
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* feat: add 11 new TOML built-in filters (xcodebuild, jq, basedpyright, ty, skopeo, stat, biome, oxlint, jj, ssh, gcc) (#490)
Closes#484, #483, #449, #448, #428, #283, #316, #195, #271, #333, #87, #376
Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: rtk rewrite accepts multiple args without quotes (#504)
* fix: rtk rewrite accepts multiple args without quotes
`rtk rewrite ls -al` now works the same as `rtk rewrite "ls -al"`.
Previously, args after the command were rejected or caused ENOENT.
Also adds rewrite tests to benchmark.sh to prevent regression.
Signed-off-by: Patrick Szymkowiak <patrick.szymkowiak@rtk-ai.app>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* test: add Clap rewrite tests + fix benchmark false failures
- Add 2 Clap try_parse_from tests for rewrite multi-args (catches the
KuSh bug at unit test level, not just benchmark)
- Fix git diff benchmark: use HEAD~1 on both sides for fair comparison
- Skip cargo/rustc benchmarks when tools not in PATH instead of false FAIL
- Benchmark: 0 fail, 4 skip (env-dependent), 52 green
Signed-off-by: Patrick Szymkowiak <patrick.szymkowiak@rtk-ai.app>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
---------
Signed-off-by: Patrick Szymkowiak <patrick.szymkowiak@rtk-ai.app>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: use rtk <cmd> instead of rtk proxy for TOML-filtered commands (#507)
- Replace all 32 `rtk proxy <cmd>` rules with `rtk <cmd>` so TOML filters
actually apply (proxy bypasses filters, giving 0% real savings)
- Extract NPM_SUBCOMMANDS to module-level const to prevent test/prod drift
Reported-by: FlorianBruniaux
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: warn when no hook installed + rtk gain hook status + PR #499 fixes
- hook_check: detect missing hook (not just outdated), warn with ⚠️
Only warns if ~/.claude/ exists (Claude Code user) — once per day
- gain: show hook status warning (missing/outdated) in rtk gain output
- ssh.toml: bump max_lines 50→200, truncate_lines_at 120→200 (Florian review)
- git.rs: mark integration test #[ignore] + assert binary exists (Florian review)
- Add HookStatus enum for reuse across gain/diagnostics
Fixes#508, Fixes#509
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: address code review — hook_check edge cases
- status() checks .claude/ existence (no false warning for non-CC users)
- Unreadable hook file returns Outdated not Missing
- Swap marker/warning order (emit warning before touching rate-limit marker)
- Rename misleading test, add end-to-end status() test
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: review iteration 2 — double-warning, case-sensitive test, ssh truncate
- Refactor check_and_warn to delegate to status() (single source of truth)
- Fix double-warning: skip maybe_warn() for `rtk gain` (has its own inline warning)
- Fix git test: case-insensitive assertion for cross-locale compatibility
- ssh.toml: keep truncate_lines_at=120 (terminal width convention)
- Robust mtime handling: unwrap_or(u64::MAX) instead of nested .ok()?
- Test handles all CI environments (no hook, no .claude, hook present)
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: detect and warn RTK_DISABLED=1 overuse (#508)
- discover: count RTK_DISABLED= bypassed commands, report top 5 examples
- gain: lightweight 7-day JSONL scan, warn if >10% commands bypassed
- registry: add has_rtk_disabled_prefix() and strip_disabled_prefix() helpers
- gitignore: add .fastembed_cache/ and .next/
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: preserve trailing newline in tail_lines + add missing test
- apply_line_window() now preserves trailing newline when input has one
- Add test for tail --lines N (space form) rewrite
- Add test for tail_lines without trailing newline
Signed-off-by: Patrick <patrick@rtk-ai.com>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* fix: respect user-specified git log limits instead of silently truncating
RTK was silently capping git log output in two ways:
1. `--oneline` without `-N` defaulted to 10 entries (now 50)
2. `filter_log_output()` re-truncated even when user explicitly set `-N`
3. Lines >80 chars were truncated, hiding PR numbers and author names
This matters for LLM workflows: Claude needs full commit history for
rebase, squash, and changelog operations. Silent truncation caused
incomplete context and repeated re-runs.
Changes:
- User-explicit `-N` → no line cap, wider 120-char truncation
- `--oneline`/`--pretty` without `-N` → default 50 (was 10)
- No flags → unchanged (default 10)
- Extract `truncate_line()` helper for clarity
Fixes#461
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: handle -n N and --max-count=N forms in git log limit parsing
- Extract parse_user_limit() to handle all 4 forms: -20, -n 20, --max-count=20, --max-count 20
- Add token savings test for filter_log_output (≥60%)
- Add 5 tests for parse_user_limit edge cases
Signed-off-by: Patrick <patrick@rtk-ai.com>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* feat: add structured dotnet support (build/test/restore/format)
Integrate PR #172 by @danielmarbach onto develop:
- MSBuild binlog parser (binary format, gzip, 7-bit varint)
- TRX test result parser (quick-xml)
- Format report JSON parser
- Subcommand routing: build, test, restore, format + passthrough
- Sensitive env var scrubbing (GH_TOKEN, AWS_SECRET_ACCESS_KEY, etc.)
- Fallback to text parsing when binlog unavailable
- 86-93% token savings on real .NET projects
Maintainer fixes applied:
- Removed binlog temp path from output (wastes tokens)
- Dropped hook file changes (incompatible with develop architecture)
- Fixed unused variable warnings
888 tests pass.
Signed-off-by: Patrick <patrick@rtk-ai.com>
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
* feat: add OpenCode plugin support (#300)
* feat(opencode): add OpenCode plugin support
Add `--opencode` flag to `rtk init` for installing a global OpenCode
plugin that rewrites Bash/shell commands through `rtk rewrite`.
- New plugin: hooks/opencode-rtk.ts (thin delegator to rtk rewrite)
- New init modes: --opencode (OpenCode only), combinable with Claude modes
- Plugin install/update/remove lifecycle with idempotent writes
- Uninstall cleans up OpenCode plugin alongside Claude Code artifacts
- `rtk init --show` reports OpenCode plugin status
- Replace unreachable!() with bail!() in match exhaustiveness guard
* docs: add OpenCode plugin documentation
- README: OpenCode plugin section, install flags, troubleshooting
- TROUBLESHOOTING: OpenCode-specific checklist
- Update init mode table to reflect Claude Code default
---------
Signed-off-by: Patrick szymkowiak <patrick.szymkowiak@innovtech.eu>
Signed-off-by: Patrick <patrick@rtk.ai>
Signed-off-by: Patrick Szymkowiak <patrick.szymkowiak@rtk-ai.app>
Signed-off-by: Patrick <patrick@rtk-ai.com>
Co-authored-by: Qingyu Li <2310301201@stu.pku.edu.cn>
Co-authored-by: Ousama Ben Younes <benyounes.ousama@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: José Almeida <57680069+zeval@users.noreply.github.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>
- Add SQLite tracking for all commands (90-day history)
- Add rtk gain command with ASCII graph visualization
- Add config.rs for TOML configuration support
- Integrate tracking in all command modules
- Add CI/CD workflow for multi-platform releases (deb, rpm, win, mac)
- Add LICENSE file and improve .gitignore