Skip to content

fix(grep): respect the invoked tool — grep runs grep, rg runs ripgrep#2183

Open
olsavmic wants to merge 1 commit into
rtk-ai:developfrom
olsavmic:fix/grep-route-rg-not-system-grep
Open

fix(grep): respect the invoked tool — grep runs grep, rg runs ripgrep#2183
olsavmic wants to merge 1 commit into
rtk-ai:developfrom
olsavmic:fix/grep-route-rg-not-system-grep

Conversation

@olsavmic
Copy link
Copy Markdown

@olsavmic olsavmic commented May 31, 2026

Summary

rg and grep were both rewritten to rtk grep, which always ran ripgrep as its backend. That collapsed the two tools into one and broke whichever didn't match the backend:

This PR preserves the invoked tool's identity from the rewrite onward, so each command runs its own backend with its own semantics — no crossover in either direction.

Design

Invoked Rewritten to Runs Semantics
grep … rtk grep system grep grep (BRE, grep flags, -r)
rg … rtk rg ripgrep rg (--glob/-g/-P/--files, any order)
  • rewrite: split the single ^(rg\|grep) rule into grep → rtk grep and rg → rtk rg.
  • rtk rg (new subcommand): faithful ripgrep passthrough — all args forwarded verbatim (flat trailing_var_arg, so leading flags never break clap) — with RTK's by-file filtering; info/list flags (--files, --json, …) stream raw.
  • rtk grep: now runs system grep, parsing grep's file:line:content output; -t/--file-type maps to grep --include. Keeps its documented -l/-m/-t/--context-only interface.
  • shared emit_filtered takes a per-backend line parser (rg NUL form file\0line:content vs grep colon form).

Why not "route everything through one backend"

The first iterations of this PR tried that (route rg-style fallbacks to rg). Review found it corrupts the other tool because grep and rg share short flags with conflicting meanings — most dangerously grep -rn foo → rg --replace=n → every match rewritten. There is no safe way to run one tool's syntax through the other; the only correct fix is to keep them separate.

Test plan

  • cargo fmt --check && cargo clippy --all-targets && cargo test1985 passed, 7 ignored, clippy clean, fmt clean
  • Unit tests: rewrite split (rgrtk rg, greprtk grep), parse_grep_line, grep_type_glob, is_raw_passthrough_flag
  • Manual verification (built binary):
# rewrite preserves origin
rg --glob '*.py' alpha .   ->  rtk rg --glob '*.py' alpha .
grep -rn alpha .           ->  rtk grep -rn alpha .

# rtk rg: ripgrep, faithful + filtered
$ rtk rg --glob '*.py' alpha gp     # was: grep: unrecognized option `--glob'
2 matches in 2 files: ...
$ rtk rg -P 'alpha\s' gp/a.py        # was: grep: invalid option -- P
1 matches in 1 files: ...
$ rtk rg --files gp                  # issue #2060

# rtk grep: system grep, correct (no rg --replace corruption)
$ rtk grep -rn alpha gp              # earlier naive routing gave "n one"
gp/a.py:1:alpha one ...
$ rtk grep -E 'alpha|beta' gp/a.py   # grep ERE, respected

Notes

  • rtk grep now runs system grep rather than ripgrep — a deliberate behavior change so grep semantics are respected (loses rg's speed/gitignore-awareness for grep commands; that's grep being grep). rtk rg is the path for ripgrep.
  • Verified on macOS (BSD grep) + ripgrep 15.1.0; logic is platform-agnostic but not yet exercised on GNU grep.

@CLAassistant
Copy link
Copy Markdown

CLA assistant check
Thank you for your submission! We really appreciate it. Like many open source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution.
You have signed the CLA already but the status is still pending? Let us recheck it.

@olsavmic olsavmic changed the title fix(grep): route ripgrep-style invocations to rg instead of system grep Draft: fix(grep): route ripgrep-style invocations to rg instead of system grep May 31, 2026
@olsavmic olsavmic force-pushed the fix/grep-route-rg-not-system-grep branch from a437345 to bc216fc Compare May 31, 2026 12:03
@olsavmic olsavmic changed the title Draft: fix(grep): route ripgrep-style invocations to rg instead of system grep fix(grep): route ripgrep-only-flag commands to rg instead of system grep May 31, 2026
`rg` and `grep` were both rewritten to `rtk grep`, which always ran ripgrep
as its backend. That collapsed the two tools' identities and broke whichever
one didn't match the backend: ripgrep-only flags (`--glob`, `-g`, `-P`,
`--files`) failed because the command fell through to system grep, while grep
commands silently misbehaved under rg (e.g. `-r` is recursive in grep but
`--replace` in rg, so `grep -rn foo` rewrote every match).

Preserve the invoked tool's identity from the rewrite onward, so each command
runs its own backend with its own semantics and there is never any crossover:

- rewrite: `grep ...` -> `rtk grep`, `rg ...` -> `rtk rg` (split the single rule).
- `rtk rg` (new subcommand): faithful ripgrep passthrough — all args forwarded
  verbatim, so rg-only flags work in any order — with RTK's by-file filtering
  (and raw streaming for info/list flags like `--files`).
- `rtk grep`: now runs system grep (grep semantics: BRE, grep flags, `-r`),
  parsing grep's `file:line:content` output; `-t/--file-type` maps to grep
  `--include`.
- shared `emit_filtered` takes a per-backend line parser (rg NUL form vs grep
  colon form).

Fixes rtk-ai#2167. Also addresses rtk-ai#2060, rtk-ai#1651.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@olsavmic olsavmic force-pushed the fix/grep-route-rg-not-system-grep branch from bc216fc to 20eedd1 Compare May 31, 2026 14:57
@olsavmic olsavmic changed the title fix(grep): route ripgrep-only-flag commands to rg instead of system grep fix(grep): respect the invoked tool — grep runs grep, rg runs ripgrep May 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants