Understanding RTK's Filter Pipeline and Command Routing Architecture
RTK (Rust Token Killer) uses a three-layer architecture where Clap routes commands to specific modules, core::runner executes them in one of three run modes, and a strategy-based filter pipeline processes output before it reaches the language model.
The RTK CLI proxy intercepts system commands and optimizes their output for LLM consumption through a deterministic pipeline. According to the rtk-ai/rtk source code, this architecture combines command routing, execution orchestration, and text filtering into a cohesive system that minimizes token usage while preserving context.
Command Routing: From CLI to Module Dispatch
The entry point in src/main.rs uses Clap to parse arguments and immediately dispatch to the appropriate command handler. This routing layer is deliberately thin to keep proxy logic centralized.
// src/main.rs
let cli = Cli::try_parse()?; // Auto-generated Commands enum
match cli.command {
Commands::Ls { args } => ls::run(&args, cli.verbose)?,
Commands::Read { files, level, .. } => {
// ... read module handles per-file filtering
}
// ... additional sub-commands
}
Each sub-command module in src/cmds/*/*.rs (such as ls.rs, git.rs, or cargo_cmd.rs) implements a run function that constructs a std::process::Command and passes it to the execution core. The helper function resolved_command in src/core/utils.rs resolves executables in PATH or validates full paths before execution begins.
Execution Core and Run Modes
The core::runner module orchestrates how commands execute and how their output is captured. Located in src/core/runner.rs, the run function accepts a RunMode that determines the I/O strategy.
// src/core/runner.rs
pub fn run(
mut cmd: Command,
tool_name: &str,
args_display: &str,
mode: RunMode<'_>,
opts: RunOptions<'_>,
) -> Result<i32> {
let timer = tracking::TimedExecution::start();
match mode {
RunMode::Filtered(filter_fn) => {
let result = stream::run_streaming(&mut cmd,
StdinMode::Null,
FilterMode::CaptureOnly)?;
// Apply filter_fn, optional tee, and track metrics
}
RunMode::Streamed(filter) => { /* ... */ }
RunMode::Passthrough => { /* ... */ }
}
}
RunMode variants control the execution flow:
- Filtered: Captures complete output, applies a filter function, then prints
- Streamed: Processes data through a
StreamFilterwhile it arrives - Passthrough: Inherits the child process's stdio directly without capture
The core::stream module handles low-level I/O through run_streaming, which returns a StreamResult containing raw, raw_stdout, raw_stderr, and exit status. When commands fail, core::tee can display raw output alongside helpful LLM hints.
Filter Pipeline Architecture
The filter system in src/core/filter.rs implements a strategy pattern that processes captured text based on language-specific rules and user-selected aggressiveness levels.
Filter Strategies
Three concrete implementations of the FilterStrategy trait define the reduction levels:
// src/core/filter.rs
pub trait FilterStrategy {
fn filter(&self, content: &str, lang: &Language) -> String;
}
| Strategy | Behavior |
|---|---|
| NoFilter | Returns input unchanged (clone only) |
| MinimalFilter | Strips line and block comments, removes empty lines, collapses whitespace, preserves documentation strings |
| AggressiveFilter | Extends MinimalFilter by preserving only imports and type signatures, replacing implementation bodies with placeholder comments |
The function core::filter::get_filter(level) instantiates the appropriate strategy based on CLI flags (--level minimal|aggressive|none) or TOML configuration.
Text Processing and Truncation
Language detection occurs via Language::from_extension, which drives the CommentPatterns system for per-language comment delimiters. After filtering, smart_truncate limits output to a maximum line count while preserving critical signatures and import statements.
use rtk::core::filter::{get_filter, FilterLevel, Language};
let src = r#"//! Example crate
fn main() {
// Print hello
println!("Hello");
}"#;
let filter = get_filter(FilterLevel::Minimal);
let out = filter.filter(src, &Language::Rust);
// Result: fn main() { println!("Hello"); }
TOML-Based Custom Filters
Before applying generic strategies, RTK checks src/core/toml_filter.rs for command-specific rules in .rtk.toml files. If a regex pattern matches the current command, the custom filter runs instead of the standard pipeline.
# .rtk.toml
[[filter]]
command = "^cargo test"
filter = """
grep -E "test result: .*"
"""
The find_matching_filter function locates applicable rules, while apply_filter executes user-defined shell pipelines on captured output.
Complete Request Flow
The architecture follows a deterministic path from input to filtered output:
- Parse:
main.rsuses Clap to generate theCommandsenum - Build: Sub-command modules construct
std::process::Commandobjects - Execute:
core::runner::runselects theRunModeand invokescore::stream::run_streaming - Filter:
get_filter(level)returns aFilterStrategyapplied to theStreamResult - Output:
core::teeoptionally displays results with telemetry tracking viacore::tracking
If command parsing fails, run_fallback in main.rs attempts TOML filter matching before falling back to passthrough execution.
Code Examples
Running a Filtered Directory Listing
rtk ls -la
Behind the scenes, Commands::Ls routes to ls::run, which builds the command and calls runner::run_filtered with a closure invoking compact_ls. The filter transforms verbose ls output into a compact directory tree with summaries.
Aggressive Filtering with Line Limits
rtk read src/index.ts --level aggressive --max-lines 50
This command:
- Loads the TypeScript file and detects
Language::TypeScript - Selects
AggressiveFilterviaget_filter - Removes function bodies, keeping only signatures and imports
- Applies
smart_truncateto limit output to 50 lines
Result:
export function fetchData(url: string): Promise<any> {
// ... implementation
}
Implementing a Custom Filter Strategy
// src/core/filter.rs
pub struct CustomFilter;
impl FilterStrategy for CustomFilter {
fn filter(&self, content: &str, _lang: &Language) -> String {
content.lines()
.filter(|line| !line.trim().is_empty())
.take(100)
.collect::<Vec<_>>()
.join("\n")
}
}
Summary
- Command routing in
src/main.rsuses Clap enums to dispatch to command modules that buildstd::process::Commandobjects - Execution core in
src/core/runner.rssupports three run modes:Filtered,Streamed, andPassthrough - Filter pipeline implements a strategy pattern with
NoFilter,MinimalFilter, andAggressiveFilterstrategies based on theFilterStrategytrait - Language detection via file extensions drives comment stripping logic in
src/core/filter.rs - TOML overrides in
src/core/toml_filter.rsallow project-specific filter rules that supersede default strategies - Telemetry tracking records execution time and token savings for analytics via
core::tracking
Frequently Asked Questions
How does RTK decide which filter strategy to apply?
RTK determines the filter strategy through a hierarchy of sources. First, it checks for a matching rule in .rtk.toml via src/core/toml_filter.rs. If no TOML rule exists, it uses the --level CLI flag (minimal, aggressive, or none) passed to get_filter(level) in src/core/filter.rs. Finally, if no flag is provided, it falls back to the default level specified in the global configuration.
What is the difference between Filtered and Streamed run modes?
Filtered mode captures the complete output into memory using run_streaming with FilterMode::CaptureOnly, then applies the filter function to the entire buffer before printing. Streamed mode processes data chunk-by-chunk through a StreamFilter as it arrives from the child process, enabling real-time display while still reducing tokens. Filtered mode suits command outputs that need context-aware reduction, while Streamed mode handles long-running processes.
Can I extend RTK with custom filtering logic for specific file types?
Yes, you can extend the filter pipeline by implementing the FilterStrategy trait in src/core/filter.rs and adding your implementation to the get_filter function's match arms. You would also need to update Language::from_extension or add custom detection logic if your file type isn't already supported. Alternatively, use the TOML-based system in src/core/toml_filter.rs to define shell-based filters for specific commands without recompiling.
Where does RTK handle command execution failures and output?
Execution failure handling resides in src/core/runner.rs and src/core/tee.rs. When a command exits with a non-zero status, the runner checks RunOptions flags like skip_filter_on_failure. If configured, it invokes the tee functionality to display raw output alongside helpful hints for the LLM, ensuring the model receives context about why the command failed even when filtering is active.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →