Skip to content
Open
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.34.1"
".": "0.34.3"
}
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,38 @@ All notable changes to rtk (Rust Token Killer) will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [0.34.3](https://github.com/rtk-ai/rtk/compare/v0.34.2...v0.34.3) (2026-04-02)


### Bug Fixes

* **automod:** add auto discovery for cmds ([234909d](https://github.com/rtk-ai/rtk/commit/234909d2c754ade2fdc939b0a1435a8e34ffc305))
* **ci:** fix validate-docs.sh broken module count check ([bbe3da6](https://github.com/rtk-ai/rtk/commit/bbe3da642b5fc4b065b13a65647ea0ebf5264e65))
* **cleaning:** constant extract ([aabc016](https://github.com/rtk-ai/rtk/commit/aabc0167bc013fd2d0c61a687580f6e69305500a))
* **cmds:** migrate remaining exit_code to exit_code_from_output ([ba9fa34](https://github.com/rtk-ai/rtk/commit/ba9fa345f3d1d14bd0af236ec9aa8a9a0e5581d6))
* **cmds:** more covering for run_filtered ([e48485a](https://github.com/rtk-ai/rtk/commit/e48485adc6a33d12b70664598020595cf7dfcd7e))
* **docs:** add documentation ([2f7278a](https://github.com/rtk-ai/rtk/commit/2f7278ac5992bf2e84b763fb05642d89900ba495))
* **docs:** add maintainers docs ([14265b4](https://github.com/rtk-ai/rtk/commit/14265b48c3a15e459a31da11250a51ab5830a508))
* **refacto-p1:** unified cmds execution flow (+ rm dead code) ([75bd607](https://github.com/rtk-ai/rtk/commit/75bd607d55235f313855f5fe8c9eceafd73700a7))
* **refacto-p2:** more standardize ([47a76ea](https://github.com/rtk-ai/rtk/commit/47a76ea35ed2fe02a3600792163f727fa3a94ff2))
* **refacto-p2:** more standardize ([92c671a](https://github.com/rtk-ai/rtk/commit/92c671a175a5e2bf09720fd1a8591140bcb473a0))
* **refacto:** wrappers for standardization, exit codes lexer tokenizer, constants, code clean ([bff0258](https://github.com/rtk-ai/rtk/commit/bff02584243f1b73418418b0c05365acf56fbb36))
* **registry:** quoted env prefix + inline regex cleanup + routing docs ([f3217a4](https://github.com/rtk-ai/rtk/commit/f3217a467b543a3181605b257162f2b3ab5d5df0))
* **review:** address PR [#910](https://github.com/rtk-ai/rtk/issues/910) review feedback ([0a8b8fd](https://github.com/rtk-ai/rtk/commit/0a8b8fd0693fa504f376146cbbcafe9ddf4632c8))
* **review:** PR [#934](https://github.com/rtk-ai/rtk/issues/934) ([5bd35a3](https://github.com/rtk-ai/rtk/commit/5bd35a33ad6abe5278749726bed19912664531c2))
* **review:** PR [#934](https://github.com/rtk-ai/rtk/issues/934) ([bae7930](https://github.com/rtk-ai/rtk/commit/bae79301194bbb48d1cbb39554096c3225f7cb73))
* **rules:** add wc RtkRule with pattern field for develop compat ([d75e864](https://github.com/rtk-ai/rtk/commit/d75e864f20451a5e17918c75f2ea32672f65e1f4))
* **standardize:** git+kube sub wrappers run_filtered ([7fd221f](https://github.com/rtk-ai/rtk/commit/7fd221f44660bcf411aa333d2c35a49ff89e7961))
* **standardize:** merge pattern into rues ([08aabb9](https://github.com/rtk-ai/rtk/commit/08aabb95c3ae6e0b734f696264e1e1a8c0f0b22e))

## [0.34.2](https://github.com/rtk-ai/rtk/compare/v0.34.1...v0.34.2) (2026-03-30)


### Bug Fixes

* **emots:** replace 📊 with "Summary:" ([495a152](https://github.com/rtk-ai/rtk/commit/495a152059feabc7b516b96e804757608b87a10a))
* **refacto-codebase:** technical docs & sub folders ([927daef](https://github.com/rtk-ai/rtk/commit/927daef49b8f771d195201d196378e27e0ee8a2b))

## [0.34.1](https://github.com/rtk-ai/rtk/compare/v0.34.0...v0.34.1) (2026-03-28)


Expand Down
2 changes: 1 addition & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rtk"
version = "0.34.1"
version = "0.34.3"
edition = "2021"
Comment on lines 1 to 4
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR description says “No existing files were modified” and “All changes are additive”, but this PR changes the crate version (and also updates CHANGELOG/Cargo.lock/release manifest). If the version bump is intentional, please update the PR description; otherwise, consider dropping these release/version changes from this docs-only PR.

Copilot uses AI. Check for mistakes.
authors = ["Patrick Szymkowiak"]
description = "Rust Token Killer - High-performance CLI proxy to minimize LLM token consumption"
Expand Down
64 changes: 64 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# RTK Documentation — Interface Contract

This directory is the source of truth for user-facing and contributor documentation.
It feeds the RTK website via the `prepare-docs.mjs` pipeline in `rtk-ai/rtk-website`.

## Structure

```
docs/
README.md <- This file (interface contract — do not remove)
guide/ -> User-facing documentation (Tab "Guide")
reference/ -> Contributor/technical documentation (Tab "Reference")
architecture/ -> Conceptual/visual documentation (Tab "Architecture")
```

## Frontmatter (required on every .md)

Every markdown file under `docs/guide/`, `docs/reference/`, and `docs/architecture/`
must include this frontmatter block at the top:

```yaml
---
title: string # Page title (used in sidebar + search)
description: string # One-line summary for search results and SEO
sidebar:
order: number # Position within the sidebar group (1 = first)
---
```

The `prepare-docs.mjs` pipeline validates this contract at build time and **fails fast**
with a clear error message if frontmatter is missing or malformed.

## Conventions

- **Filenames**: kebab-case, `.md` only (e.g., `getting-started.md`, `quick-start.md`)
- **Subdirectories**: become sidebar groups in Starlight (directory name = group label)
- **Internal links**: relative within the same tab (`./foo.md`, `./getting-started/installation.md`)
- **Cross-tab links**: full path from `docs/` root (`../../reference/internals/command-routing.md`)
- **Diagrams**: Mermaid in fenced code blocks (` ```mermaid `)
- **Code samples**: always specify the language (`rust`, `toml`, `bash`, `shell`)
- **Language**: English only

## Tabs overview

| Tab | Path | Audience |
|-----|------|----------|
| Guide | `docs/guide/` | End users installing and using RTK |
| Reference | `docs/reference/` | Contributors, maintainers, integrators |
| Architecture | `docs/architecture/` | Readers exploring design decisions and diagrams |

## Root files (do not move or modify structure)

The following files live at the repository root and are **not** managed by this pipeline.
They are the canonical source for GitHub display and remain unchanged.

- `README.md` — Project overview
- `INSTALL.md` — Installation reference (full)
- `CONTRIBUTING.md` — Contribution guide
- `SECURITY.md` — Security policy
- `ARCHITECTURE.md` — Full architecture document
- `CHANGELOG.md` — Release history

The guide files in `docs/` are derived, English-only, structured versions intended
for the website. They reference root files as source material but do not replace them.
68 changes: 68 additions & 0 deletions docs/architecture/decisions/proxy-architecture.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: Proxy Architecture
description: ADR — why RTK uses a CLI proxy pattern instead of shell aliases or wrappers
sidebar:
order: 2
---

# ADR: Proxy Architecture

## Decision

RTK is a CLI proxy: a single binary that intercepts commands, executes them as subprocesses, filters the output, and exits. Users invoke `rtk git status` instead of `git status`.

Hooks extend this by making the interception transparent — the AI agent's command is rewritten before execution, so neither the agent nor the user types `rtk`.

## Alternatives considered

### Shell aliases

```bash
alias git='rtk git'
alias cargo='rtk cargo'
```

**Rejected because:**
- Requires shell configuration per-user, per-machine, per-shell
- Doesn't work for non-interactive contexts (scripts, CI, AI agents)
- Can't be installed programmatically without modifying shell dotfiles
- Breaks if the user has other aliases or functions with the same name

### Shell function wrappers

Similar to aliases but more fragile. Same problems.

### `LD_PRELOAD` / dynamic linking interception

**Rejected because:**
- Platform-specific (Linux only with glibc)
- Security restrictions (macOS SIP, container environments)
- Complex to implement, maintain, and debug

### Hook-only approach (no explicit `rtk` prefix)

Make RTK entirely invisible — install hooks and never expose `rtk <cmd>` as a user-facing interface.

**Rejected because:**
- Users need a way to invoke RTK explicitly for debugging (`rtk git status -vvv`)
- `rtk gain` and `rtk discover` need a namespace
- Transparent hooks are additive, not a replacement for the explicit interface

## Why the proxy pattern works

**Single binary, no configuration:** `rtk git status` works identically on macOS, Linux, and Windows. No dotfiles. No shell-specific setup.

**Explicit and debuggable:** `-v`/`-vv`/`-vvv` flags expose what RTK is doing at each phase. `RTK_DISABLED=1` bypasses it for one command.

**Exit code preservation:** RTK propagates the underlying tool's exit code. CI pipelines that check `$?` work correctly.

**Fail-safe:** If RTK's filter fails, it falls back to raw output. The user always gets a result.

**Hook interception as an enhancement:** The hook layer adds transparency on top of the proxy pattern — it rewrites `git status` to `rtk git status` before the agent sees it. But the proxy interface remains available for direct use, debugging, and tools that can't be hooked.

## Consequences

- Every supported command needs a module in `src/cmds/`
- Unsupported commands pass through transparently (no breakage)
- The binary grows as new commands are added (~5MB currently, well within the `<5MB` soft target after stripping)
- Adding a new command = adding a module + registering in `main.rs` + adding rewrite pattern in `discover/registry.rs`
52 changes: 52 additions & 0 deletions docs/architecture/decisions/why-no-async.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
title: Why No Async
description: ADR — why RTK is single-threaded and does not use tokio or async runtimes
sidebar:
order: 1
---

# ADR: Why No Async

## Decision

RTK is single-threaded. No `tokio`, `async-std`, or `futures`. All I/O is blocking.

## Context

RTK is a CLI proxy that runs for milliseconds and exits. The typical invocation:

1. Parse CLI arguments (~0.1ms)
2. Spawn one subprocess and capture its output (~2-5ms)
3. Filter the output (~0.1ms)
4. Print and exit

There is no concurrent I/O, no network server, no parallel request handling.

## Consequences

**Why async would hurt:**

- `tokio` adds 5-10ms to startup time from runtime initialization. RTK's target is `<10ms` total. Async would consume half the budget before the first useful line of code.
- The entire value proposition of RTK is zero-overhead transparency. If developers perceive any delay, they disable it.
- One subprocess. One output stream. No concurrency needed.

**Why blocking I/O is correct here:**

- `std::process::Command::output()` captures stdout + stderr in one blocking call. This is exactly what RTK needs.
- No event loop required. No `.await` noise in filter code.
- Binary stays under 5MB. No runtime dependencies.

## Tradeoffs

**What we give up:**
- Hypothetical future parallelism (e.g., running multiple filters in parallel). Not needed today.
- Async ecosystem crates (reqwest, sqlx). RTK uses `rusqlite` (sync) and `ureq` (sync) instead.

**What we gain:**
- `<10ms` startup, always.
- Simple, readable filter code with no `.await` punctuation.
- No runtime initialization path that can fail.

## Rule

If you add a dependency that pulls in `tokio` or any async runtime, the PR will be rejected. Check before adding: `cargo tree | grep tokio`.
65 changes: 65 additions & 0 deletions docs/architecture/diagrams/command-flow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
---
title: Command Flow
description: End-to-end diagram of how a command flows through RTK from agent to LLM
sidebar:
order: 1
---

# Command Flow

End-to-end flow from the AI agent issuing a command to the filtered output reaching the LLM.

## With hook (transparent rewrite)

```mermaid
flowchart TD
A["AI Agent\n(Claude Code, Cursor, etc.)"] -->|"runs: cargo test"| B

subgraph HOOK ["Hook Interception (PreToolUse)"]
B["Hook reads JSON input\nextract command string"] --> C
C["rtk rewrite 'cargo test'"] --> D
D{"Registry match?"}
D -->|"yes"| E["returns 'rtk cargo test'"]
D -->|"no match"| F["returns original unchanged"]
end

E --> G
F --> G

subgraph RTK ["RTK Binary"]
G["Phase 1: Parse\nClap → Commands::Cargo"] --> H
H["Phase 2: Route\ncargo::run(args)"] --> I
I["Phase 3: Execute\nstd::process::Command::new('cargo')\n.args(['test'])"] --> J
J["Phase 4: Filter\nfailures only\n200 lines → 5 lines"] --> K
K["Phase 5: Print\nprintln!(filtered)"] --> L
L["Phase 6: Track\nSQLite INSERT\n(input=5000tok, output=50tok)"]
end

K -->|"filtered output"| M["LLM Context\n~90% fewer tokens"]
```

## Without hook (direct usage)

```mermaid
flowchart LR
A["Developer\ntype: rtk git status"] --> B["RTK Binary"]
B --> C["git status (subprocess)"]
C -->|"20 lines raw"| B
B -->|"5 lines filtered"| D["Terminal\n(or LLM context)"]
```

## Filter lookup (TOML path)

```mermaid
flowchart LR
CMD["rtk my-tool args"] --> P1
P1{"1. .rtk/filters.toml\n(project-local)"}
P1 -->|"match"| WIN["apply filter → print"]
P1 -->|"no match"| P2
P2{"2. ~/.config/rtk/filters.toml\n(user-global)"}
P2 -->|"match"| WIN
P2 -->|"no match"| P3
P3{"3. BUILTIN_TOML\n(binary)"}
P3 -->|"match"| WIN
P3 -->|"no match"| P4[["exec raw\n(passthrough)"]]
```
71 changes: 71 additions & 0 deletions docs/architecture/diagrams/filter-pipeline.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
title: Filter Pipeline
description: How a TOML filter goes from file to execution — build pipeline and runtime stages
sidebar:
order: 2
---

# Filter Pipeline

## Build pipeline

```mermaid
flowchart TD
A[["src/filters/my-tool.toml\n(new file)"]] --> B

subgraph BUILD ["cargo build"]
B["build.rs\n1. ls src/filters/*.toml\n2. sort alphabetically\n3. concat → BUILTIN_TOML"] --> C
C{"TOML valid?\nDuplicate names?"} -->|"❌ panic"| D[["Build fails\nerror points to bad file"]]
C -->|"✅ ok"| E[["OUT_DIR/builtin_filters.toml\n(generated)"]]
E --> F["rustc embeds via include_str!"]
F --> G[["rtk binary\nBUILTIN_TOML embedded"]]
end

subgraph TESTS ["cargo test"]
H["test_builtin_filter_count\nassert_eq!(filters.len(), N)"] -->|"❌ wrong count"| I[["FAIL"]]
J["test_builtin_all_filters_present\nassert!(names.contains('my-tool'))"] -->|"❌ name missing"| K[["FAIL"]]
L["test_builtin_all_filters_have_inline_tests\nassert!(tested.contains(name))"] -->|"❌ no tests"| M[["FAIL"]]
end

subgraph VERIFY ["rtk verify"]
N["runs [[tests.my-tool]]\ninput → filter → compare expected"]
N -->|"❌ mismatch"| O[["FAIL\nshows actual vs expected"]]
N -->|"✅ pass"| P[["All tests passed"]]
end

G --> H & J & L & N
```

## Runtime stages

```mermaid
flowchart TD
CMD["rtk my-tool args"] --> LOOKUP

subgraph LOOKUP ["Filter Lookup"]
L1{".rtk/filters.toml"} -->|"match"| APPLY
L1 -->|"no match"| L2
L2{"~/.config/rtk/filters.toml"} -->|"match"| APPLY
L2 -->|"no match"| L3
L3{"BUILTIN_TOML"} -->|"match"| APPLY
L3 -->|"no match"| RAW[["exec raw (passthrough)"]]
end

APPLY --> EXEC["exec command\ncapture stdout"]
EXEC --> PIPE

subgraph PIPE ["8-stage filter pipeline"]
S1["1. strip_ansi"] --> S2
S2["2. replace"] --> S3
S3{"3. match_output\nshort-circuit?"}
S3 -->|"✅ match"| MSG[["emit on_match\nstop"]]
Copy link

Copilot AI Apr 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the runtime pipeline diagram, match_output is shown emitting on_match, but the TOML schema uses match_output = [{ pattern, message, unless? }] and there is no on_match field (see src/core/toml_filter.rs). The diagram should reference the rule’s message instead.

Suggested change
S3 -->|"✅ match"| MSG[["emit on_match\nstop"]]
S3 -->|"✅ match"| MSG[["emit rule message\nstop"]]

Copilot uses AI. Check for mistakes.
S3 -->|"no match"| S4
S4["4. strip/keep_lines"] --> S5
S5["5. truncate_lines_at"] --> S6
S6["6. tail_lines"] --> S7
S7["7. max_lines"] --> S8
S8{"8. output empty?"}
S8 -->|"yes"| EMPTY[["emit on_empty"]]
S8 -->|"no"| OUT[["print filtered output\n+ exit code"]]
end
```
Loading