# Understanding RTK's Filter Pipeline and Command Routing Architecture

> Explore RTK's filter pipeline and command routing architecture. Learn how Clap routes commands, core::runner executes them, and a strategy-based filter pipeline processes output. Understand RTK's three-layer design for efficien...

- Repository: [rtk-ai/rtk](https://github.com/rtk-ai/rtk)
- Tags: architecture
- Published: 2026-04-24

---

**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`](https://github.com/rtk-ai/rtk/blob/main/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.

```rust
// 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`](https://github.com/rtk-ai/rtk/blob/main/ls.rs), [`git.rs`](https://github.com/rtk-ai/rtk/blob/main/git.rs), or [`cargo_cmd.rs`](https://github.com/rtk-ai/rtk/blob/main/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`](https://github.com/rtk-ai/rtk/blob/main/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`](https://github.com/rtk-ai/rtk/blob/main/src/core/runner.rs), the `run` function accepts a `RunMode` that determines the I/O strategy.

```rust
// 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 `StreamFilter` while 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`](https://github.com/rtk-ai/rtk/blob/main/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:

```rust
// 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.

```rust
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`](https://github.com/rtk-ai/rtk/blob/main/src/core/toml_filter.rs) for command-specific rules in [`.rtk.toml`](https://github.com/rtk-ai/rtk/blob/main/.rtk.toml) files. If a regex pattern matches the current command, the custom filter runs **instead of** the standard pipeline.

```toml

# .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:

1. **Parse**: [`main.rs`](https://github.com/rtk-ai/rtk/blob/main/main.rs) uses Clap to generate the `Commands` enum
2. **Build**: Sub-command modules construct `std::process::Command` objects
3. **Execute**: `core::runner::run` selects the `RunMode` and invokes `core::stream::run_streaming`
4. **Filter**: `get_filter(level)` returns a `FilterStrategy` applied to the `StreamResult`
5. **Output**: `core::tee` optionally displays results with telemetry tracking via `core::tracking`

If command parsing fails, `run_fallback` in [`main.rs`](https://github.com/rtk-ai/rtk/blob/main/main.rs) attempts TOML filter matching before falling back to passthrough execution.

## Code Examples

### Running a Filtered Directory Listing

```bash
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

```bash
rtk read src/index.ts --level aggressive --max-lines 50

```

This command:
1. Loads the TypeScript file and detects `Language::TypeScript`
2. Selects `AggressiveFilter` via `get_filter`
3. Removes function bodies, keeping only signatures and imports
4. Applies `smart_truncate` to limit output to 50 lines

Result:

```typescript
export function fetchData(url: string): Promise<any> {
    // ... implementation
}

```

### Implementing a Custom Filter Strategy

```rust
// 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.rs`](https://github.com/rtk-ai/rtk/blob/main/src/main.rs) uses Clap enums to dispatch to command modules that build `std::process::Command` objects
- **Execution core** in [`src/core/runner.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/runner.rs) supports three run modes: `Filtered`, `Streamed`, and `Passthrough`
- **Filter pipeline** implements a strategy pattern with `NoFilter`, `MinimalFilter`, and `AggressiveFilter` strategies based on the `FilterStrategy` trait
- **Language detection** via file extensions drives comment stripping logic in [`src/core/filter.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/filter.rs)
- **TOML overrides** in [`src/core/toml_filter.rs`](https://github.com/rtk-ai/rtk/blob/main/src/core/toml_filter.rs) allow 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`](https://github.com/rtk-ai/rtk/blob/main/.rtk.toml) via [`src/core/toml_filter.rs`](https://github.com/rtk-ai/rtk/blob/main/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`](https://github.com/rtk-ai/rtk/blob/main/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`](https://github.com/rtk-ai/rtk/blob/main/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`](https://github.com/rtk-ai/rtk/blob/main/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`](https://github.com/rtk-ai/rtk/blob/main/src/core/runner.rs) and [`src/core/tee.rs`](https://github.com/rtk-ai/rtk/blob/main/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.