rtk-ai / rtk-ai/rtk

Feature: use heading-style output for `rtk grep`

Open
#2,175 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

area:cli enhancement priority:medium
Dominant language
Rust
Stars
81.1k
Forks
5.1k
Avg merge
4d 21h
Merged PRs (30d)
35

Description

rg uses heading-style output by default when printing to a TTY:

$ rg -n "FilterLevel" src
src/core/filter.rs
8:pub enum FilterLevel {
14:impl FromStr for FilterLevel {
19:            "none" => Ok(FilterLevel::None),
20:            "minimal" => Ok(FilterLevel::Minimal),
21:            "aggressive" => Ok(FilterLevel::Aggressive),
27:impl std::fmt::Display for FilterLevel {
30:            FilterLevel::None => write!(f, "none"),
31:            FilterLevel::Minimal => write!(f, "minimal"),
32:            FilterLevel::Aggressive => write!(f, "aggressive"),
315:pub fn get_filter(level: FilterLevel) -> Box<dyn FilterStrategy> {
317:        FilterLevel::None => Box::new(NoFilter),
318:        FilterLevel::Minimal => Box::new(MinimalFilter),
319:        FilterLevel::Aggressive => Box::new(AggressiveFilter),
370:        assert_eq!(FilterLevel::from_str("none").unwrap(), FilterLevel::None);
372:            FilterLevel::from_str("minimal").unwrap(),
373:            FilterLevel::Minimal
376:            FilterLevel::from_str("aggressive").unwrap(),
377:            FilterLevel::Aggressive

src/main.rs
101:        level: core::filter::FilterLevel,

src/cmds/system/read.rs
3:use crate::core::filter::{self, FilterLevel, Language};
11:    level: FilterLevel,
84:    level: FilterLevel,
197:        run(file.path(), FilterLevel::Minimal, None, None, false, 0)?;

src/cmds/system/README.md
7:- `read.rs` uses `core/filter` for language-aware code stripping (FilterLevel: none/minimal/aggressive)

But in agent cli, commands are usually captured through non-TTY stdout. In that case, rg defaults to grep-like output instead:

$ rg -n "FilterLevel" src > output.txt
$ cat output.txt
src/main.rs:101:        level: core::filter::FilterLevel,
src/core/filter.rs:8:pub enum FilterLevel {
src/core/filter.rs:14:impl FromStr for FilterLevel {
src/core/filter.rs:19:            "none" => Ok(FilterLevel::None),
src/core/filter.rs:20:            "minimal" => Ok(FilterLevel::Minimal),
src/core/filter.rs:21:            "aggressive" => Ok(FilterLevel::Aggressive),
src/core/filter.rs:27:impl std::fmt::Display for FilterLevel {
src/core/filter.rs:30:            FilterLevel::None => write!(f, "none"),
src/core/filter.rs:31:            FilterLevel::Minimal => write!(f, "minimal"),
src/core/filter.rs:32:            FilterLevel::Aggressive => write!(f, "aggressive"),
src/core/filter.rs:315:pub fn get_filter(level: FilterLevel) -> Box<dyn FilterStrategy> {
src/core/filter.rs:317:        FilterLevel::None => Box::new(NoFilter),
src/core/filter.rs:318:        FilterLevel::Minimal => Box::new(MinimalFilter),
src/core/filter.rs:319:        FilterLevel::Aggressive => Box::new(AggressiveFilter),
src/core/filter.rs:370:        assert_eq!(FilterLevel::from_str("none").unwrap(), FilterLevel::None);
src/core/filter.rs:372:            FilterLevel::from_str("minimal").unwrap(),
src/core/filter.rs:373:            FilterLevel::Minimal
src/core/filter.rs:376:            FilterLevel::from_str("aggressive").unwrap(),
src/core/filter.rs:377:            FilterLevel::Aggressive
src/cmds/system/README.md:7:- `read.rs` uses `core/filter` for language-aware code stripping (FilterLevel: none/minimal/aggressive)
src/cmds/system/read.rs:3:use crate::core::filter::{self, FilterLevel, Language};
src/cmds/system/read.rs:11:    level: FilterLevel,
src/cmds/system/read.rs:84:    level: FilterLevel,
src/cmds/system/read.rs:197:        run(file.path(), FilterLevel::Minimal, None, None, false, 0)?;

Because of this, rtk grep ends up producing output similar to the non-TTY rg format:

$ rtk grep -n "FilterLevel" src
24 matches in 4 files:

src/cmds/system/README.md:7:...filter` for language-aware code stripping (FilterLevel: none/minimal/aggressive)
src/cmds/system/read.rs:3:use crate::core::filter::{self, FilterLevel, Language};
src/cmds/system/read.rs:11:level: FilterLevel,
src/cmds/system/read.rs:84:level: FilterLevel,
src/cmds/system/read.rs:197:run(file.path(), FilterLevel::Minimal, None, None, false, 0)?;
src/core/filter.rs:8:pub enum FilterLevel {
src/core/filter.rs:14:impl FromStr for FilterLevel {
src/core/filter.rs:19:"none" => Ok(FilterLevel::None),
src/core/filter.rs:20:"minimal" => Ok(FilterLevel::Minimal),
src/core/filter.rs:21:"aggressive" => Ok(FilterLevel::Aggressive),
src/core/filter.rs:27:impl std::fmt::Display for FilterLevel {
src/core/filter.rs:30:FilterLevel::None => write!(f, "none"),
src/core/filter.rs:31:FilterLevel::Minimal => write!(f, "minimal"),
src/core/filter.rs:32:FilterLevel::Aggressive => write!(f, "aggressive"),
src/core/filter.rs:315:pub fn get_filter(level: FilterLevel) -> Box<dyn FilterStrategy> {
src/core/filter.rs:317:FilterLevel::None => Box::new(NoFilter),
src/core/filter.rs:318:FilterLevel::Minimal => Box::new(MinimalFilter),
src/core/filter.rs:319:FilterLevel::Aggressive => Box::new(AggressiveFilter),
src/core/filter.rs:370:assert_eq!(FilterLevel::from_str("none").unwrap(), FilterLevel::None);
src/core/filter.rs:372:FilterLevel::from_str("minimal").unwrap(),
src/core/filter.rs:373:FilterLevel::Minimal
src/core/filter.rs:376:FilterLevel::from_str("aggressive").unwrap(),
src/core/filter.rs:377:FilterLevel::Aggressive
src/main.rs:101:level: core::filter::FilterLevel,

This repeats the file path for every match, even when many matches come from the same file. For agent output, this is
noticeably more verbose than rg's default TTY heading style.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at the rtk grep command entry point and trace how matches are formatted when stdout is captured rather than attached to a TTY. Compare the current repeated-path output with the heading-style example in the issue, then add coverage for grouped file headings and verify that the requested output is produced.

Written by the indexing model from the issue text.

Assessment

Tech stack
rust
Domain
cli
Issue type
Feature
Difficulty
3/5
Estimated time
1-2 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
68/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.