chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,189 @@
|
||||
# Telemetry
|
||||
|
||||
RTK collects anonymous, aggregate usage metrics once per day to help improve the product. Telemetry is **disabled by default** and requires explicit consent during `rtk init` or `rtk telemetry enable`.
|
||||
|
||||
## Data Collector
|
||||
|
||||
**Entity**: `RTK AI Labs`
|
||||
**Contact**: contact@rtk-ai.app
|
||||
|
||||
## Why we collect telemetry
|
||||
|
||||
RTK supports 100+ commands across 15+ ecosystems. Without telemetry, we have no way to know:
|
||||
|
||||
- Which commands are used most and need the best filters
|
||||
- Which filters are underperforming and need improvement
|
||||
- Which ecosystems to prioritize for new filter development
|
||||
- How much value RTK delivers to users (token savings in $ terms)
|
||||
- Whether users stay engaged over time or churn after trying RTK
|
||||
|
||||
This data directly drives our roadmap. For example, if telemetry shows that 40% of users run Python commands but only 10% of our filters cover Python, we know where to invest next.
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Once per day** (23-hour interval), RTK sends a single HTTPS POST to our telemetry endpoint
|
||||
2. The ping runs in a **background thread** and never blocks the CLI (2-second timeout)
|
||||
3. A marker file prevents duplicate pings within the interval
|
||||
4. If the server is unreachable, the ping is silently dropped — no retries, no queue
|
||||
|
||||
**Source code**: [`src/core/telemetry.rs`](../src/core/telemetry.rs)
|
||||
|
||||
## What is collected
|
||||
|
||||
### Identity (anonymous)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `device_hash` | `a3f8c9...` (64 hex chars) | Count unique installations. SHA-256 of a per-device random salt stored locally (`~/.local/share/rtk/.device_salt`). Not reversible. No hostname or username included. |
|
||||
|
||||
### Environment
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `version` | `0.34.1` | Track adoption of new versions |
|
||||
| `os` | `macos` | Know which platforms to support and test |
|
||||
| `arch` | `aarch64` | Prioritize ARM vs x86 builds |
|
||||
| `install_method` | `homebrew` | Understand distribution channels (homebrew/cargo/script/nix) |
|
||||
|
||||
### Usage volume
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `commands_24h` | `142` | Daily activity level |
|
||||
| `commands_total` | `32888` | Lifetime usage — segment light vs heavy users |
|
||||
| `top_commands` | `["git", "cargo", "ls"]` | Most popular tools (names only, max 5) |
|
||||
| `tokens_saved_24h` | `450000` | Daily value delivered |
|
||||
| `tokens_saved_total` | `96500000` | Lifetime value delivered |
|
||||
| `savings_pct` | `72.5` | Overall effectiveness |
|
||||
|
||||
### Quality (filter improvement)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `passthrough_top` | `["git:15", "npm:8"]` | Top 5 commands with 0% savings — these need filters |
|
||||
| `parse_failures_24h` | `3` | Filter fragility — high count means filters are breaking |
|
||||
| `low_savings_commands` | `["rtk docker ps:25%"]` | Commands averaging <30% savings — filters to improve |
|
||||
| `avg_savings_per_command` | `68.5` | Unweighted average (vs global which is volume-biased) |
|
||||
|
||||
### Ecosystem distribution
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `ecosystem_mix` | `{"git": 45, "cargo": 20, "js": 15}` | Category percentages — where to invest filter development |
|
||||
|
||||
### Retention (engagement)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `first_seen_days` | `45` | Installation age in days |
|
||||
| `active_days_30d` | `22` | Days with at least 1 command in last 30 days — measures stickiness |
|
||||
|
||||
### Economics
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `tokens_saved_30d` | `12000000` | 30-day token savings for trend analysis |
|
||||
| `estimated_savings_usd_30d` | `36.0` | Estimated dollar value saved (at ~$3/Mtok input pricing, Claude Sonnet) |
|
||||
|
||||
### Adoption
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `hook_type` | `claude` | Which AI agent hook is installed (claude/gemini/codex/cursor/none) |
|
||||
| `custom_toml_filters` | `3` | Number of user-created TOML filter files — DSL adoption |
|
||||
|
||||
### Configuration (user maturity)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `has_config_toml` | `true` | Whether user has customized RTK config |
|
||||
| `exclude_commands_count` | `2` | Commands excluded from rewriting — high count may indicate frustration |
|
||||
| `projects_count` | `5` | Distinct project paths — multi-project = power user |
|
||||
|
||||
### Feature adoption
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `meta_usage` | `{"gain": 5, "discover": 2}` | Which RTK features are actually used |
|
||||
|
||||
## What is NOT collected
|
||||
|
||||
- Source code or file contents
|
||||
- Full command lines or arguments (only tool names like "git", "cargo")
|
||||
- File paths or directory structures
|
||||
- Secrets, API keys, or environment variable values
|
||||
- Repository names or URLs
|
||||
- Personally identifiable information
|
||||
- IP addresses (not stored in telemetry pings; stored temporarily in erasure audit log for accountability, anonymized after 6 months)
|
||||
|
||||
## Consent
|
||||
|
||||
Telemetry requires explicit opt-in consent (GDPR Art. 6, 7). Consent is requested during `rtk init` or via `rtk telemetry enable`. Without consent, no data is sent.
|
||||
|
||||
```bash
|
||||
rtk telemetry status # Check current consent state
|
||||
rtk telemetry enable # Give consent (interactive prompt)
|
||||
rtk telemetry disable # Withdraw consent
|
||||
rtk telemetry forget # Withdraw consent + delete local data + request server erasure
|
||||
```
|
||||
|
||||
Environment variable override (blocks telemetry regardless of consent):
|
||||
```bash
|
||||
export RTK_TELEMETRY_DISABLED=1
|
||||
```
|
||||
|
||||
## Retention Policy
|
||||
|
||||
- **Server-side**: telemetry records are retained for a maximum of **12 months**, then automatically purged (periodic task every 24 hours).
|
||||
- **Server-side (erasure log)**: IP addresses in the erasure audit log are **anonymized after 6 months** (GDPR — IP is personal data).
|
||||
- **Client-side**: the local SQLite database (`~/.local/share/rtk/tracking.db`) retains data for **90 days** by default (configurable via `tracking.history_days` in `config.toml`). Deleted entirely by `rtk telemetry forget`.
|
||||
|
||||
## Your Rights (GDPR)
|
||||
|
||||
Under the EU General Data Protection Regulation, you have the right to:
|
||||
|
||||
- **Access** your data: `rtk telemetry status` shows your device hash; the telemetry payload is fully documented above.
|
||||
- **Rectification**: since data is anonymous and aggregate, rectification is not applicable.
|
||||
- **Erasure** (Art. 17): run `rtk telemetry forget` to delete local data and send an erasure request to the server. Alternatively, email contact@rtk-ai.app with your device hash.
|
||||
- **Restriction of processing**: `rtk telemetry disable` stops all data collection immediately.
|
||||
- **Portability**: the local SQLite database at `~/.local/share/rtk/tracking.db` contains all locally stored data.
|
||||
- **Objection**: `rtk telemetry disable` or `export RTK_TELEMETRY_DISABLED=1`.
|
||||
|
||||
## Erasure Procedure
|
||||
|
||||
1. Run `rtk telemetry forget` — this disables telemetry, deletes your device salt, ping marker, and local tracking database (`history.db`), then sends an erasure request to the server.
|
||||
2. If the server is unreachable, the CLI prints your full device hash and fallback instructions to email contact@rtk-ai.app for manual erasure.
|
||||
3. You can also email contact@rtk-ai.app directly to request manual erasure.
|
||||
|
||||
## Data Handling
|
||||
|
||||
- Telemetry endpoint URL and auth token are injected at **compile time** via `option_env!()` — they are not in the source code
|
||||
- All communications use HTTPS (TLS)
|
||||
- Data is used exclusively for RTK product improvement
|
||||
- No data is sold or shared with third parties
|
||||
- Aggregate statistics may be published (e.g. "70% of RTK users are on macOS")
|
||||
|
||||
### Server-side Requirements
|
||||
|
||||
The telemetry server must implement:
|
||||
- `POST /erasure` endpoint accepting `{"device_hash": "...", "action": "erasure"}`, authenticated via `X-RTK-Token`
|
||||
- Automatic periodic purge of telemetry records older than 12 months
|
||||
- Audit log for erasure requests (GDPR Art. 17(2) accountability) with IP anonymization after 6 months
|
||||
|
||||
## For contributors
|
||||
|
||||
The telemetry implementation lives in `src/core/telemetry.rs`. Key design decisions:
|
||||
|
||||
- **Fire-and-forget**: errors are silently ignored, never shown to users
|
||||
- **Non-blocking**: runs in a `std::thread::spawn`, 2-second timeout
|
||||
- **No async**: consistent with RTK's single-threaded design
|
||||
- **Compile-time gating**: if `RTK_TELEMETRY_URL` is not set at build time, all telemetry code is dead — the binary makes zero network calls
|
||||
- **23-hour interval**: prevents clock-drift accumulation that a strict 24h interval would cause
|
||||
|
||||
When adding new fields:
|
||||
1. Add the query method to `src/core/tracking.rs`
|
||||
2. Add the field to `EnrichedStats` in `src/core/telemetry.rs`
|
||||
3. Populate it in `get_enriched_stats()`
|
||||
4. Add it to the JSON payload in `send_ping()`
|
||||
5. Update this document and the README.md privacy table
|
||||
6. Ensure the field contains only **aggregate counts or anonymized names** — no raw paths, arguments, or user data
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,186 @@
|
||||
# RTK Coding Practices v1.0
|
||||
|
||||
This document follows the [Design Philosophy](../../CONTRIBUTING.md#design-philosophy) in `CONTRIBUTING.md`. Once you understand the mental model there, this guide describes the coding practices we use day-to-day in RTK and what reviewers will look for on your PR.
|
||||
|
||||
Our goal is to keep the codebase consistent and easy to extend. PRs that deviate from these practices may be asked for changes during review — this is guidance, not a gate. If a rule seems wrong for your specific case, flag it in the PR and we'll discuss.
|
||||
|
||||
> **Heads up:** RTK has grown quickly and some code in the repository predates these practices. You may spot modules that don't fully follow them — this is expected, and core/ecosystem maintainers will refactor them over time. When in doubt, follow the practices below for new code rather than mirroring older patterns.
|
||||
|
||||
---
|
||||
|
||||
## Quick Start for Contributors
|
||||
|
||||
New to RTK? The fastest path to a mergeable first PR:
|
||||
|
||||
1. **Read the flow once.** Start at [`CONTRIBUTING.md`](../../CONTRIBUTING.md), then skim [`docs/contributing/TECHNICAL.md`](TECHNICAL.md) to see how a command flows from `main.rs` → a `*_cmd.rs` filter → tracking → stdout.
|
||||
2. **Look at a good example.** [`src/cmds/git/git.rs`](../../src/cmds/git/git.rs) is a representative filter — it shows the `run()` entry point, `lazy_static!` regex setup, filter helpers, and embedded tests all in one file.
|
||||
3. **Know the shared helpers before reimplementing.** Two files cover most of what you need:
|
||||
- [`src/core/runner.rs`](../../src/core/runner.rs) — command execution wrappers: `run_filtered()` (run a command, then apply your filter function), `run_passthrough()` (run unfiltered but tracked), `run_streamed()` (streaming filter).
|
||||
- [`src/core/utils.rs`](../../src/core/utils.rs) — shared utilities: `resolved_command()`, `strip_ansi()`, `truncate()`, `count_tokens()`, and more.
|
||||
4. **Follow the checklist.** [`src/cmds/README.md — Adding a New Command Filter`](../../src/cmds/README.md#adding-a-new-command-filter) walks you through creating a filter, registering it, and adding tests.
|
||||
5. **Write the test first.** We follow Red-Green-Refactor. A snapshot test plus a token-savings assertion (see [Testing](#testing) below) is enough for most filters.
|
||||
|
||||
If you're unsure whether your approach fits, open a draft PR or a discussion early — we'd rather help shape the design than ask for a rewrite at review.
|
||||
|
||||
---
|
||||
|
||||
## Design Philosophy
|
||||
|
||||
For the full framing (Correctness vs. Token Savings, Transparency, Never Block, Zero Overhead, Extensibility), see the [Design Philosophy](../../CONTRIBUTING.md#design-philosophy) section in `CONTRIBUTING.md`.
|
||||
|
||||
Two practical reminders that come up often in review:
|
||||
|
||||
**Portability.** RTK should behave the same across platforms. Use `#[cfg(target_os = "...")]` for platform-specific code; never assume a single OS.
|
||||
|
||||
**Extensibility.** RTK should be modular. Before writing a new feature or filter, check whether an existing entry point fits — `runner::run_filtered()`, `runner::run_passthrough()`, helpers in `src/core/utils.rs`, etc. If your logic could be reused elsewhere, lift it into a shared component rather than burying it in one `*_cmd.rs` file.
|
||||
|
||||
---
|
||||
|
||||
## Files, Functions, and Documentation
|
||||
|
||||
Each folder contains a root `README.md` that explains the main principles, flows, and specificities of the source files it owns. These READMEs should describe concepts and cases — not list individual source files or counts, to avoid stale lists as the code evolves. Because the root README reflects core features and logic, it should not change often; meaningful edits usually imply a core refactor.
|
||||
|
||||
Tests live in the same file as the code they test (inside `#[cfg(test)] mod tests { ... }`), not in a separate test file. This keeps the filter, its fixtures, and its assertions close together.
|
||||
|
||||
---
|
||||
|
||||
## Edge Cases
|
||||
|
||||
When you add an edge-case branch or a non-obvious exception, leave a short comment above it explaining *why* it exists. This prevents a future contributor from removing it because the reason isn't visible from the code alone.
|
||||
|
||||
Referencing an issue is often the clearest form:
|
||||
|
||||
```rust
|
||||
// ISSUE #463: some `git log` output contains NUL bytes when --format=%x00 is used;
|
||||
// skip the line rather than panicking on invalid UTF-8.
|
||||
if line.contains('\0') {
|
||||
continue;
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Comments
|
||||
|
||||
Prefer code that reads clearly over code that needs comments to explain it. In particular, avoid redundant comments that restate what the function signature already says.
|
||||
|
||||
Comments are welcome when they add information the code cannot carry on its own. The common cases:
|
||||
|
||||
- **File header (`//!`)** — purpose and scope of the current file.
|
||||
- **Edge case** — a non-obvious branch or exception, as described above.
|
||||
- **Issue reference** — e.g. `// ISSUE #463: the fix for this`.
|
||||
- **"Why, not what"** — when the intent or tradeoff behind a decision isn't obvious from the code.
|
||||
|
||||
In short: avoid noise comments; keep the ones that would save a future reader a trip to `git blame`.
|
||||
|
||||
---
|
||||
|
||||
## Variables
|
||||
|
||||
Use explicit, descriptive names for variables, just like for functions.
|
||||
|
||||
Do not hardcode repetitive patterns or values that control behavior — extract them into named constants at the top of the file. For anything a user might want to tune (thresholds, limits, display cutoffs), use `config::limits()` so it flows through `~/.config/rtk/config.toml`.
|
||||
|
||||
Example from `src/cmds/git/git.rs`:
|
||||
|
||||
```rust
|
||||
let limits = config::limits();
|
||||
let max_files = limits.status_max_files;
|
||||
let max_untracked = limits.status_max_untracked;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Function and File Size
|
||||
|
||||
**Prefer functions under ~60 lines.** Shorter functions are easier to read, test, and reuse. If a function grows beyond that, it's usually a sign the logic should be split into helpers — but this is a guideline, not a hard cap.
|
||||
|
||||
Legitimate exceptions include:
|
||||
- Dispatcher / match functions that route to subcommands, where each arm delegates to a focused helper.
|
||||
- State-machine parsers where splitting would harm readability.
|
||||
|
||||
When you keep a longer function, aim to make each block obviously cohesive — and consider leaving a short comment on *why* splitting it would hurt.
|
||||
|
||||
**Files are expected to be large** in RTK because each module keeps its tests and fixtures alongside the implementation. When a file becomes hard to navigate, split responsibilities across multiple files where possible. If it isn't possible, a big file is acceptable for now.
|
||||
|
||||
---
|
||||
|
||||
## Imports and Dependencies
|
||||
|
||||
RTK is a low-dependency project. Before adding a crate, check whether the functionality is already covered by `std`, an existing dependency, or `src/core/utils.rs`. If a few lines of straightforward code will do the job, prefer that over a new dependency.
|
||||
|
||||
When a new dependency is genuinely needed, justify it in the PR description. For non-trivial additions, it's worth opening a discussion with maintainers first.
|
||||
|
||||
---
|
||||
|
||||
## Error Handling
|
||||
|
||||
Use `anyhow::Result` everywhere, and always attach context with `.context("description")?` or `.with_context(|| format!(...))`.
|
||||
|
||||
Never silently swallow errors (`Err(_) => {}`). Either log with `eprintln!` and fall back to raw output (the common case for filters), or propagate the error.
|
||||
|
||||
Example of the standard fallback pattern for a filter:
|
||||
|
||||
```rust
|
||||
let filtered = filter_output(&output.stdout)
|
||||
.unwrap_or_else(|e| {
|
||||
eprintln!("rtk: filter warning: {}", e);
|
||||
output.stdout.clone() // passthrough on failure — never block the user
|
||||
});
|
||||
```
|
||||
|
||||
For the full error-handling architecture (propagation chain, exit code preservation), see [ARCHITECTURE.md — Error Handling](ARCHITECTURE.md#error-handling).
|
||||
|
||||
---
|
||||
|
||||
## Testing
|
||||
|
||||
See [`CONTRIBUTING.md` — Testing](../../CONTRIBUTING.md#testing) for the full strategy. In short, for a new filter you typically want:
|
||||
|
||||
- **Unit + snapshot tests** in the same file, using the `insta` crate.
|
||||
- **A token-savings assertion** verifying the filter hits the ≥60% target on a real fixture.
|
||||
|
||||
Minimal example:
|
||||
|
||||
```rust
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use insta::assert_snapshot;
|
||||
|
||||
fn count_tokens(s: &str) -> usize { s.split_whitespace().count() }
|
||||
|
||||
#[test]
|
||||
fn filter_git_log_snapshot() {
|
||||
let input = include_str!("../../../tests/fixtures/git_log_raw.txt");
|
||||
let output = filter_git_log(input);
|
||||
assert_snapshot!(output);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn filter_git_log_savings() {
|
||||
let input = include_str!("../../../tests/fixtures/git_log_raw.txt");
|
||||
let output = filter_git_log(input);
|
||||
let savings = 100.0 - (count_tokens(&output) as f64 / count_tokens(input) as f64 * 100.0);
|
||||
assert!(savings >= 60.0, "expected ≥60% savings, got {:.1}%", savings);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Fixtures go in `tests/fixtures/` and should be captured from real command output rather than hand-written.
|
||||
|
||||
---
|
||||
|
||||
## Security
|
||||
|
||||
RTK executes shell commands on behalf of the user, so security is a first-class concern.
|
||||
|
||||
**Command execution.** All commands go through argument arrays via `Command::new().args()` — never through shell string concatenation. This prevents injection. Always use `resolved_command()` from `src/core/utils.rs` instead of a raw `Command::new()`.
|
||||
|
||||
**Hook integrity.** RTK verifies hook files via SHA-256 hashes before operational commands. If a hook has been tampered with, RTK exits with code 1. See [`src/hooks/integrity.rs`](../../src/hooks/integrity.rs).
|
||||
|
||||
**Project filter trust.** `.rtk/filters.toml` files are not loaded until the user explicitly trusts them, and content changes require re-trust. See [`src/hooks/trust.rs`](../../src/hooks/trust.rs).
|
||||
|
||||
**Permission whitelist.** `is_operational_command()` in `main.rs` uses a whitelist pattern — new commands are *not* integrity-checked until explicitly added. This is an intentional security posture: fail-open with an audit trail is preferred over false confidence.
|
||||
|
||||
**`unsafe` code.** Not allowed except for Unix signal handling in proxy mode, which is correctly scoped to `#[cfg(unix)]`.
|
||||
@@ -0,0 +1,432 @@
|
||||
# RTK Technical Documentation
|
||||
|
||||
> **Start here** for a guided tour of how RTK works end-to-end.
|
||||
>
|
||||
> - [CONTRIBUTING.md](../CONTRIBUTING.md) — Design philosophy, PR process, branch naming, testing requirements
|
||||
> - [ARCHITECTURE.md](ARCHITECTURE.md) — Deep reference: filtering taxonomy, performance benchmarks, architecture decisions
|
||||
> - Each folder has its own `README.md` with implementation details and file descriptions
|
||||
|
||||
---
|
||||
|
||||
## 1. Project Vision
|
||||
|
||||
LLM-powered coding agents (Claude Code, Copilot, Cursor, etc.) consume tokens for every CLI command output they process. Most command outputs contain boilerplate, progress bars, ANSI escape codes, and verbose formatting that wastes tokens without providing actionable information.
|
||||
|
||||
RTK sits between the agent and the CLI, filtering outputs to keep only what matters. This achieves 60-90% token savings per command, reducing costs and increasing effective context window utilization. RTK is a single Rust binary with no runtime dependencies beyond the compiled binary itself, adding less than 10ms overhead per command.
|
||||
|
||||
---
|
||||
|
||||
## 2. Architecture Overview
|
||||
|
||||
```
|
||||
User / LLM Agent
|
||||
|
|
||||
v
|
||||
+--------------------------------------------------+
|
||||
| LLM Agent Hook |
|
||||
| hooks/{claude,copilot,cursor,...}/ |
|
||||
| Intercepts: "git status" -> "rtk git status" |
|
||||
+-------------------------+------------------------+
|
||||
|
|
||||
v
|
||||
+--------------------------------------------------+
|
||||
| RTK CLI (main.rs) |
|
||||
| |
|
||||
| +-------------+ +-----------------+ |
|
||||
| | Clap Parser | -> | Command Routing | |
|
||||
| | (Commands | | (match on enum) | |
|
||||
| | enum) | +--------+--------+ |
|
||||
| +-------------+ | |
|
||||
| +---------+---------+ |
|
||||
| v v v |
|
||||
| +----------+ +--------+ +----------+|
|
||||
| |Rust Filter| |TOML DSL| |Passthru ||
|
||||
| |(cmds/**) | |Filter | |(fallback)||
|
||||
| +-----+----+ +----+---+ +----+-----+|
|
||||
| | | | |
|
||||
| +-----+-----+-----------+ |
|
||||
| v |
|
||||
| +---------------------+ |
|
||||
| | Token Tracking | |
|
||||
| | (core/tracking) | |
|
||||
| | SQLite DB | |
|
||||
| +---------------------+ |
|
||||
+--------------------------------------------------+
|
||||
```
|
||||
|
||||
**Design principles:**
|
||||
- Single-threaded, no async (startup < 10ms)
|
||||
- Graceful degradation: filter failure falls back to raw output
|
||||
- Exit code propagation: RTK never swallows non-zero exits
|
||||
- Transparent proxy: unknown commands pass through unchanged
|
||||
|
||||
---
|
||||
|
||||
## 3. End-to-End Flow
|
||||
|
||||
This is the full lifecycle of a command through RTK, from LLM agent to filtered output.
|
||||
|
||||
### 3.1 Hook Installation (`rtk init`)
|
||||
|
||||
The user runs `rtk init` to set up hooks for their LLM agent. This:
|
||||
|
||||
1. Writes a thin shell hook script (e.g., `~/.claude/hooks/rtk-rewrite.sh`)
|
||||
2. Stores its SHA-256 hash for integrity verification
|
||||
3. Patches the agent's settings file (e.g., `settings.json`) to register the hook
|
||||
4. Writes RTK awareness instructions (e.g., `RTK.md`) for prompt-level guidance
|
||||
|
||||
RTK supports 7 agents, each with its own installation mode. The hook scripts are embedded in the binary and written at install time.
|
||||
|
||||
> **Details**: [`src/hooks/README.md`](../src/hooks/README.md) covers all installation modes, configuration files, and the uninstall flow.
|
||||
|
||||
### 3.2 Hook Interception (Command Rewriting)
|
||||
|
||||
When an LLM agent runs a command (e.g., `git status`):
|
||||
|
||||
1. The agent fires a `PreToolUse` event (or equivalent) containing the command as JSON
|
||||
2. The hook script reads the JSON, extracts the command string
|
||||
3. The hook calls `rtk rewrite "git status"` as a subprocess
|
||||
4. `rtk rewrite` consults the command registry and returns `rtk git status`
|
||||
5. The hook sends a response telling the agent to use the rewritten command
|
||||
6. If anything fails (jq missing, rtk not found, no match), the hook exits silently -- the raw command runs unchanged
|
||||
|
||||
All rewrite logic lives in Rust (`src/discover/registry.rs`). Hooks are thin delegates that handle agent-specific JSON formats.
|
||||
|
||||
> **Details**: [`hooks/README.md`](../hooks/README.md) covers each agent's JSON format, the rewrite registry, compound command handling, and the `RTK_DISABLED` override.
|
||||
|
||||
#### Rewrite Pipeline
|
||||
|
||||
The rewrite pipeline is how RTK intercepts and rewrites commands. The call chain is:
|
||||
|
||||
```
|
||||
hook shell → rewrite_cmd.rs → rewrite_command() → rewrite_compound() → rewrite_segment() → classify_command()
|
||||
```
|
||||
|
||||
Traced step by step for `cargo fmt --all && cargo test 2>&1 | tail -20`:
|
||||
|
||||
```
|
||||
LLM Agent: "cargo fmt --all && cargo test 2>&1 | tail -20"
|
||||
|
|
||||
| Hook shell (hooks/claude/rtk-rewrite.sh)
|
||||
| Reads JSON from agent, extracts command, calls `rtk rewrite "$CMD"`
|
||||
| On failure (jq missing, rtk missing, old version): exit 0 (passthrough)
|
||||
|
|
||||
v
|
||||
rewrite_cmd::run(cmd) [src/hooks/rewrite_cmd.rs]
|
||||
| 1. Load config → hooks.exclude_commands
|
||||
| 2. check_command(cmd) → Deny → exit(2)
|
||||
| 3. registry::rewrite_command(cmd, excluded)
|
||||
| → None → exit(1) (no RTK equivalent, passthrough)
|
||||
| → Some + Allow → print, exit(0)
|
||||
| → Some + Ask → print, exit(3)
|
||||
|
|
||||
v
|
||||
rewrite_command(cmd, excluded) [src/discover/registry.rs]
|
||||
| Early exits:
|
||||
| - Empty → None
|
||||
| - Contains "<<" or "$((" (heredoc/arithmetic) → None
|
||||
| - Simple "rtk ..." (no operators) → return as-is
|
||||
| - Otherwise → rewrite_compound(cmd, excluded)
|
||||
|
|
||||
v
|
||||
rewrite_compound(cmd, excluded) [src/discover/registry.rs]
|
||||
|
|
||||
| Step 1 — Tokenize (lexer.rs)
|
||||
| tokenize() produces typed tokens with byte offsets:
|
||||
| Arg("cargo") Arg("fmt") Arg("--all")
|
||||
| Operator("&&")
|
||||
| Arg("cargo") Arg("test") Redirect("2>&1")
|
||||
| Pipe("|")
|
||||
| Arg("tail") Arg("-20")
|
||||
|
|
||||
| Step 2 — Split on operators, rewrite each segment
|
||||
| Operator (&&, ||, ;) → rewrite both sides
|
||||
| Pipe (|) → rewrite left side only, keep right side raw
|
||||
| exception: find/fd before pipe → skip rewrite
|
||||
| Shellism (&) → rewrite both sides (background)
|
||||
|
|
||||
| Calls rewrite_segment() per segment:
|
||||
| segment 1: "cargo fmt --all"
|
||||
| segment 2: "cargo test 2>&1"
|
||||
| after pipe: "tail -20" kept raw
|
||||
|
|
||||
v
|
||||
rewrite_segment(seg, excluded) [src/discover/registry.rs]
|
||||
|
|
||||
| Step 3 — Strip trailing redirects
|
||||
| strip_trailing_redirects() re-tokenizes the segment:
|
||||
| "cargo test 2>&1" → cmd_part="cargo test", redirect=" 2>&1"
|
||||
| (simple commands like "cargo fmt --all" → no redirect, suffix is "")
|
||||
|
|
||||
| Step 4 — Already RTK → return as-is
|
||||
|
|
||||
| Step 5 — Special cases (short-circuit before classification)
|
||||
| head -N / --lines=N → rewrite_line_range() → "rtk read file --max-lines N"
|
||||
| tail -N / -n N / --lines N → rewrite_line_range() → "rtk read file --tail-lines N"
|
||||
| head/tail with unsupported flag (-c, -f) → None (skip rewrite)
|
||||
| cat with incompatible flag (-A, -v, -e) → None (skip rewrite)
|
||||
|
|
||||
| Step 6 — classify_command(cmd_part) [see below]
|
||||
| → Supported → check excluded list → continue
|
||||
| → Unsupported/Ignored → None (skip rewrite)
|
||||
|
|
||||
| Step 7 — Build rewritten command
|
||||
| a. Find matching rule from rules.rs
|
||||
| b. Extract env prefix (ENV_PREFIX regex, second pass — first was in classify)
|
||||
| e.g. "GIT_SSH_COMMAND=\"ssh -o ...\" git push" → prefix="GIT_SSH_COMMAND=..."
|
||||
| c. Guard: RTK_DISABLED=1 in prefix → None
|
||||
| d. Guard: gh with --json/--jq/--template → None
|
||||
| e. Apply rule's rewrite_prefixes: "cargo fmt" → "rtk cargo fmt"
|
||||
| f. Reassemble: env_prefix + rtk_cmd + args + redirect_suffix
|
||||
|
|
||||
v
|
||||
classify_command(cmd) [src/discover/registry.rs]
|
||||
| 1. Check IGNORED_EXACT (cd, echo, fi, done, ...)
|
||||
| 2. Check IGNORED_PREFIXES (rtk, mkdir, mv, ...)
|
||||
| 3. Strip env prefix with ENV_PREFIX regex (for pattern matching only)
|
||||
| 4. Normalize absolute paths: /usr/bin/grep → grep
|
||||
| 5. Strip git global opts: git -C /tmp status → git status
|
||||
| 6. Guard: cat/head/tail with redirect (>, >>) → Unsupported (write, not read)
|
||||
| 7. Match against REGEX_SET (60+ compiled patterns from rules.rs)
|
||||
| 8. Extract subcommand → lookup custom savings/status overrides
|
||||
| 9. Return Classification::Supported { rtk_equivalent, category, savings, status }
|
||||
|
|
||||
v
|
||||
Result: "rtk cargo fmt --all && rtk cargo test 2>&1 | tail -20"
|
||||
|
|
||||
| Hook response
|
||||
| Hook wraps result in agent-specific JSON, returns to LLM agent
|
||||
|
|
||||
v
|
||||
LLM Agent executes rewritten command
|
||||
(bash handles && and |, each rtk invocation is a separate process)
|
||||
```
|
||||
|
||||
Key design decisions:
|
||||
- **Lexer-based tokenization**: A single-pass state machine (`lexer.rs`) handles all shell constructs (quotes, escapes, redirects, operators). Used for both compound splitting and redirect stripping.
|
||||
- **Segment-level rewriting**: Compound commands are split by operators, each segment rewritten independently. Bash recombines them at execution time.
|
||||
- **Pipe semantics**: Only the left side of `|` is rewritten. The pipe consumer (grep, head, wc) runs raw. `find`/`fd` before a pipe is never rewritten (output format incompatible with xargs).
|
||||
- **Double env prefix handling**: `classify_command()` strips env prefixes to match the underlying command against rules. `rewrite_segment()` extracts the same prefix separately to re-prepend it to the rewritten command.
|
||||
- **Fallback contract**: If any segment fails to match, it stays raw. `rewrite_command()` returns `None` only when zero segments were rewritten.
|
||||
|
||||
### 3.3 CLI Parsing and Routing
|
||||
|
||||
Once the rewritten command reaches RTK:
|
||||
|
||||
1. **Telemetry**: `telemetry::maybe_ping()` fires a non-blocking daily usage ping
|
||||
2. **Clap parsing**: `Cli::try_parse()` matches against the `Commands` enum
|
||||
3. **Hook check**: `hook_check::maybe_warn()` warns if the installed hook is outdated (rate-limited to 1/day)
|
||||
4. **Integrity check**: `integrity::runtime_check()` verifies the hook's SHA-256 hash for operational commands
|
||||
5. **Routing**: A `match cli.command` dispatches to the specialized filter module
|
||||
|
||||
If Clap parsing fails (command not in the enum), the fallback path runs instead.
|
||||
|
||||
### 3.4 Filter Execution
|
||||
|
||||
RTK has two filter systems:
|
||||
|
||||
**Rust Filters**: Compiled modules in `src/cmds/` that execute the command, parse its output, and apply specialized transformations (regex, JSON, state machines).
|
||||
|
||||
**TOML DSL Filters**: Declarative filters in `src/filters/*.toml` that apply regex-based line filtering, truncation, and section extraction. Applied in `run_fallback()` when no Rust filter matches.
|
||||
|
||||
Each filter module follows the same pattern:
|
||||
1. Start a timer (`TimedExecution::start()`)
|
||||
2. Execute the underlying command (`std::process::Command`)
|
||||
3. Apply filtering (strip boilerplate, group errors, truncate)
|
||||
4. On filter error, fall back to raw output
|
||||
5. Track token savings to SQLite
|
||||
6. Propagate exit code
|
||||
|
||||
> **Details**: [`src/cmds/README.md`](../src/cmds/README.md) covers the common pattern, ecosystem organization, cross-command dependencies, and how to add new filters.
|
||||
|
||||
### 3.5 Fallback Path
|
||||
|
||||
When Clap parsing fails (unknown command):
|
||||
|
||||
1. Guard: check if the command is an RTK meta-command (`gain`, `init`, etc.) -- if so, show Clap error
|
||||
2. Look up TOML DSL filters via `toml_filter::find_matching_filter()`
|
||||
3. If TOML match: capture stdout, apply filter pipeline, track savings
|
||||
4. If no match: pure passthrough with `Stdio::inherit`, track as 0% savings
|
||||
|
||||
```
|
||||
Command received
|
||||
-> Clap parse succeeds?
|
||||
-> Yes: Route to Rust filter module
|
||||
-> No: run_fallback()
|
||||
-> TOML filter match?
|
||||
-> Yes: Capture stdout, apply filter, track savings
|
||||
-> No: Passthrough (inherit stdio, track 0% savings)
|
||||
```
|
||||
|
||||
> **Details**: [`src/core/README.md`](../src/core/README.md) covers the TOML filter engine, filter pipeline stages, and trust-gated project filters.
|
||||
|
||||
### 3.6 Token Tracking
|
||||
|
||||
Every command execution records metrics to SQLite (`~/.local/share/rtk/tracking.db`):
|
||||
|
||||
- Input tokens (raw output size) and output tokens (filtered size)
|
||||
- Savings percentage, execution time, project path
|
||||
- 90-day automatic retention cleanup
|
||||
- Token estimation: `ceil(chars / 4.0)` approximation
|
||||
|
||||
Analytics commands (`rtk gain`, `rtk cc-economics`, `rtk session`) query this database to produce dashboards and ROI reports.
|
||||
|
||||
> **Details**: [`src/analytics/README.md`](../src/analytics/README.md) covers the analytics modules, and [`src/core/README.md`](../src/core/README.md) covers the tracking database schema.
|
||||
|
||||
### 3.7 Tee Recovery
|
||||
|
||||
On command failure (non-zero exit code):
|
||||
|
||||
1. Raw unfiltered output is saved to `~/.local/share/rtk/tee/{epoch}_{slug}.log`
|
||||
2. A hint line is printed: `[full output: ~/.../tee/1234_cargo_test.log]`
|
||||
3. LLM agents can re-read the file instead of re-running the failed command
|
||||
|
||||
Tee is configurable (enabled/disabled, min size, max files, max file size) and never affects command output or exit code on failure.
|
||||
|
||||
> **Details**: [`src/core/README.md`](../src/core/README.md) covers tee configuration and the rotation strategy.
|
||||
|
||||
---
|
||||
|
||||
## 4. Folder Map
|
||||
|
||||
Start here, then drill down into each README for file-level details.
|
||||
|
||||
### `src/` — Rust source code
|
||||
|
||||
| Directory | What it does | What you'll find in its README |
|
||||
|-----------|-------------|-------------------------------|
|
||||
| `main.rs` | CLI entry point, `Commands` enum, routing match | _(no README — read the file directly)_ |
|
||||
| [`core/`](../src/core/README.md) | Shared infrastructure | Tracking DB schema, config system, tee recovery, TOML filter engine, utility functions |
|
||||
| [`hooks/`](../src/hooks/README.md) | Hook system | Installation flow (`rtk init`), integrity verification, rewrite command, trust model |
|
||||
| [`analytics/`](../src/analytics/README.md) | Token savings analytics | `rtk gain` dashboard, Claude Code economics, ccusage parsing |
|
||||
| [`cmds/`](../src/cmds/README.md) | **Command filters (9 ecosystems)** | Common filter pattern, cross-command routing, token savings table, **links to each ecosystem** |
|
||||
| [`discover/`](../src/discover/README.md) | History analysis + rewrite registry | Rewrite patterns, session providers, compound command splitting |
|
||||
| [`learn/`](../src/learn/README.md) | CLI correction detection | Error classification, correction pair detection, rule generation |
|
||||
| [`parser/`](../src/parser/README.md) | Parser infrastructure | Canonical types (TestResult, LintResult, etc.), 3-tier format modes, migration guide |
|
||||
| [`filters/`](../src/filters/README.md) | TOML filter configs | TOML DSL syntax, 8-stage pipeline, inline testing, naming conventions |
|
||||
|
||||
### `hooks/` — Deployed hook artifacts (root directory)
|
||||
|
||||
| Directory | Agent | What you'll find in its README |
|
||||
|-----------|-------|-------------------------------|
|
||||
| [`hooks/`](../hooks/README.md) | _(parent)_ | **All JSON formats**, rewrite registry overview, exit code contract, override controls |
|
||||
| [`claude/`](../hooks/claude/README.md) | Claude Code | Shell hook mechanism, `PreToolUse` JSON, test script |
|
||||
| [`copilot/`](../hooks/copilot/README.md) | GitHub Copilot | Rust binary hook, VS Code Chat vs Copilot CLI dual format |
|
||||
| [`cursor/`](../hooks/cursor/README.md) | Cursor IDE | Shell hook, empty JSON response requirement |
|
||||
| [`cline/`](../hooks/cline/README.md) | Cline / Roo Code | Rules file (prompt-level, no programmatic hook) |
|
||||
| [`windsurf/`](../hooks/windsurf/README.md) | Windsurf / Cascade | Rules file (workspace-scoped) |
|
||||
| [`codex/`](../hooks/codex/README.md) | OpenAI Codex CLI | Awareness document, AGENTS.md integration |
|
||||
| [`opencode/`](../hooks/opencode/README.md) | OpenCode | TypeScript plugin, zx library, in-place mutation |
|
||||
|
||||
---
|
||||
|
||||
## 5. Hook System Summary
|
||||
|
||||
RTK supports the following LLM agents through hook integrations:
|
||||
|
||||
| Agent | Hook Type | Mechanism | Can Modify Command? |
|
||||
|-------|-----------|-----------|---------------------|
|
||||
| Claude Code | Shell hook | `PreToolUse` in `settings.json` | Yes (`updatedInput`) |
|
||||
| GitHub Copilot (VS Code) | Rust binary | `rtk hook copilot` reads JSON | Yes (`updatedInput`) |
|
||||
| GitHub Copilot CLI | Rust binary | `rtk hook copilot` reads JSON | No (deny + suggestion) |
|
||||
| Cursor | Rust binary | `rtk hook cursor` reads JSON | Yes (`updated_input`) |
|
||||
| Gemini CLI | Rust binary | `rtk hook gemini` reads JSON | Yes (`hookSpecificOutput`) |
|
||||
| Cline/Roo Code | Rules file | Prompt-level guidance | N/A (prompt) |
|
||||
| Windsurf | Rules file | Prompt-level guidance | N/A (prompt) |
|
||||
| Codex CLI | Awareness doc | AGENTS.md integration | N/A (prompt) |
|
||||
| OpenCode | TS plugin | `tool.execute.before` event | Yes (in-place mutation) |
|
||||
|
||||
> **Details**: [`hooks/README.md`](../hooks/README.md) has the full JSON schemas for each agent. [`src/hooks/README.md`](../src/hooks/README.md) covers installation, integrity verification, and the rewrite command.
|
||||
|
||||
---
|
||||
|
||||
## 6. Filter Pipeline Summary
|
||||
|
||||
### Rust Filters (cmds/**)
|
||||
|
||||
Compiled filter modules for complex transformations with 60-95% token savings.
|
||||
|
||||
> **Details**: [`src/cmds/README.md`](../src/cmds/README.md) and each ecosystem subdirectory README.
|
||||
|
||||
### TOML DSL Filters (src/filters/*.toml)
|
||||
|
||||
Declarative filters with an 8-stage pipeline: strip ANSI, regex replace, match output, strip/keep lines, truncate lines, head/tail, max lines, on-empty message. Loaded from three tiers: built-in (compiled), global (`~/.config/rtk/filters/`), project-local (`.rtk/filters/`, trust-gated).
|
||||
|
||||
> **Details**: [`src/core/README.md`](../src/core/README.md) covers the TOML filter engine.
|
||||
|
||||
---
|
||||
|
||||
## 7. Performance Constraints
|
||||
|
||||
| Metric | Target | Verification |
|
||||
|--------|--------|--------------|
|
||||
| Startup time | < 10ms | `hyperfine 'rtk git status' 'git status'` |
|
||||
| Memory usage | < 5MB resident | `/usr/bin/time -v rtk git status` |
|
||||
| Binary size | < 5MB stripped | `ls -lh target/release/rtk` |
|
||||
| Token savings | 60-90% per filter | Snapshot + token count tests |
|
||||
|
||||
Achieved through:
|
||||
- Zero async overhead (single-threaded, no tokio)
|
||||
- Lazy regex compilation (`lazy_static!`)
|
||||
- Minimal allocations (borrow over clone)
|
||||
- No config file I/O on startup (loaded on-demand)
|
||||
|
||||
---
|
||||
|
||||
## 8. Testing
|
||||
|
||||
Tests live **in the module file itself** inside a `#[cfg(test)] mod tests` block (e.g., tests for `src/cmds/cloud/container.rs` go at the bottom of that same file).
|
||||
|
||||
### How to Write Tests
|
||||
|
||||
**1. Create a fixture from real command output** (not synthetic data):
|
||||
```bash
|
||||
kubectl get pods > tests/fixtures/kubectl_pods_raw.txt
|
||||
```
|
||||
|
||||
**2. Write your test in the same module file** (`#[cfg(test)] mod tests`):
|
||||
```rust
|
||||
#[test]
|
||||
fn test_my_filter() {
|
||||
let input = include_str!("../tests/fixtures/my_cmd_raw.txt");
|
||||
let output = filter_my_cmd(input);
|
||||
assert!(output.contains("expected content"));
|
||||
assert!(!output.contains("noise line"));
|
||||
}
|
||||
```
|
||||
|
||||
**3. Verify token savings** (60% minimum required):
|
||||
```rust
|
||||
#[test]
|
||||
fn test_my_filter_savings() {
|
||||
let input = include_str!("../tests/fixtures/my_cmd_raw.txt");
|
||||
let output = filter_my_cmd(input);
|
||||
let savings = 100.0 - (count_tokens(&output) as f64 / count_tokens(input) as f64 * 100.0);
|
||||
assert!(savings >= 60.0, "Expected >=60% savings, got {:.1}%", savings);
|
||||
}
|
||||
```
|
||||
|
||||
### Test Organization
|
||||
|
||||
```
|
||||
tests/
|
||||
├── fixtures/ # Real command output (never synthetic)
|
||||
│ ├── git_log_raw.txt
|
||||
│ ├── cargo_test_raw.txt
|
||||
│ └── dotnet/ # Ecosystem-specific fixtures
|
||||
└── integration_test.rs # Integration tests (#[ignore])
|
||||
```
|
||||
|
||||
- **Unit tests**: `#[cfg(test)] mod tests` embedded in each module
|
||||
- **Fixtures**: real command output in `tests/fixtures/`
|
||||
- **Integration tests**: `#[ignore]` attribute, run with `cargo test --ignored`
|
||||
|
||||
> For testing requirements, pre-commit gate, and PR checklist, see [CONTRIBUTING.md — Testing](../CONTRIBUTING.md#testing).
|
||||
|
||||
---
|
||||
|
||||
## 9. Future Improvements
|
||||
|
||||
- **Extract cli.rs**: Move `Commands` enum, 13 sub-enums (`GitCommands`, `CargoCommands`, etc.), and `AgentTarget` from main.rs to a dedicated cli.rs module. This would reduce main.rs from ~2600 to ~1500 lines.
|
||||
- **Split routing**: Extract the `match cli.command { ... }` block into a separate routing module.
|
||||
- **Streaming filters**: For long-running commands, filter output line-by-line as it arrives instead of buffering.
|
||||
@@ -0,0 +1,58 @@
|
||||
---
|
||||
title: Discover and Session
|
||||
description: Find missed savings opportunities with rtk discover, and track RTK adoption with rtk session
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
# Discover and Session
|
||||
|
||||
## rtk discover — find missed savings
|
||||
|
||||
`rtk discover` analyzes your Claude Code command history to identify commands that ran without RTK filtering and calculates how many tokens you lost.
|
||||
|
||||
```bash
|
||||
rtk discover # analyze current project history
|
||||
rtk discover --all # all projects
|
||||
rtk discover --all --since 7 # last 7 days, all projects
|
||||
```
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
Missed savings analysis (last 7 days)
|
||||
────────────────────────────────────
|
||||
Command Count Est. lost
|
||||
cargo test 12 ~48,000 tokens
|
||||
git log 8 ~12,000 tokens
|
||||
pnpm list 3 ~6,000 tokens
|
||||
────────────────────────────────────
|
||||
Total missed: 23 ~66,000 tokens
|
||||
|
||||
Run `rtk init --global` to capture these automatically.
|
||||
```
|
||||
|
||||
If commands appear in the missed list after installing RTK, it usually means the hook isn't active for that agent. See [Troubleshooting](../resources/troubleshooting.md) — "Agent not using RTK".
|
||||
|
||||
## rtk session — adoption tracking
|
||||
|
||||
`rtk session` shows RTK adoption across recent Claude Code sessions: how many shell commands ran through RTK vs. raw.
|
||||
|
||||
```bash
|
||||
rtk session
|
||||
```
|
||||
|
||||
**Example output:**
|
||||
|
||||
```
|
||||
Recent sessions (last 10)
|
||||
─────────────────────────────────────────────────────
|
||||
Session Total RTK Coverage
|
||||
2026-04-06 14:32 (45 cmds) 45 43 95.6%
|
||||
2026-04-05 09:14 (38 cmds) 38 38 100.0%
|
||||
2026-04-04 16:50 (52 cmds) 52 49 94.2%
|
||||
─────────────────────────────────────────────────────
|
||||
Average coverage: 96.6%
|
||||
```
|
||||
|
||||
Low coverage on a session usually means RTK was disabled (`RTK_DISABLED=1`) or the hook wasn't active for a specific subagent.
|
||||
@@ -0,0 +1,215 @@
|
||||
---
|
||||
title: Token Savings Analytics
|
||||
description: Measure and analyze your RTK token savings with rtk gain
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
# Token Savings Analytics
|
||||
|
||||
`rtk gain` shows how many tokens RTK has saved across all your commands, with daily, weekly, and monthly breakdowns.
|
||||
|
||||
## Quick reference
|
||||
|
||||
```bash
|
||||
# Default summary
|
||||
rtk gain
|
||||
|
||||
# Temporal breakdowns
|
||||
rtk gain --daily # all days since tracking started
|
||||
rtk gain --weekly # aggregated by week
|
||||
rtk gain --monthly # aggregated by month
|
||||
rtk gain --all # all breakdowns at once
|
||||
|
||||
# Classic flags
|
||||
rtk gain --graph # ASCII graph, last 30 days
|
||||
rtk gain --history # last 10 commands
|
||||
rtk gain --quota # monthly quota savings estimate (default tier: 20x)
|
||||
rtk gain --quota -t pro # use pro tier token budget for estimate
|
||||
|
||||
# Export
|
||||
rtk gain --all --format json > savings.json
|
||||
rtk gain --all --format csv > savings.csv
|
||||
```
|
||||
|
||||
## Daily breakdown
|
||||
|
||||
```bash
|
||||
rtk gain --daily
|
||||
```
|
||||
|
||||
```
|
||||
📅 Daily Breakdown (3 days)
|
||||
════════════════════════════════════════════════════════════════
|
||||
Date Cmds Input Output Saved Save%
|
||||
────────────────────────────────────────────────────────────────
|
||||
2026-01-28 89 380.9K 26.7K 355.8K 93.4%
|
||||
2026-01-29 102 894.5K 32.4K 863.7K 96.6%
|
||||
2026-01-30 5 749 55 694 92.7%
|
||||
────────────────────────────────────────────────────────────────
|
||||
TOTAL 196 1.3M 59.2K 1.2M 95.6%
|
||||
```
|
||||
|
||||
- **Cmds**: RTK commands executed
|
||||
- **Input**: Estimated tokens from raw command output
|
||||
- **Output**: Actual tokens after filtering
|
||||
- **Saved**: Input - Output (tokens that never reached the LLM)
|
||||
- **Save%**: Saved / Input × 100
|
||||
|
||||
## Weekly and monthly breakdowns
|
||||
|
||||
```bash
|
||||
rtk gain --weekly
|
||||
rtk gain --monthly
|
||||
```
|
||||
|
||||
Same columns as daily, aggregated by Sunday-Saturday week or calendar month.
|
||||
|
||||
## Export formats
|
||||
|
||||
| Format | Flag | Use case |
|
||||
|--------|------|----------|
|
||||
| `text` | default | Terminal display |
|
||||
| `json` | `--format json` | Programmatic analysis, dashboards |
|
||||
| `csv` | `--format csv` | Excel, Python/R, Google Sheets |
|
||||
|
||||
**JSON structure:**
|
||||
```json
|
||||
{
|
||||
"summary": {
|
||||
"total_commands": 196,
|
||||
"total_input": 1276098,
|
||||
"total_output": 59244,
|
||||
"total_saved": 1220217,
|
||||
"avg_savings_pct": 95.62
|
||||
},
|
||||
"daily": [...],
|
||||
"weekly": [...],
|
||||
"monthly": [...]
|
||||
}
|
||||
```
|
||||
|
||||
## Typical savings by command
|
||||
|
||||
| Command | Typical savings | Mechanism |
|
||||
|---------|----------------|-----------|
|
||||
| `git status` | 77-93% | Compact stat format |
|
||||
| `eslint` | 84% | Group by rule |
|
||||
| `jest` | 94-99% | Show failures only |
|
||||
| `vitest` | 94-99% | Show failures only |
|
||||
| `find` | 75% | Tree format |
|
||||
| `pnpm list` | 70-90% | Compact dependencies |
|
||||
| `grep` | 70% | Truncate + group |
|
||||
|
||||
## How token estimation works
|
||||
|
||||
RTK estimates tokens using `text.len() / 4` (4 characters per token average). This is accurate to ±10% compared to actual LLM tokenization — sufficient for trend analysis.
|
||||
|
||||
```
|
||||
Input Tokens = estimate_tokens(raw_command_output)
|
||||
Output Tokens = estimate_tokens(rtk_filtered_output)
|
||||
Saved Tokens = Input - Output
|
||||
Savings % = (Saved / Input) × 100
|
||||
```
|
||||
|
||||
## Database
|
||||
|
||||
Savings data is stored locally in SQLite:
|
||||
|
||||
- **Location**: `~/.local/share/rtk/history.db` (Linux / macOS)
|
||||
- **Retention**: 90 days (automatic cleanup)
|
||||
- **Scope**: Global across all projects and Claude sessions
|
||||
|
||||
```bash
|
||||
# Inspect raw data
|
||||
sqlite3 ~/.local/share/rtk/history.db \
|
||||
"SELECT timestamp, rtk_cmd, saved_tokens FROM commands
|
||||
ORDER BY timestamp DESC LIMIT 10"
|
||||
|
||||
# Backup
|
||||
cp ~/.local/share/rtk/history.db ~/backups/rtk-history-$(date +%Y%m%d).db
|
||||
|
||||
# Reset
|
||||
rm ~/.local/share/rtk/history.db # recreated on next command
|
||||
```
|
||||
|
||||
## Analysis workflows
|
||||
|
||||
```bash
|
||||
# Weekly progress: generate a CSV report every Monday
|
||||
rtk gain --weekly --format csv > reports/week-$(date +%Y-%W).csv
|
||||
|
||||
# Monthly budget review
|
||||
rtk gain --monthly --format json | jq '.monthly[] |
|
||||
{month, saved_tokens, quota_pct: (.saved_tokens / 6000000 * 100)}'
|
||||
|
||||
# Cron: daily JSON snapshot for a dashboard
|
||||
0 0 * * * rtk gain --all --format json > /var/www/dashboard/rtk-stats.json
|
||||
```
|
||||
|
||||
**Python/pandas:**
|
||||
```python
|
||||
import pandas as pd
|
||||
import subprocess
|
||||
|
||||
result = subprocess.run(['rtk', 'gain', '--all', '--format', 'csv'],
|
||||
capture_output=True, text=True)
|
||||
lines = result.stdout.split('\n')
|
||||
daily_start = lines.index('# Daily Data') + 2
|
||||
daily_end = lines.index('', daily_start)
|
||||
daily_df = pd.read_csv(pd.StringIO('\n'.join(lines[daily_start:daily_end])))
|
||||
daily_df['date'] = pd.to_datetime(daily_df['date'])
|
||||
daily_df.plot(x='date', y='savings_pct', kind='line')
|
||||
```
|
||||
|
||||
**GitHub Actions (weekly stats):**
|
||||
```yaml
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1'
|
||||
jobs:
|
||||
stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- run: cargo install rtk
|
||||
- run: rtk gain --weekly --format json > stats/week-$(date +%Y-%W).json
|
||||
- run: git add stats/ && git commit -m "Weekly rtk stats" && git push
|
||||
```
|
||||
|
||||
## Quota estimate
|
||||
|
||||
`--quota` estimates how many tokens RTK has saved relative to your monthly subscription budget, so you can see the cost impact of those savings.
|
||||
|
||||
```bash
|
||||
rtk gain --quota # uses 20x tier by default
|
||||
rtk gain --quota -t pro # Claude Pro plan budget
|
||||
rtk gain --quota -t 5x # 5× usage plan budget
|
||||
rtk gain --quota -t 20x # 20× usage plan budget
|
||||
```
|
||||
|
||||
The tiers (`pro`, `5x`, `20x`) correspond to Anthropic Claude API subscription levels, each with a different monthly token allocation. RTK uses those allocations as a denominator to express your savings as a percentage of your budget.
|
||||
|
||||
:::tip[Find missed savings]
|
||||
`rtk gain` shows what RTK saved. To find commands that ran *without* RTK and calculate what you lost, see [rtk discover](./discover.md).
|
||||
:::
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**No data showing:**
|
||||
```bash
|
||||
ls -lh ~/.local/share/rtk/history.db
|
||||
sqlite3 ~/.local/share/rtk/history.db "SELECT COUNT(*) FROM commands"
|
||||
git status # run any tracked command to generate data
|
||||
```
|
||||
|
||||
**Incorrect statistics:** Token estimation is a heuristic. For precise counts, use `tiktoken`:
|
||||
```bash
|
||||
pip install tiktoken
|
||||
git status > output.txt
|
||||
python -c "
|
||||
import tiktoken
|
||||
enc = tiktoken.get_encoding('cl100k_base')
|
||||
print(len(enc.encode(open('output.txt').read())), 'actual tokens')
|
||||
"
|
||||
```
|
||||
@@ -0,0 +1,150 @@
|
||||
---
|
||||
title: Configuration
|
||||
description: Customize RTK behavior via config.toml, environment variables, and per-project filters
|
||||
sidebar:
|
||||
order: 4
|
||||
---
|
||||
|
||||
# Configuration
|
||||
|
||||
## Config file location
|
||||
|
||||
| Platform | Path |
|
||||
|----------|------|
|
||||
| Linux | `~/.config/rtk/config.toml` |
|
||||
| macOS | `~/Library/Application Support/rtk/config.toml` |
|
||||
|
||||
```bash
|
||||
rtk config # show current configuration
|
||||
rtk config --create # create config file with defaults
|
||||
```
|
||||
|
||||
## Full config structure
|
||||
|
||||
```toml
|
||||
[tracking]
|
||||
enabled = true # enable/disable token tracking
|
||||
history_days = 90 # retention in days (auto-cleanup)
|
||||
database_path = "/custom/path/history.db" # optional override
|
||||
|
||||
[display]
|
||||
colors = true # colored output
|
||||
emoji = true # use emojis in output
|
||||
max_width = 120 # maximum output width
|
||||
|
||||
[filters]
|
||||
# These apply to file-reading commands (ls, find, grep, cat/rtk read).
|
||||
# Paths matching these patterns are excluded from output, keeping noise low.
|
||||
ignore_dirs = [".git", "node_modules", "target", "__pycache__", ".venv", "vendor"]
|
||||
ignore_files = ["*.lock", "*.min.js", "*.min.css"]
|
||||
|
||||
[tee]
|
||||
enabled = true # save raw output on failure
|
||||
mode = "failures" # "failures" (default), "always", "never"
|
||||
max_files = 20 # rotation: keep last N files
|
||||
# directory = "/custom/tee/path" # optional override
|
||||
|
||||
[telemetry]
|
||||
enabled = true # anonymous daily ping — see Telemetry & Privacy for full details
|
||||
|
||||
[hooks]
|
||||
exclude_commands = [] # commands to never auto-rewrite
|
||||
```
|
||||
|
||||
For full details on what is collected, opt-out options, and GDPR rights, see [Telemetry & Privacy](../resources/telemetry.md).
|
||||
|
||||
## Environment variables
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `RTK_DISABLED=1` | Disable RTK for a single command (`RTK_DISABLED=1 git status`) |
|
||||
| `RTK_TEE_DIR` | Override the tee directory |
|
||||
| `RTK_TELEMETRY_DISABLED=1` | Disable telemetry |
|
||||
| `RTK_HOOK_AUDIT=1` | Enable hook audit logging |
|
||||
| `SKIP_ENV_VALIDATION=1` | Skip env validation (useful with Next.js) |
|
||||
|
||||
## Tee system
|
||||
|
||||
When a command fails, RTK saves the full raw output to a local file and prints the path:
|
||||
|
||||
```
|
||||
FAILED: 2/15 tests
|
||||
[full output: ~/.local/share/rtk/tee/1707753600_cargo_test.log]
|
||||
```
|
||||
|
||||
Your AI assistant can then read the file if it needs more detail, without re-running the command.
|
||||
|
||||
| Setting | Default | Description |
|
||||
|---------|---------|-------------|
|
||||
| `tee.enabled` | `true` | Enable/disable |
|
||||
| `tee.mode` | `"failures"` | `"failures"`, `"always"`, `"never"` |
|
||||
| `tee.max_files` | `20` | Rotation: keep last N files |
|
||||
| Min size | 500 bytes | Outputs shorter than this are not saved |
|
||||
| Max file size | 1 MB | Truncated above this |
|
||||
|
||||
## Excluding commands from auto-rewrite
|
||||
|
||||
Prevent specific commands from being rewritten by the hook:
|
||||
|
||||
```toml
|
||||
[hooks]
|
||||
exclude_commands = ["git rebase", "git cherry-pick", "docker exec"]
|
||||
```
|
||||
|
||||
Patterns match against the full command after stripping env prefixes (`sudo`, `VAR=val`), so `"psql"` excludes both `psql -h localhost` and `PGPASSWORD=x psql -h localhost`.
|
||||
|
||||
Subcommand patterns work too: `"git push"` excludes `git push origin main` but not `git status`.
|
||||
|
||||
Patterns starting with `^` are treated as regex:
|
||||
|
||||
```toml
|
||||
[hooks]
|
||||
exclude_commands = ["^curl", "^wget", "git rebase"]
|
||||
```
|
||||
|
||||
Invalid regex patterns fall back to prefix matching.
|
||||
|
||||
Or for a single invocation:
|
||||
|
||||
```bash
|
||||
RTK_DISABLED=1 git rebase main
|
||||
```
|
||||
|
||||
## Telemetry
|
||||
|
||||
RTK sends one anonymous ping per day (23h interval). No personal data, no file paths, no command content.
|
||||
|
||||
Data sent: device hash, version, OS, architecture, command count/24h, top commands, savings %.
|
||||
|
||||
To opt out:
|
||||
|
||||
```bash
|
||||
# Via environment variable
|
||||
export RTK_TELEMETRY_DISABLED=1
|
||||
|
||||
# Via config.toml
|
||||
[telemetry]
|
||||
enabled = false
|
||||
```
|
||||
|
||||
## Custom filters
|
||||
|
||||
Add your own filters (or override built-ins) in either location:
|
||||
|
||||
- **Project-local** — `.rtk/filters.toml` in your project root (committed with the repo)
|
||||
- **User-global** — `~/.config/rtk/filters.toml` (applies to every project)
|
||||
|
||||
See [`src/filters/README.md`](https://github.com/rtk-ai/rtk/blob/master/src/filters/README.md) for the full TOML DSL reference.
|
||||
|
||||
### Trusting custom filters
|
||||
|
||||
Because a filter can rewrite what your AI assistant sees, custom filter files are **not applied until you trust them**. An untrusted (or edited) filter file is skipped silently on the command path. You review and manage trust with explicit commands:
|
||||
|
||||
```bash
|
||||
rtk trust # shows each filter and asks to confirm (--yes to skip the prompt)
|
||||
rtk untrust # revokes trust
|
||||
```
|
||||
|
||||
`rtk init` also detects existing filters and lets you enable them — interactively, or non-interactively with `--trust-filters` / `--no-trust-filters`. Trust is tied to the file's contents (SHA-256), so editing a trusted file requires re-running `rtk trust`.
|
||||
|
||||
> **Upgrading:** earlier versions applied `~/.config/rtk/filters.toml` without trust. After upgrading, the user-global file is gated like project filters — if you already relied on a global filter, run `rtk trust` once to re-enable it.
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
title: Installation
|
||||
description: Install RTK via curl, Homebrew, Cargo, or from source, and verify the correct version
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
# Installation
|
||||
|
||||
## Name collision warning
|
||||
|
||||
Two unrelated projects share the name `rtk`. Make sure you install the right one:
|
||||
|
||||
- **Rust Token Killer** (`rtk-ai/rtk`) — this project, a token-saving CLI proxy
|
||||
- **Rust Type Kit** (`reachingforthejack/rtk`) — a different tool for generating Rust types
|
||||
|
||||
The easiest way to verify you have the correct one: run `rtk gain`. It should display token savings stats. If it returns "command not found", you either have the wrong package or RTK is not installed.
|
||||
|
||||
## Check before installing
|
||||
|
||||
```bash
|
||||
rtk --version # should print: rtk x.y.z
|
||||
rtk gain # should show token savings stats
|
||||
```
|
||||
|
||||
If both commands work, RTK is already installed. Skip to [Project initialization](#project-initialization).
|
||||
|
||||
## Quick install (Linux and macOS)
|
||||
|
||||
```bash
|
||||
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/master/install.sh | sh
|
||||
```
|
||||
|
||||
## Homebrew (macOS and Linux)
|
||||
|
||||
```bash
|
||||
brew install rtk-ai/tap/rtk
|
||||
```
|
||||
|
||||
## Cargo
|
||||
|
||||
:::caution[Name collision risk]
|
||||
`cargo install rtk` may install **Rust Type Kit** instead of Rust Token Killer — two unrelated projects share the same crate name. Use the explicit Git URL to guarantee the correct package:
|
||||
:::
|
||||
|
||||
```bash
|
||||
cargo install --git https://github.com/rtk-ai/rtk rtk
|
||||
```
|
||||
|
||||
## Pre-built binaries (Windows, Linux, macOS)
|
||||
|
||||
Download from [GitHub releases](https://github.com/rtk-ai/rtk/releases):
|
||||
|
||||
- macOS: `rtk-x86_64-apple-darwin.tar.gz` / `rtk-aarch64-apple-darwin.tar.gz`
|
||||
- Linux: `rtk-x86_64-unknown-linux-musl.tar.gz` / `rtk-aarch64-unknown-linux-gnu.tar.gz`
|
||||
- Windows: `rtk-x86_64-pc-windows-msvc.zip`
|
||||
|
||||
**Windows users**: Extract the zip and place `rtk.exe` in a directory on your PATH. Run RTK from Command Prompt, PowerShell, or Windows Terminal — do not double-click the `.exe` (it prints usage and exits immediately). For full hook support, use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) instead.
|
||||
|
||||
## Verify installation
|
||||
|
||||
```bash
|
||||
rtk --version # rtk x.y.z
|
||||
rtk gain # token savings dashboard
|
||||
```
|
||||
|
||||
If `rtk gain` fails but `rtk --version` succeeds, you installed Rust Type Kit by mistake. Uninstall it first:
|
||||
|
||||
```bash
|
||||
cargo uninstall rtk
|
||||
```
|
||||
|
||||
Then reinstall using one of the methods above.
|
||||
|
||||
## Project initialization
|
||||
|
||||
Run once per project to enable the Claude Code hook:
|
||||
|
||||
```bash
|
||||
rtk init
|
||||
```
|
||||
|
||||
For a global install that patches `settings.json` automatically:
|
||||
|
||||
```bash
|
||||
rtk init --global
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
||||
```bash
|
||||
rtk init -g --uninstall # remove hook, RTK.md, and settings.json entry
|
||||
cargo uninstall rtk # remove binary (if installed via Cargo)
|
||||
brew uninstall rtk # remove binary (if installed via Homebrew)
|
||||
```
|
||||
@@ -0,0 +1,86 @@
|
||||
---
|
||||
title: Quick Start
|
||||
description: Get RTK running in 5 minutes and see your first token savings
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
# Quick Start
|
||||
|
||||
This guide walks you through your first RTK commands after installation.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
RTK is installed and verified:
|
||||
|
||||
```bash
|
||||
rtk --version # rtk x.y.z
|
||||
rtk gain # shows token savings dashboard
|
||||
```
|
||||
|
||||
If not, see [Installation](./installation.md).
|
||||
|
||||
## Step 1: Initialize for your AI assistant
|
||||
|
||||
```bash
|
||||
# For Claude Code (global — applies to all projects)
|
||||
rtk init --global
|
||||
|
||||
# For a single project only
|
||||
cd /your/project && rtk init
|
||||
```
|
||||
|
||||
This installs the hook that automatically rewrites commands. Restart your AI assistant after this step.
|
||||
|
||||
### Preview without writing: `--dry-run`
|
||||
|
||||
To see exactly what `init` would change before it touches anything, add `--dry-run`:
|
||||
|
||||
```bash
|
||||
rtk init --global --dry-run
|
||||
```
|
||||
|
||||
Every would-be file create/update/patch is printed with a `[dry-run] would ...` prefix, then a `[dry-run] Nothing written.` footer. Nothing on disk is modified, no settings.json is patched, and the telemetry consent prompt is skipped. Combine with `-v` to also print the full content RTK would write:
|
||||
|
||||
```bash
|
||||
rtk init --global --dry-run -v
|
||||
```
|
||||
|
||||
`--dry-run` works for every init flavour (`--agent cursor`, `--gemini`, `--codex`, `--copilot`, `--uninstall`, ...). It cannot be combined with `--show`.
|
||||
|
||||
## Step 2: Use your tools normally
|
||||
|
||||
Once the hook is installed, nothing changes in how you work. Your AI assistant runs commands as usual — the hook intercepts them transparently and rewrites them before execution.
|
||||
|
||||
For example, when Claude Code runs `cargo test`, the hook rewrites it to `rtk cargo test` before it executes. The LLM receives filtered output with only the failures — not 500 lines of passing tests. You never see or type `rtk`.
|
||||
|
||||
RTK covers all major ecosystems — Git, Cargo/Rust, JavaScript, Python, Go, Ruby, .NET, Docker/Kubernetes, and more. See [What RTK Optimizes](../resources/what-rtk-covers.md) for the full list.
|
||||
|
||||
## Step 3: Check your savings
|
||||
|
||||
After a few commands, see how much was saved:
|
||||
|
||||
```bash
|
||||
rtk gain
|
||||
```
|
||||
|
||||
```
|
||||
Total commands : 12
|
||||
Input tokens : 45,230
|
||||
Output tokens : 4,890
|
||||
Saved : 40,340 (89.2%)
|
||||
```
|
||||
|
||||
## Step 4: Unsupported commands
|
||||
|
||||
Commands RTK doesn't recognize run through passthrough — output is unchanged, usage is tracked:
|
||||
|
||||
```bash
|
||||
rtk proxy make install
|
||||
```
|
||||
|
||||
## Next steps
|
||||
|
||||
- [What RTK Optimizes](../resources/what-rtk-covers.md) — all supported commands and savings by ecosystem
|
||||
- [Supported agents](./supported-agents.md) — Claude Code, Cursor, Copilot, and more
|
||||
- [Configuration](./configuration.md) — customize RTK behavior
|
||||
@@ -0,0 +1,242 @@
|
||||
---
|
||||
title: Supported Agents
|
||||
description: How to integrate RTK with Claude Code, Cursor, Copilot, Cline, Windsurf, Codex, OpenCode, Hermes, Kilo Code, Antigravity, and Factory Droid
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
# Supported Agents
|
||||
|
||||
RTK supports all major AI coding agents across 3 integration tiers. Mistral Vibe support is planned.
|
||||
|
||||
## How it works
|
||||
|
||||
Each agent integration intercepts CLI commands before execution and rewrites them to their RTK equivalent. The agent runs `rtk cargo test` instead of `cargo test`, sees filtered output, and uses up to 90% fewer tokens — without any change to your workflow.
|
||||
|
||||
All rewrite logic lives in the RTK binary (`rtk rewrite`). Agent hooks are thin delegates that parse the agent-specific JSON format and call `rtk rewrite` for the actual decision.
|
||||
|
||||
```
|
||||
Agent runs "cargo test"
|
||||
-> Hook intercepts (PreToolUse / plugin event)
|
||||
-> Calls rtk rewrite "cargo test"
|
||||
-> Returns "rtk cargo test"
|
||||
-> Agent executes filtered command
|
||||
-> LLM sees 90% fewer tokens
|
||||
```
|
||||
|
||||
## Supported agents
|
||||
|
||||
| Agent | Integration tier | Can rewrite transparently? |
|
||||
|-------|-----------------|---------------------------|
|
||||
| Claude Code | Shell hook (`PreToolUse`) | Yes |
|
||||
| VS Code Copilot Chat | Shell hook (`PreToolUse`) | Yes |
|
||||
| GitHub Copilot CLI | Shell hook (`preToolUse` `modifiedArgs`) | Yes |
|
||||
| Cursor | Shell hook (`preToolUse`) | Yes |
|
||||
| Gemini CLI | Rust binary (`BeforeTool`) | Yes |
|
||||
| OpenCode | TypeScript plugin (`tool.execute.before`) | Yes |
|
||||
| OpenClaw | TypeScript plugin (`before_tool_call`) | Yes |
|
||||
| Pi | TypeScript extension (`tool_call` event) | Yes |
|
||||
| Hermes | Python plugin (`terminal` command mutation) | Yes |
|
||||
| Factory Droid | Shell hook (`PreToolUse`, matcher `Execute`) | Yes |
|
||||
| Cline / Roo Code | Rules file (prompt-level) | N/A |
|
||||
| Windsurf | Rules file (prompt-level) | N/A |
|
||||
| Codex CLI | AGENTS.md instructions | N/A |
|
||||
| Kilo Code | Rules file (prompt-level) | N/A |
|
||||
| Google Antigravity | Rules file (prompt-level) | N/A |
|
||||
| Mistral Vibe | Planned ([#800](https://github.com/rtk-ai/rtk/issues/800)) | Pending upstream |
|
||||
|
||||
## Installation by agent
|
||||
|
||||
### Claude Code
|
||||
|
||||
```bash
|
||||
rtk init --global # installs hook + patches settings.json
|
||||
```
|
||||
|
||||
Restart Claude Code. Verify:
|
||||
|
||||
```bash
|
||||
rtk init --show # shows hook status
|
||||
```
|
||||
|
||||
### Cursor
|
||||
|
||||
```bash
|
||||
rtk init --global --agent cursor
|
||||
```
|
||||
|
||||
Restart Cursor. The hook uses `preToolUse` with Cursor's `updated_input` format.
|
||||
|
||||
### GitHub Copilot (VS Code Chat + CLI)
|
||||
|
||||
```bash
|
||||
rtk init --copilot # project-scoped (.github/hooks/)
|
||||
rtk init --global --copilot # user-scoped (~/.copilot/hooks/, respects $COPILOT_HOME)
|
||||
```
|
||||
|
||||
Project-scoped writes `.github/hooks/rtk-rewrite.json` (both hosts get transparent rewrite — VS Code Chat via `updatedInput`, Copilot CLI via `modifiedArgs`) plus the RTK block in `.github/copilot-instructions.md`. User-scoped writes the same hook config to `~/.copilot/hooks/rtk-rewrite.json` and the RTK block to `~/.copilot/copilot-instructions.md` (both respect `$COPILOT_HOME` if set).
|
||||
|
||||
Uninstall:
|
||||
|
||||
```bash
|
||||
rtk init --uninstall --copilot
|
||||
rtk init --uninstall --global --copilot
|
||||
```
|
||||
|
||||
Removes only RTK's hook file (and, for project, the RTK block in `copilot-instructions.md`). Other files in `.github/hooks/` or `~/.copilot/hooks/` and your own instruction content are untouched.
|
||||
|
||||
### Gemini CLI
|
||||
|
||||
```bash
|
||||
rtk init --global --gemini
|
||||
```
|
||||
|
||||
### OpenCode
|
||||
|
||||
```bash
|
||||
rtk init --global --opencode
|
||||
```
|
||||
|
||||
Creates `~/.config/opencode/plugins/rtk.ts`. Uses the `tool.execute.before` hook.
|
||||
|
||||
### Pi
|
||||
|
||||
```bash
|
||||
# Project-local (default)
|
||||
rtk init --agent pi
|
||||
|
||||
# Global — all projects
|
||||
rtk init --agent pi --global
|
||||
```
|
||||
|
||||
Creates `.pi/extensions/rtk.ts` (local) or `~/.pi/agent/extensions/rtk.ts` (global). Pi auto-discovers extensions from both paths on startup.
|
||||
|
||||
Uninstall:
|
||||
|
||||
```bash
|
||||
rtk init --uninstall --agent pi
|
||||
rtk init --uninstall --agent pi --global
|
||||
```
|
||||
|
||||
Removes only the installed Pi extension file.
|
||||
|
||||
### OpenClaw
|
||||
|
||||
```bash
|
||||
openclaw plugins install ./openclaw
|
||||
```
|
||||
|
||||
Plugin in the `openclaw/` directory. Uses the `before_tool_call` hook, delegates to `rtk rewrite`.
|
||||
|
||||
### Hermes
|
||||
|
||||
```bash
|
||||
rtk init --agent hermes
|
||||
```
|
||||
|
||||
Creates `~/.hermes/plugins/rtk-rewrite/` and enables it through `plugins.enabled` in the Hermes config. Hermes loads Python plugins, so the plugin entrypoint is Python, but it is only a thin adapter. It mutates the Hermes `terminal` tool `command` before execution and delegates all rewrite decisions to Rust through `rtk rewrite`. The repository source and tests for that adapter live in `hooks/hermes/`; only installed runtime files use the `~/.hermes/plugins/rtk-rewrite/` path.
|
||||
|
||||
The plugin fails open. If `rtk` is missing at load time, the hook is not registered. If `rtk rewrite` errors, the tool is not `terminal`, the payload has no string `command`, or the plugin raises an exception, Hermes runs the original command unchanged. The same `rtk rewrite` limitations apply: already-prefixed `rtk` commands, compound shell commands, heredocs, and commands without filters are not rewritten.
|
||||
|
||||
### Factory Droid
|
||||
|
||||
```bash
|
||||
rtk init -g --agent droid # user-scoped (~/.factory/hooks.json)
|
||||
rtk init --agent droid # project-scoped (.factory/hooks.json, commit to share)
|
||||
```
|
||||
|
||||
Installs a `PreToolUse` hook (matcher `Execute`) into Droid's canonical `hooks.json` — falling back to the `hooks` key of `settings.json` only when that file already carries live `PreToolUse` hooks. Respects `$FACTORY_HOME_OVERRIDE`.
|
||||
|
||||
RTK honors Droid's own permission lists, never another agent's settings. Commands matching an explicit `commandDenylist` or `commandBlocklist` entry — read from all four settings scopes (`~/.factory/settings.json`, `~/.factory/settings.local.json`, `.factory/settings.json`, `.factory/settings.local.json`) — are left untouched so Droid's native confirmation or block fires on the original command. Every other command is rewritten via `updatedInput` with **no** permission decision: Droid's native flow (allowlist, autonomy level, other hooks) decides on the rewritten command. To auto-run rewritten read-only commands, add `rtk`-prefixed entries (e.g. `rtk git status`) to your `commandAllowlist`.
|
||||
|
||||
Uninstall:
|
||||
|
||||
```bash
|
||||
rtk init --uninstall -g --agent droid
|
||||
rtk init --uninstall --agent droid
|
||||
```
|
||||
|
||||
Removes only RTK's hook entry; other hooks and settings are untouched.
|
||||
|
||||
### Cline / Roo Code
|
||||
|
||||
```bash
|
||||
rtk init --agent cline # creates .clinerules in current project
|
||||
```
|
||||
|
||||
Cline reads `.clinerules` as custom instructions. RTK adds guidance telling Cline to prefer `rtk <cmd>` over raw commands.
|
||||
|
||||
### Windsurf
|
||||
|
||||
```bash
|
||||
rtk init --global --agent windsurf # creates .windsurfrules in current project
|
||||
```
|
||||
|
||||
### Codex CLI
|
||||
|
||||
```bash
|
||||
rtk init --codex # project-scoped (AGENTS.md)
|
||||
rtk init --global --codex # user-global (~/.codex/AGENTS.md)
|
||||
```
|
||||
|
||||
### Kilo Code
|
||||
|
||||
```bash
|
||||
rtk init --agent kilocode # creates .kilocode/rules/rtk-rules.md in current project
|
||||
```
|
||||
|
||||
Kilo Code reads `.kilocode/rules/` as custom instructions. RTK adds guidance telling Kilo Code to prefer `rtk <cmd>` over raw commands.
|
||||
|
||||
### Google Antigravity
|
||||
|
||||
```bash
|
||||
rtk init --agent antigravity # creates .agents/rules/antigravity-rtk-rules.md in current project
|
||||
```
|
||||
|
||||
Antigravity reads `.agents/rules/` as custom instructions. RTK adds guidance telling Antigravity to prefer `rtk <cmd>` over raw commands.
|
||||
|
||||
### Mistral Vibe (planned)
|
||||
|
||||
Support is blocked on upstream `BeforeToolCallback` ([mistral-vibe#531](https://github.com/mistralai/mistral-vibe/issues/531)). Tracked in [#800](https://github.com/rtk-ai/rtk/issues/800).
|
||||
|
||||
## Integration tiers explained
|
||||
|
||||
| Tier | Mechanism | How rewrites work |
|
||||
|------|-----------|------------------|
|
||||
| **Full hook** | Shell script or Rust binary, intercepts via agent API | Transparent — agent never sees the raw command |
|
||||
| **Plugin** | TypeScript, JavaScript, or Python in agent's plugin system | Transparent, in-place mutation when the agent allows it |
|
||||
| **Rules file** | Prompt-level instructions | Guidance only — agent is told to prefer `rtk <cmd>` |
|
||||
|
||||
Rules file integrations (Cline, Windsurf, Codex, Kilo Code, Antigravity) rely on the model following instructions. Full hook integrations (Claude Code, Cursor, Gemini) are guaranteed — the command is rewritten before the agent sees it. Plugin integrations (OpenCode, Pi) use in-place mutation via the agent's TypeScript extension API.
|
||||
|
||||
## Windows support
|
||||
|
||||
The shell hook (`rtk-rewrite.sh`) requires a Unix shell. On native Windows:
|
||||
|
||||
- `rtk init -g` automatically falls back to **CLAUDE.md injection mode** (prompt-level instructions)
|
||||
- Filters work normally (`rtk cargo test`, `rtk git status`)
|
||||
- Auto-rewrite does not work — the AI assistant is instructed to use RTK but commands are not intercepted
|
||||
|
||||
For full hook support on Windows, use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install). Inside WSL, all agents with shell hook integration (Claude Code, Cursor, Gemini) work identically to Linux.
|
||||
|
||||
## Graceful degradation
|
||||
|
||||
Hooks never block command execution. If RTK is missing, the hook exits cleanly and the raw command runs unchanged:
|
||||
|
||||
- RTK binary not found: warning to stderr, exit 0
|
||||
- Invalid JSON input: pass through unchanged
|
||||
- RTK version too old: warning to stderr, exit 0
|
||||
- Filter logic error: fallback to raw command output
|
||||
|
||||
## Override: disable RTK for one command
|
||||
|
||||
```bash
|
||||
RTK_DISABLED=1 git status # runs raw git status, no rewrite
|
||||
```
|
||||
|
||||
Or exclude commands permanently in `~/.config/rtk/config.toml`:
|
||||
|
||||
```toml
|
||||
[hooks]
|
||||
exclude_commands = ["git rebase", "git cherry-pick"]
|
||||
```
|
||||
@@ -0,0 +1,65 @@
|
||||
---
|
||||
title: RTK Documentation
|
||||
description: RTK (Rust Token Killer) — reduce LLM token consumption by 60-90% on common dev commands, with zero workflow changes
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
# RTK — Rust Token Killer
|
||||
|
||||
RTK is a CLI proxy that sits between your AI assistant and your development tools. It filters command output before it reaches the LLM, keeping only what matters and discarding boilerplate, progress bars, and noise.
|
||||
|
||||
**Result:** 60-90% fewer tokens consumed per command, without changing how you work. You run `git status` as usual — RTK's hook intercepts it, filters the output, and the LLM sees a compact 3-line summary instead of 40 lines.
|
||||
|
||||
## How it works
|
||||
|
||||
```
|
||||
Your AI assistant runs: git status
|
||||
↓
|
||||
Hook intercepts (PreToolUse)
|
||||
↓
|
||||
rtk git status (transparent rewrite)
|
||||
↓
|
||||
Raw output: 40 lines → Filtered: 3 lines
|
||||
~800 tokens → ~60 tokens (92% saved)
|
||||
↓
|
||||
LLM sees the compact output
|
||||
```
|
||||
|
||||
Zero config changes to your workflow. The hook handles everything automatically.
|
||||
|
||||
## What RTK optimizes
|
||||
|
||||
Dozens of commands across all major ecosystems — Git, Cargo/Rust, JavaScript, Python, Go, Ruby, .NET, Docker/Kubernetes, and more. See [What RTK Optimizes](./resources/what-rtk-covers.md) for the full list with savings percentages.
|
||||
|
||||
## Get started
|
||||
|
||||
1. **[Installation](./getting-started/installation.md)** — Install RTK and verify you have the right package
|
||||
2. **[Quick Start](./getting-started/quick-start.md)** — Connect to your AI assistant in 5 minutes
|
||||
3. **[Supported Agents](./getting-started/supported-agents.md)** — Claude Code, Cursor, Copilot, Gemini, and more
|
||||
|
||||
## Measure your savings
|
||||
|
||||
```bash
|
||||
rtk gain # total savings across all sessions
|
||||
rtk gain --daily # day-by-day breakdown
|
||||
rtk gain --weekly # weekly aggregation
|
||||
```
|
||||
|
||||
See [Token Savings Analytics](./analytics/gain.md) for export formats and analysis workflows.
|
||||
|
||||
## Analyze your usage
|
||||
|
||||
```bash
|
||||
rtk discover # find commands that ran without RTK (missed savings)
|
||||
rtk session # RTK adoption rate per Claude Code session
|
||||
```
|
||||
|
||||
See [Discover and Session](./analytics/discover.md) for details.
|
||||
|
||||
## Further reading
|
||||
|
||||
- [Configuration](./getting-started/configuration.md) — config.toml, global flags, env vars, tee recovery
|
||||
- [Troubleshooting](./resources/troubleshooting.md) — common issues and fixes
|
||||
- [Telemetry & Privacy](./resources/telemetry.md) — what RTK collects and how to opt out
|
||||
- [ARCHITECTURE.md](https://github.com/rtk-ai/rtk/blob/master/ARCHITECTURE.md) — system design for contributors
|
||||
@@ -0,0 +1,168 @@
|
||||
---
|
||||
title: Telemetry & Privacy
|
||||
description: What RTK collects, how to opt out, and your GDPR rights
|
||||
sidebar:
|
||||
order: 3
|
||||
---
|
||||
|
||||
# Telemetry & Privacy
|
||||
|
||||
RTK collects anonymous, aggregate usage metrics once per day to help improve the product. Telemetry is **disabled by default** and requires explicit consent during `rtk init` or `rtk telemetry enable`.
|
||||
|
||||
## Data Collector
|
||||
|
||||
**Entity**: `RTK AI Labs`
|
||||
**Contact**: contact@rtk-ai.app
|
||||
|
||||
## Why we collect telemetry
|
||||
|
||||
Without telemetry, we have no visibility into:
|
||||
|
||||
- Which commands are used most and need the best filters
|
||||
- Which filters are underperforming and need improvement
|
||||
- Which ecosystems to prioritize for new filter development
|
||||
- How much value RTK delivers to users (token savings in $ terms)
|
||||
- Whether users stay engaged over time or churn after trying RTK
|
||||
|
||||
This data directly drives our roadmap. For example, if telemetry shows that 40% of users run Python commands but only 10% of our filters cover Python, we know where to invest next.
|
||||
|
||||
## How it works
|
||||
|
||||
1. **Once per day** (23-hour interval), RTK sends a single HTTPS POST to our telemetry endpoint
|
||||
2. The ping runs in a **background thread** and never blocks the CLI (2-second timeout)
|
||||
3. A marker file prevents duplicate pings within the interval
|
||||
4. If the server is unreachable, the ping is silently dropped — no retries, no queue
|
||||
|
||||
## What is collected
|
||||
|
||||
### Identity (anonymous)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `device_hash` | `a3f8c9...` (64 hex chars) | Count unique installations. SHA-256 of a per-device random salt stored locally (`~/.local/share/rtk/.device_salt`). Not reversible. No hostname or username included. |
|
||||
|
||||
### Environment
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `version` | `0.34.1` | Track adoption of new versions |
|
||||
| `os` | `macos` | Know which platforms to support and test |
|
||||
| `arch` | `aarch64` | Prioritize ARM vs x86 builds |
|
||||
| `install_method` | `homebrew` | Understand distribution channels (homebrew/cargo/script/nix) |
|
||||
|
||||
### Usage volume
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `commands_24h` | `142` | Daily activity level |
|
||||
| `commands_total` | `32888` | Lifetime usage — segment light vs heavy users |
|
||||
| `top_commands` | `["git", "cargo", "ls"]` | Most popular tools (names only, max 5) |
|
||||
| `tokens_saved_24h` | `450000` | Daily value delivered |
|
||||
| `tokens_saved_total` | `96500000` | Lifetime value delivered |
|
||||
| `savings_pct` | `72.5` | Overall effectiveness |
|
||||
|
||||
### Quality (filter improvement)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `passthrough_top` | `["git:15", "npm:8"]` | Top 5 commands with 0% savings — these need filters |
|
||||
| `parse_failures_24h` | `3` | Filter fragility — high count means filters are breaking |
|
||||
| `low_savings_commands` | `["rtk docker ps:25%"]` | Commands averaging <30% savings — filters to improve |
|
||||
| `avg_savings_per_command` | `68.5` | Unweighted average (vs global which is volume-biased) |
|
||||
|
||||
### Ecosystem distribution
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `ecosystem_mix` | `{"git": 45, "cargo": 20, "js": 15}` | Category percentages — where to invest filter development |
|
||||
|
||||
### Retention (engagement)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `first_seen_days` | `45` | Installation age in days |
|
||||
| `active_days_30d` | `22` | Days with at least 1 command in last 30 days — measures stickiness |
|
||||
|
||||
### Economics
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `tokens_saved_30d` | `12000000` | 30-day token savings for trend analysis |
|
||||
| `estimated_savings_usd_30d` | `36.0` | Estimated dollar value saved (at ~$3/Mtok input pricing, Claude Sonnet) |
|
||||
|
||||
### Adoption
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `hook_type` | `claude` | Which AI agent hook is installed (claude/gemini/codex/cursor/none) |
|
||||
| `custom_toml_filters` | `3` | Number of user-created TOML filter files — DSL adoption |
|
||||
|
||||
### Configuration (user maturity)
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `has_config_toml` | `true` | Whether user has customized RTK config |
|
||||
| `exclude_commands_count` | `2` | Commands excluded from rewriting — high count may indicate frustration |
|
||||
| `projects_count` | `5` | Distinct project paths — multi-project = power user |
|
||||
|
||||
### Feature adoption
|
||||
|
||||
| Field | Example | Purpose |
|
||||
|-------|---------|---------|
|
||||
| `meta_usage` | `{"gain": 5, "discover": 2}` | Which RTK features are actually used |
|
||||
|
||||
## What is NOT collected
|
||||
|
||||
- Source code or file contents
|
||||
- Full command lines or arguments (only tool names like "git", "cargo")
|
||||
- File paths or directory structures
|
||||
- Secrets, API keys, or environment variable values
|
||||
- Repository names or URLs
|
||||
- Personally identifiable information
|
||||
- IP addresses (not stored in telemetry pings; stored temporarily in erasure audit log for accountability, anonymized after 6 months)
|
||||
|
||||
## Consent
|
||||
|
||||
Telemetry requires explicit opt-in consent (GDPR Art. 6, 7). Consent is requested during `rtk init` or via `rtk telemetry enable`. Without consent, no data is sent.
|
||||
|
||||
```bash
|
||||
rtk telemetry status # Check current consent state
|
||||
rtk telemetry enable # Give consent (interactive prompt)
|
||||
rtk telemetry disable # Withdraw consent
|
||||
rtk telemetry forget # Withdraw consent + delete local data + request server erasure
|
||||
```
|
||||
|
||||
Environment variable override (blocks telemetry regardless of consent):
|
||||
```bash
|
||||
export RTK_TELEMETRY_DISABLED=1
|
||||
```
|
||||
|
||||
## Retention Policy
|
||||
|
||||
- **Server-side**: telemetry records are retained for a maximum of **12 months**, then automatically purged.
|
||||
- **Server-side (erasure log)**: IP addresses in the erasure audit log are **anonymized after 6 months** (GDPR — IP is personal data).
|
||||
- **Client-side**: the local SQLite database (`~/.local/share/rtk/history.db`) retains data for **90 days** by default (configurable via `tracking.history_days` in `config.toml`). Deleted entirely by `rtk telemetry forget`.
|
||||
|
||||
## Your Rights (GDPR)
|
||||
|
||||
Under the EU General Data Protection Regulation, you have the right to:
|
||||
|
||||
- **Access** your data: `rtk telemetry status` shows your device hash; the telemetry payload is fully documented above.
|
||||
- **Rectification**: since data is anonymous and aggregate, rectification is not applicable.
|
||||
- **Erasure** (Art. 17): run `rtk telemetry forget` to delete local data and send an erasure request to the server. Alternatively, email contact@rtk-ai.app with your device hash.
|
||||
- **Restriction of processing**: `rtk telemetry disable` stops all data collection immediately.
|
||||
- **Portability**: the local SQLite database at `~/.local/share/rtk/history.db` contains all locally stored data.
|
||||
- **Objection**: `rtk telemetry disable` or `export RTK_TELEMETRY_DISABLED=1`.
|
||||
|
||||
## Erasure Procedure
|
||||
|
||||
1. Run `rtk telemetry forget` — this disables telemetry, deletes your device salt, ping marker, and local tracking database (`history.db`), then sends an erasure request to the server.
|
||||
2. If the server is unreachable, the CLI prints your full device hash and fallback instructions to email contact@rtk-ai.app for manual erasure.
|
||||
3. You can also email contact@rtk-ai.app directly to request manual erasure.
|
||||
|
||||
## Data Handling
|
||||
|
||||
- All communications use HTTPS (TLS)
|
||||
- Data is used exclusively for RTK product improvement
|
||||
- No data is sold or shared with third parties
|
||||
- Aggregate statistics may be published (e.g. "70% of RTK users are on macOS")
|
||||
@@ -0,0 +1,184 @@
|
||||
---
|
||||
title: Troubleshooting
|
||||
description: Common RTK issues and how to fix them
|
||||
sidebar:
|
||||
order: 2
|
||||
---
|
||||
|
||||
# Troubleshooting
|
||||
|
||||
## `rtk gain` says "not a rtk command"
|
||||
|
||||
**Symptom:**
|
||||
```bash
|
||||
$ rtk gain
|
||||
rtk: 'gain' is not a rtk command. See 'rtk --help'.
|
||||
```
|
||||
|
||||
**Cause:** You installed **Rust Type Kit** (`reachingforthejack/rtk`) instead of **Rust Token Killer** (`rtk-ai/rtk`). They share the same binary name.
|
||||
|
||||
**Fix:**
|
||||
```bash
|
||||
cargo uninstall rtk
|
||||
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/master/install.sh | sh
|
||||
rtk gain # should now show token savings stats
|
||||
```
|
||||
|
||||
## How to tell which rtk you have
|
||||
|
||||
| If `rtk gain`... | You have |
|
||||
|------------------|----------|
|
||||
| Shows token savings dashboard | Rust Token Killer ✅ |
|
||||
| Returns "not a rtk command" | Rust Type Kit ❌ |
|
||||
|
||||
## AI assistant not using RTK
|
||||
|
||||
**Symptom:** Claude Code (or another agent) runs `cargo test` instead of `rtk cargo test`.
|
||||
|
||||
**Checklist:**
|
||||
|
||||
1. Verify RTK is installed:
|
||||
```bash
|
||||
rtk --version
|
||||
rtk gain
|
||||
```
|
||||
|
||||
2. Initialize the hook:
|
||||
```bash
|
||||
rtk init --global # Claude Code
|
||||
rtk init --global --cursor # Cursor
|
||||
rtk init --global --opencode # OpenCode
|
||||
```
|
||||
|
||||
3. Restart your AI assistant.
|
||||
|
||||
4. Verify hook status:
|
||||
```bash
|
||||
rtk init --show
|
||||
```
|
||||
|
||||
5. Check `settings.json` has the hook registered (Claude Code):
|
||||
```bash
|
||||
cat ~/.claude/settings.json | grep rtk
|
||||
```
|
||||
|
||||
## RTK not found after `cargo install`
|
||||
|
||||
**Symptom:**
|
||||
```bash
|
||||
$ rtk --version
|
||||
zsh: command not found: rtk
|
||||
```
|
||||
|
||||
**Cause:** `~/.cargo/bin` is not in your PATH.
|
||||
|
||||
**Fix:**
|
||||
|
||||
For bash (`~/.bashrc`) or zsh (`~/.zshrc`):
|
||||
```bash
|
||||
export PATH="$HOME/.cargo/bin:$PATH"
|
||||
```
|
||||
|
||||
For fish (`~/.config/fish/config.fish`):
|
||||
```fish
|
||||
set -gx PATH $HOME/.cargo/bin $PATH
|
||||
```
|
||||
|
||||
Then reload:
|
||||
```bash
|
||||
source ~/.zshrc # or ~/.bashrc
|
||||
rtk --version
|
||||
```
|
||||
|
||||
## RTK on Windows
|
||||
|
||||
### Double-clicking rtk.exe does nothing
|
||||
|
||||
**Symptom:** You double-click `rtk.exe`, a terminal flashes and closes instantly.
|
||||
|
||||
**Cause:** RTK is a command-line tool. With no arguments, it prints usage and exits. The console window opens and closes before you can read anything.
|
||||
|
||||
**Fix:** Open a terminal first, then run RTK from there:
|
||||
- Press `Win+R`, type `cmd`, press Enter
|
||||
- Or open PowerShell or Windows Terminal
|
||||
- Then run: `rtk --version`
|
||||
|
||||
### Hook not working (no auto-rewrite)
|
||||
|
||||
**Symptom:** `rtk init -g` shows "Falling back to --claude-md mode" on Windows.
|
||||
|
||||
**Cause:** The auto-rewrite hook (`rtk-rewrite.sh`) requires a Unix shell. Native Windows doesn't have one.
|
||||
|
||||
**Fix:** Use [WSL](https://learn.microsoft.com/en-us/windows/wsl/install) for full hook support:
|
||||
```bash
|
||||
# Inside WSL
|
||||
curl -fsSL https://raw.githubusercontent.com/rtk-ai/rtk/refs/heads/master/install.sh | sh
|
||||
rtk init -g # full hook mode works in WSL
|
||||
```
|
||||
|
||||
On native Windows, RTK falls back to CLAUDE.md injection. Your AI assistant gets RTK instructions but won't auto-rewrite commands. It can still use RTK manually: `rtk cargo test`, `rtk git status`, etc.
|
||||
|
||||
### Node.js tools not found
|
||||
|
||||
**Symptom:**
|
||||
```
|
||||
rtk vitest --run
|
||||
Error: program not found
|
||||
```
|
||||
|
||||
**Cause:** On Windows, Node.js tools are installed as `.CMD`/`.BAT` wrappers. Older RTK versions couldn't find them.
|
||||
|
||||
**Fix:** Update to RTK v0.23.1+:
|
||||
```bash
|
||||
cargo install --git https://github.com/rtk-ai/rtk
|
||||
rtk --version # should be 0.23.1+
|
||||
```
|
||||
|
||||
## Compilation error during installation
|
||||
|
||||
```bash
|
||||
rustup update stable
|
||||
rustup default stable
|
||||
cargo clean
|
||||
cargo build --release
|
||||
cargo install --path . --force
|
||||
```
|
||||
|
||||
Minimum required Rust version: 1.70+.
|
||||
|
||||
## OpenCode not using RTK
|
||||
|
||||
```bash
|
||||
rtk init --global --opencode
|
||||
# restart OpenCode
|
||||
rtk init --show # should show "OpenCode: plugin installed"
|
||||
```
|
||||
|
||||
## `cargo install rtk` installs the wrong package
|
||||
|
||||
If Rust Type Kit is published to crates.io under the name `rtk`, `cargo install rtk` may install the wrong one.
|
||||
|
||||
Always use the explicit URL:
|
||||
|
||||
```bash
|
||||
cargo install --git https://github.com/rtk-ai/rtk
|
||||
```
|
||||
|
||||
## Run the diagnostic script
|
||||
|
||||
From the RTK repository root:
|
||||
|
||||
```bash
|
||||
bash scripts/check-installation.sh
|
||||
```
|
||||
|
||||
Checks:
|
||||
- RTK installed and in PATH
|
||||
- Correct version (Token Killer, not Type Kit)
|
||||
- Available features
|
||||
- Claude Code integration
|
||||
- Hook status
|
||||
|
||||
## Still stuck?
|
||||
|
||||
Open an issue: https://github.com/rtk-ai/rtk/issues
|
||||
@@ -0,0 +1,157 @@
|
||||
---
|
||||
title: What RTK Optimizes
|
||||
description: Commands and ecosystems automatically optimized by RTK with typical token savings
|
||||
sidebar:
|
||||
order: 1
|
||||
---
|
||||
|
||||
# What RTK Optimizes
|
||||
|
||||
Once RTK is installed with a hook, these commands are automatically intercepted and filtered. You run them normally — the hook rewrites them transparently before execution.
|
||||
|
||||
Typical savings: 60-99%.
|
||||
|
||||
## Git
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `git status` | 75-93% | Compact stat format, grouped by state |
|
||||
| `git log` | 80-92% | Hash + author + subject only |
|
||||
| `git diff` | 70% | Context reduced, headers stripped |
|
||||
| `git show` | 70% | Same as diff |
|
||||
| `git stash list` | 75% | Compact one-line per entry |
|
||||
|
||||
## GitHub CLI
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `gh pr view` | 87% | Removes ASCII art and verbose metadata |
|
||||
| `gh pr checks` | 79% | Status + name only, failures highlighted |
|
||||
| `gh run list` | 82% | Compact workflow run summary |
|
||||
| `gh issue view` | 80% | Body only, no decoration |
|
||||
|
||||
## Graphite (Stacked PRs)
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `gt log` | 75% | Stack summary only |
|
||||
| `gt status` | 70% | Current branch context |
|
||||
|
||||
## Cargo / Rust
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `cargo test` | 90% | Failures only, passed tests suppressed |
|
||||
| `cargo nextest` | 90% | Same as test |
|
||||
| `cargo build` | 80% | Errors and warnings only |
|
||||
| `cargo check` | 80% | Errors and warnings only |
|
||||
| `cargo clippy` | 80% | Lint warnings grouped by file |
|
||||
|
||||
## JavaScript / TypeScript
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `jest` | 94-99% | Failures only |
|
||||
| `vitest` | 94-99% | Failures only |
|
||||
| `tsc` | 75% | Type errors grouped by file |
|
||||
| `eslint` | 84% | Violations grouped by rule |
|
||||
| `pnpm list` | 70-90% | Compact dependency tree |
|
||||
| `pnpm outdated` | 70% | Package + current + latest only |
|
||||
| `next build` | 80% | Route summary + errors only |
|
||||
| `prisma migrate` | 75% | Migration status only |
|
||||
| `playwright test` | 90% | Failures + trace links only |
|
||||
|
||||
## Python
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `pytest` | 80-90% | Failures only |
|
||||
| `ruff check` | 75% | Violations grouped by file |
|
||||
| `mypy` | 75% | Type errors grouped by file |
|
||||
| `pip install` | 70% | Installed packages only, progress stripped |
|
||||
|
||||
## Go
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `go test` | 80-90% | Failures only |
|
||||
| `golangci-lint run` | 75% | Violations grouped by file |
|
||||
| `go build` | 75% | Errors only |
|
||||
|
||||
## Ruby
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `rspec` | 80-90% | Failures only |
|
||||
| `rubocop` | 75% | Offenses grouped by file |
|
||||
| `rake` | 70% | Task output, build errors highlighted |
|
||||
|
||||
## .NET
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `dotnet build` | 80% | Errors and warnings only |
|
||||
| `dotnet test` | 85-90% | Failures only |
|
||||
| `dotnet format` | 75% | Changed files only |
|
||||
|
||||
## Docker / Kubernetes
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `docker ps` | 65% | Essential columns (name, image, status, port) |
|
||||
| `docker images` | 60% | Name + tag + size only |
|
||||
| `docker logs` | 70% | Deduplicated, last N lines |
|
||||
| `docker compose up` | 75% | Service status, errors highlighted |
|
||||
| `kubectl get pods` | 65% | Name + status + restarts only |
|
||||
| `kubectl logs` | 70% | Deduplicated entries |
|
||||
|
||||
## Files and Search
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `ls` | 80% | Tree format with file counts |
|
||||
| `find` | 75% | Tree format |
|
||||
| `grep` | 70% | Truncated lines, grouped by file |
|
||||
| `diff` | 65% | Context reduced |
|
||||
| `wc` | 60% | Compact counts |
|
||||
| `cat` / `head` / `tail <file>` | 60-80% | Smart file reading via `rtk read` |
|
||||
| `rtk smart <file>` | 85% | 2-line heuristic code summary (signatures only) |
|
||||
|
||||
## Cloud and Data
|
||||
|
||||
| Command | Savings | What changes |
|
||||
|---------|---------|--------------|
|
||||
| `aws` | 70% | JSON condensed, relevant fields only |
|
||||
| `psql` | 65% | Query results without decoration |
|
||||
| `curl` | 60% | Response body only, headers stripped |
|
||||
|
||||
## Global flags
|
||||
|
||||
These flags apply to all RTK commands and can push savings even higher:
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--ultra-compact` | ASCII icons, inline format — extra token reduction on top of normal filtering |
|
||||
| `-v` / `--verbose` | Show filtering details on stderr (`-v`, `-vv`, `-vvv` for increasing detail) |
|
||||
|
||||
```bash
|
||||
# Ultra-compact: even smaller output
|
||||
rtk git log --ultra-compact
|
||||
|
||||
# Debug: see what RTK is doing
|
||||
rtk git status -vvv
|
||||
```
|
||||
|
||||
:::note
|
||||
Use `--ultra-compact` (long form) rather than `-u` when working with Git commands. Git's own `-u` flag means `--set-upstream` and the short form can cause confusion.
|
||||
:::
|
||||
|
||||
## Commands that are not rewritten
|
||||
|
||||
If a command isn't in the list above, RTK runs it through passthrough — the output reaches the LLM unchanged. You can explicitly track unsupported commands:
|
||||
|
||||
```bash
|
||||
rtk proxy make install # runs make install, tracks usage, no filtering
|
||||
```
|
||||
|
||||
To check which commands were missed opportunities: `rtk discover`.
|
||||
@@ -0,0 +1,72 @@
|
||||
# RTK Maintainers Application
|
||||
|
||||
RTK is growing fast, with more contributors, PRs, and ideas than ever. To keep things moving smoothly, we're looking for new maintainers.
|
||||
|
||||
We've introduced two types of maintainers to progressively build a clean process and strong collaboration between contributors.
|
||||
For now, we're starting by recruiting **Ecosystem Maintainers** only. As the project evolves, we'll soon begin accepting **Core Maintainers** as well.
|
||||
|
||||
> Maintainers are expected to be active and involved over time, not just occasional contributors.
|
||||
|
||||
---
|
||||
|
||||
## How to apply guide
|
||||
|
||||
#### ✅ Requirements
|
||||
|
||||
To apply, you should have:
|
||||
|
||||
- 3+ merged PRs to RTK (filters, fixes, docs — all contributions count)
|
||||
- 3+ PR reviews with helpful, constructive feedback
|
||||
|
||||
---
|
||||
|
||||
### ✍️ How to Apply
|
||||
|
||||
1. Open a discussion in [rtk-ai/rtk Maintainers Applications · Discussions · GitHub](https://github.com/rtk-ai/rtk/discussions/categories/maintainers-applications) titled **Maintainer Application: [Your GitHub Handle]**
|
||||
2. In your application, include:
|
||||
- The ecosystem(s) you're interested in
|
||||
- Your experience with those ecosystems
|
||||
- Links to your merged PRs and reviews
|
||||
- Your Discord username (and make sure you've joined the server)
|
||||
- Your PRs that have been accepted in RTK
|
||||
3. For **Core Maintainer** applications, also include:
|
||||
- Your experience with Rust
|
||||
- Your experience with Open Source
|
||||
4. A Core Maintainer will get back to you as soon as possible
|
||||
5. If it's a good fit, we'll continue the conversation on Discord and guide you through the next steps
|
||||
|
||||
---
|
||||
|
||||
### 👀 What to Expect
|
||||
|
||||
- A review of your ecosystem experience and understanding of RTK concepts
|
||||
- A discussion with current maintainers
|
||||
- Introduction to the team
|
||||
|
||||
---
|
||||
|
||||
## What Maintainers Do
|
||||
|
||||
### 🌱 Ecosystem Maintainers
|
||||
|
||||
Ecosystem Maintainers are responsible for specific environments inside the `cmds/` folder (e.g. `git`, `system`, etc.). They own and manage their ecosystem end-to-end:
|
||||
|
||||
- Responsible for the quality of filters
|
||||
- Review and ensure quality of contributions
|
||||
- Maintain consistency with the rest of the RTK ecosystem
|
||||
- Help shape and grow their specific domain
|
||||
- Handle issues and PRs related to their environment *(security and quality review from core maintainers still required for release)*
|
||||
|
||||
### 🔧 Core Maintainers (once we've fully integrated some Ecosystem Maintainers)
|
||||
|
||||
Core Maintainers are responsible for the core of RTK. They have a broader scope and higher responsibilities and permissions, including:
|
||||
|
||||
- Maintaining core functionalities and architecture
|
||||
- Reviewing and merging PRs for release with the core team
|
||||
- Defining project direction and standards with the core team
|
||||
- Ensuring consistency across the entire project
|
||||
- Refactoring for optimization, standardization & conformity
|
||||
|
||||
---
|
||||
|
||||
If you enjoy contributing and want to help RTK scale in a healthy way, we'd be excited to have you onboard 🚀
|
||||
@@ -0,0 +1,446 @@
|
||||
# RTK Token Savings Audit Guide
|
||||
|
||||
Complete guide to analyzing your rtk token savings with temporal breakdowns and data exports.
|
||||
|
||||
## Overview
|
||||
|
||||
The `rtk gain` command provides comprehensive analytics for tracking your token savings across time periods.
|
||||
|
||||
**Database Location**: `~/.local/share/rtk/history.db`
|
||||
**Retention Policy**: 90 days
|
||||
**Scope**: Global across all projects, worktrees, and Claude sessions
|
||||
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
# Default summary view
|
||||
rtk gain
|
||||
|
||||
# Temporal breakdowns
|
||||
rtk gain --daily # All days since tracking started
|
||||
rtk gain --weekly # Aggregated by week
|
||||
rtk gain --monthly # Aggregated by month
|
||||
rtk gain --all # Show all breakdowns at once
|
||||
|
||||
# Export formats
|
||||
rtk gain --all --format json > savings.json
|
||||
rtk gain --all --format csv > savings.csv
|
||||
|
||||
# Combined flags
|
||||
rtk gain --graph --history --quota # Classic view with extras
|
||||
rtk gain --daily --weekly --monthly # Multiple breakdowns
|
||||
|
||||
# Reset all tracking data
|
||||
rtk gain --reset # prompts [y/N] before deleting
|
||||
rtk gain --reset --yes # skip prompt (CI/scripts)
|
||||
```
|
||||
|
||||
## Command Options
|
||||
|
||||
### Temporal Flags
|
||||
|
||||
| Flag | Description | Output |
|
||||
|------|-------------|--------|
|
||||
| `--daily` | Day-by-day breakdown | All days with full metrics |
|
||||
| `--weekly` | Week-by-week breakdown | Aggregated by Sunday-Saturday weeks |
|
||||
| `--monthly` | Month-by-month breakdown | Aggregated by calendar month |
|
||||
| `--all` | All time breakdowns | Daily + Weekly + Monthly combined |
|
||||
|
||||
### Classic Flags (still available)
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--graph` | ASCII graph of last 30 days |
|
||||
| `--history` | Recent 10 commands |
|
||||
| `--quota` | Monthly quota analysis (Pro/5x/20x tiers) |
|
||||
| `--tier <TIER>` | Quota tier: pro, 5x, 20x (default: 20x) |
|
||||
|
||||
### Reset Flag
|
||||
|
||||
| Flag | Description |
|
||||
|------|-------------|
|
||||
| `--reset` | Permanently delete all tracking data (commands + parse failures) |
|
||||
| `--yes` | Skip the confirmation prompt (for CI/scripts) |
|
||||
|
||||
> **Warning**: `--reset` is irreversible. It clears both the `commands` and `parse_failures` tables atomically. A `[y/N]` confirmation prompt is shown by default. In non-interactive environments (piped stdin), it defaults to `N` unless `--yes` is passed.
|
||||
|
||||
### Export Formats
|
||||
|
||||
| Format | Flag | Use Case |
|
||||
|--------|------|----------|
|
||||
| `text` | `--format text` (default) | Terminal display |
|
||||
| `json` | `--format json` | Programmatic analysis, APIs |
|
||||
| `csv` | `--format csv` | Excel, data analysis, plotting |
|
||||
|
||||
## Output Examples
|
||||
|
||||
### Daily Breakdown
|
||||
|
||||
```
|
||||
📅 Daily Breakdown (3 days)
|
||||
════════════════════════════════════════════════════════════════
|
||||
Date Cmds Input Output Saved Save%
|
||||
────────────────────────────────────────────────────────────────
|
||||
2026-01-28 89 380.9K 26.7K 355.8K 93.4%
|
||||
2026-01-29 102 894.5K 32.4K 863.7K 96.6%
|
||||
2026-01-30 5 749 55 694 92.7%
|
||||
────────────────────────────────────────────────────────────────
|
||||
TOTAL 196 1.3M 59.2K 1.2M 95.6%
|
||||
```
|
||||
|
||||
**Metrics explained:**
|
||||
- **Cmds**: Number of rtk commands executed
|
||||
- **Input**: Estimated tokens from raw command output
|
||||
- **Output**: Actual tokens after rtk filtering
|
||||
- **Saved**: Input - Output (tokens prevented from reaching LLM)
|
||||
- **Save%**: Percentage reduction (Saved / Input × 100)
|
||||
|
||||
### Weekly Breakdown
|
||||
|
||||
```
|
||||
📊 Weekly Breakdown (1 weeks)
|
||||
════════════════════════════════════════════════════════════════════════
|
||||
Week Cmds Input Output Saved Save%
|
||||
────────────────────────────────────────────────────────────────────────
|
||||
01-26 → 02-01 196 1.3M 59.2K 1.2M 95.6%
|
||||
────────────────────────────────────────────────────────────────────────
|
||||
TOTAL 196 1.3M 59.2K 1.2M 95.6%
|
||||
```
|
||||
|
||||
**Week definition**: Sunday to Saturday (ISO week starting Sunday at 00:00)
|
||||
|
||||
### Monthly Breakdown
|
||||
|
||||
```
|
||||
📆 Monthly Breakdown (1 months)
|
||||
════════════════════════════════════════════════════════════════
|
||||
Month Cmds Input Output Saved Save%
|
||||
────────────────────────────────────────────────────────────────
|
||||
2026-01 196 1.3M 59.2K 1.2M 95.6%
|
||||
────────────────────────────────────────────────────────────────
|
||||
TOTAL 196 1.3M 59.2K 1.2M 95.6%
|
||||
```
|
||||
|
||||
**Month format**: YYYY-MM (calendar month)
|
||||
|
||||
### JSON Export
|
||||
|
||||
```json
|
||||
{
|
||||
"summary": {
|
||||
"total_commands": 196,
|
||||
"total_input": 1276098,
|
||||
"total_output": 59244,
|
||||
"total_saved": 1220217,
|
||||
"avg_savings_pct": 95.62
|
||||
},
|
||||
"daily": [
|
||||
{
|
||||
"date": "2026-01-28",
|
||||
"commands": 89,
|
||||
"input_tokens": 380894,
|
||||
"output_tokens": 26744,
|
||||
"saved_tokens": 355779,
|
||||
"savings_pct": 93.41
|
||||
}
|
||||
],
|
||||
"weekly": [...],
|
||||
"monthly": [...]
|
||||
}
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- API integration
|
||||
- Custom dashboards
|
||||
- Automated reporting
|
||||
- Data pipeline ingestion
|
||||
|
||||
### CSV Export
|
||||
|
||||
```csv
|
||||
# Daily Data
|
||||
date,commands,input_tokens,output_tokens,saved_tokens,savings_pct
|
||||
2026-01-28,89,380894,26744,355779,93.41
|
||||
2026-01-29,102,894455,32445,863744,96.57
|
||||
|
||||
# Weekly Data
|
||||
week_start,week_end,commands,input_tokens,output_tokens,saved_tokens,savings_pct
|
||||
2026-01-26,2026-02-01,196,1276098,59244,1220217,95.62
|
||||
|
||||
# Monthly Data
|
||||
month,commands,input_tokens,output_tokens,saved_tokens,savings_pct
|
||||
2026-01,196,1276098,59244,1220217,95.62
|
||||
```
|
||||
|
||||
**Use cases:**
|
||||
- Excel analysis
|
||||
- Python/R data science
|
||||
- Google Sheets dashboards
|
||||
- Matplotlib/seaborn plotting
|
||||
|
||||
## Analysis Workflows
|
||||
|
||||
### Weekly Progress Tracking
|
||||
|
||||
```bash
|
||||
# Generate weekly report every Monday
|
||||
rtk gain --weekly --format csv > reports/week-$(date +%Y-%W).csv
|
||||
|
||||
# Compare this week vs last week
|
||||
rtk gain --weekly | tail -3
|
||||
```
|
||||
|
||||
### Monthly Cost Analysis
|
||||
|
||||
```bash
|
||||
# Export monthly data for budget review
|
||||
rtk gain --monthly --format json | jq '.monthly[] |
|
||||
{month, saved_tokens, quota_pct: (.saved_tokens / 6000000 * 100)}'
|
||||
```
|
||||
|
||||
### Data Science Analysis
|
||||
|
||||
```python
|
||||
import pandas as pd
|
||||
import subprocess
|
||||
|
||||
# Get CSV data
|
||||
result = subprocess.run(['rtk', 'gain', '--all', '--format', 'csv'],
|
||||
capture_output=True, text=True)
|
||||
|
||||
# Parse daily data
|
||||
lines = result.stdout.split('\n')
|
||||
daily_start = lines.index('# Daily Data') + 2
|
||||
daily_end = lines.index('', daily_start)
|
||||
daily_df = pd.read_csv(pd.StringIO('\n'.join(lines[daily_start:daily_end])))
|
||||
|
||||
# Plot savings trend
|
||||
daily_df['date'] = pd.to_datetime(daily_df['date'])
|
||||
daily_df.plot(x='date', y='savings_pct', kind='line')
|
||||
```
|
||||
|
||||
### Excel Analysis
|
||||
|
||||
1. Export CSV: `rtk gain --all --format csv > rtk-data.csv`
|
||||
2. Open in Excel
|
||||
3. Create pivot tables:
|
||||
- Daily trends (line chart)
|
||||
- Weekly totals (bar chart)
|
||||
- Savings % distribution (histogram)
|
||||
|
||||
### Dashboard Creation
|
||||
|
||||
```bash
|
||||
# Generate dashboard data daily via cron
|
||||
0 0 * * * rtk gain --all --format json > /var/www/dashboard/rtk-stats.json
|
||||
|
||||
# Serve with static site
|
||||
cat > index.html <<'EOF'
|
||||
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
|
||||
<canvas id="savings"></canvas>
|
||||
<script>
|
||||
fetch('rtk-stats.json')
|
||||
.then(r => r.json())
|
||||
.then(data => {
|
||||
new Chart(document.getElementById('savings'), {
|
||||
type: 'line',
|
||||
data: {
|
||||
labels: data.daily.map(d => d.date),
|
||||
datasets: [{
|
||||
label: 'Daily Savings %',
|
||||
data: data.daily.map(d => d.savings_pct)
|
||||
}]
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
EOF
|
||||
```
|
||||
|
||||
## Understanding Token Savings
|
||||
|
||||
### Token Estimation
|
||||
|
||||
rtk estimates tokens using `text.len() / 4` (4 characters per token average).
|
||||
|
||||
**Accuracy**: ±10% compared to actual LLM tokenization (sufficient for trends).
|
||||
|
||||
### Savings Calculation
|
||||
|
||||
```
|
||||
Input Tokens = estimate_tokens(raw_command_output)
|
||||
Output Tokens = estimate_tokens(rtk_filtered_output)
|
||||
Saved Tokens = Input - Output
|
||||
Savings % = (Saved / Input) × 100
|
||||
```
|
||||
|
||||
### Typical Savings by Command
|
||||
|
||||
| Command | Typical Savings | Mechanism |
|
||||
|---------|----------------|-----------|
|
||||
| `rtk git status` | 77-93% | Compact stat format |
|
||||
| `rtk eslint` | 84% | Group by rule |
|
||||
| `rtk jest` | 94-99% | Show failures only |
|
||||
| `rtk vitest` | 94-99% | Show failures only |
|
||||
| `rtk find` | 75% | Tree format |
|
||||
| `rtk pnpm list` | 70-90% | Compact dependencies |
|
||||
| `rtk grep` | 70% | Truncate + group |
|
||||
|
||||
## Database Management
|
||||
|
||||
### Inspect Raw Data
|
||||
|
||||
```bash
|
||||
# Location
|
||||
ls -lh ~/.local/share/rtk/history.db
|
||||
|
||||
# Schema
|
||||
sqlite3 ~/.local/share/rtk/history.db ".schema"
|
||||
|
||||
# Recent records
|
||||
sqlite3 ~/.local/share/rtk/history.db \
|
||||
"SELECT timestamp, rtk_cmd, saved_tokens FROM commands
|
||||
ORDER BY timestamp DESC LIMIT 10"
|
||||
|
||||
# Total database size
|
||||
sqlite3 ~/.local/share/rtk/history.db \
|
||||
"SELECT COUNT(*),
|
||||
SUM(saved_tokens) as total_saved,
|
||||
MIN(DATE(timestamp)) as first_record,
|
||||
MAX(DATE(timestamp)) as last_record
|
||||
FROM commands"
|
||||
```
|
||||
|
||||
### Backup & Restore
|
||||
|
||||
```bash
|
||||
# Backup
|
||||
cp ~/.local/share/rtk/history.db ~/backups/rtk-history-$(date +%Y%m%d).db
|
||||
|
||||
# Restore
|
||||
cp ~/backups/rtk-history-20260128.db ~/.local/share/rtk/history.db
|
||||
|
||||
# Export for migration
|
||||
sqlite3 ~/.local/share/rtk/history.db .dump > rtk-backup.sql
|
||||
```
|
||||
|
||||
### Cleanup
|
||||
|
||||
```bash
|
||||
# Manual cleanup (older than 90 days)
|
||||
sqlite3 ~/.local/share/rtk/history.db \
|
||||
"DELETE FROM commands WHERE timestamp < datetime('now', '-90 days')"
|
||||
|
||||
# Reset all data
|
||||
rm ~/.local/share/rtk/history.db
|
||||
# Next rtk command will recreate database
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### GitHub Actions CI/CD
|
||||
|
||||
```yaml
|
||||
# .github/workflows/rtk-stats.yml
|
||||
name: RTK Stats Report
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1' # Weekly on Monday
|
||||
jobs:
|
||||
stats:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Install rtk
|
||||
run: cargo install --path .
|
||||
- name: Generate report
|
||||
run: |
|
||||
rtk gain --weekly --format json > stats/week-$(date +%Y-%W).json
|
||||
- name: Commit stats
|
||||
run: |
|
||||
git add stats/
|
||||
git commit -m "Weekly rtk stats"
|
||||
git push
|
||||
```
|
||||
|
||||
### Slack Bot
|
||||
|
||||
```python
|
||||
import subprocess
|
||||
import json
|
||||
import requests
|
||||
|
||||
def send_rtk_stats():
|
||||
result = subprocess.run(['rtk', 'gain', '--format', 'json'],
|
||||
capture_output=True, text=True)
|
||||
data = json.loads(result.stdout)
|
||||
|
||||
message = f"""
|
||||
📊 *RTK Token Savings Report*
|
||||
|
||||
Total Saved: {data['summary']['total_saved']:,} tokens
|
||||
Savings Rate: {data['summary']['avg_savings_pct']:.1f}%
|
||||
Commands: {data['summary']['total_commands']}
|
||||
"""
|
||||
|
||||
requests.post(SLACK_WEBHOOK_URL, json={'text': message})
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### No data showing
|
||||
|
||||
```bash
|
||||
# Check if database exists
|
||||
ls -lh ~/.local/share/rtk/history.db
|
||||
|
||||
# Check record count
|
||||
sqlite3 ~/.local/share/rtk/history.db "SELECT COUNT(*) FROM commands"
|
||||
|
||||
# Run a tracked command to generate data
|
||||
rtk git status
|
||||
```
|
||||
|
||||
### Export fails
|
||||
|
||||
```bash
|
||||
# Check for pipe errors
|
||||
rtk gain --format json 2>&1 | tee /tmp/rtk-debug.log | jq .
|
||||
|
||||
# Use release build to avoid warnings
|
||||
cargo build --release
|
||||
./target/release/rtk gain --format json
|
||||
```
|
||||
|
||||
### Incorrect statistics
|
||||
|
||||
Token estimation is a heuristic. For precise measurements:
|
||||
|
||||
```bash
|
||||
# Install tiktoken
|
||||
pip install tiktoken
|
||||
|
||||
# Validate estimation
|
||||
rtk git status > output.txt
|
||||
python -c "
|
||||
import tiktoken
|
||||
enc = tiktoken.get_encoding('cl100k_base')
|
||||
text = open('output.txt').read()
|
||||
print(f'Actual tokens: {len(enc.encode(text))}')
|
||||
print(f'rtk estimate: {len(text) // 4}')
|
||||
"
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
1. **Regular Exports**: `rtk gain --all --format json > monthly-$(date +%Y%m).json`
|
||||
2. **Trend Analysis**: Compare week-over-week savings to identify optimization opportunities
|
||||
3. **Command Profiling**: Use `--history` to see which commands save the most
|
||||
4. **Backup Before Cleanup**: Always backup before manual database operations
|
||||
5. **CI Integration**: Track savings across team in shared dashboards
|
||||
|
||||
## See Also
|
||||
|
||||
- [README.md](../README.md) - Full rtk documentation
|
||||
- [CLAUDE.md](../CLAUDE.md) - Claude Code integration guide
|
||||
- [ARCHITECTURE.md](../contributing/ARCHITECTURE.md) - Technical architecture
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,583 @@
|
||||
# RTK Tracking API Documentation
|
||||
|
||||
Comprehensive documentation for RTK's token savings tracking system.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Overview](#overview)
|
||||
- [Architecture](#architecture)
|
||||
- [Public API](#public-api)
|
||||
- [Usage Examples](#usage-examples)
|
||||
- [Data Formats](#data-formats)
|
||||
- [Integration Examples](#integration-examples)
|
||||
- [Database Schema](#database-schema)
|
||||
|
||||
## Overview
|
||||
|
||||
RTK's tracking system records every command execution to provide analytics on token savings. The system:
|
||||
- Stores command history in SQLite (~/.local/share/rtk/tracking.db)
|
||||
- Tracks input/output tokens, savings percentage, and execution time
|
||||
- Automatically cleans up records older than 90 days
|
||||
- Provides aggregation APIs (daily/weekly/monthly)
|
||||
- Exports to JSON/CSV for external integrations
|
||||
|
||||
## Architecture
|
||||
|
||||
### Data Flow
|
||||
|
||||
```
|
||||
rtk command execution
|
||||
↓
|
||||
TimedExecution::start()
|
||||
↓
|
||||
[command runs]
|
||||
↓
|
||||
TimedExecution::track(original_cmd, rtk_cmd, input, output)
|
||||
↓
|
||||
Tracker::record(original_cmd, rtk_cmd, input_tokens, output_tokens, exec_time_ms)
|
||||
↓
|
||||
SQLite database (~/.local/share/rtk/tracking.db)
|
||||
↓
|
||||
Aggregation APIs (get_summary, get_all_days, etc.)
|
||||
↓
|
||||
CLI output (rtk gain) or JSON/CSV export
|
||||
```
|
||||
|
||||
### Storage Location
|
||||
|
||||
- **Linux**: `~/.local/share/rtk/tracking.db`
|
||||
- **macOS**: `~/Library/Application Support/rtk/tracking.db`
|
||||
- **Windows**: `%APPDATA%\rtk\tracking.db`
|
||||
|
||||
### Data Retention
|
||||
|
||||
Records older than **90 days** are automatically deleted on each write operation to prevent unbounded database growth.
|
||||
|
||||
## Public API
|
||||
|
||||
### Core Types
|
||||
|
||||
#### `Tracker`
|
||||
|
||||
Main tracking interface for recording and querying command history.
|
||||
|
||||
```rust
|
||||
pub struct Tracker {
|
||||
conn: Connection, // SQLite connection
|
||||
}
|
||||
|
||||
impl Tracker {
|
||||
/// Create new tracker instance (opens/creates database)
|
||||
pub fn new() -> Result<Self>;
|
||||
|
||||
/// Record a command execution
|
||||
pub fn record(
|
||||
&self,
|
||||
original_cmd: &str, // Standard command (e.g., "ls -la")
|
||||
rtk_cmd: &str, // RTK command (e.g., "rtk ls")
|
||||
input_tokens: usize, // Estimated input tokens
|
||||
output_tokens: usize, // Actual output tokens
|
||||
exec_time_ms: u64, // Execution time in milliseconds
|
||||
) -> Result<()>;
|
||||
|
||||
/// Get overall summary statistics
|
||||
pub fn get_summary(&self) -> Result<GainSummary>;
|
||||
|
||||
/// Get daily statistics (all days)
|
||||
pub fn get_all_days(&self) -> Result<Vec<DayStats>>;
|
||||
|
||||
/// Get weekly statistics (grouped by week)
|
||||
pub fn get_by_week(&self) -> Result<Vec<WeekStats>>;
|
||||
|
||||
/// Get monthly statistics (grouped by month)
|
||||
pub fn get_by_month(&self) -> Result<Vec<MonthStats>>;
|
||||
|
||||
/// Get recent command history (limit = max records)
|
||||
pub fn get_recent(&self, limit: usize) -> Result<Vec<CommandRecord>>;
|
||||
}
|
||||
```
|
||||
|
||||
#### `GainSummary`
|
||||
|
||||
Aggregated statistics across all recorded commands.
|
||||
|
||||
```rust
|
||||
pub struct GainSummary {
|
||||
pub total_commands: usize, // Total commands recorded
|
||||
pub total_input: usize, // Total input tokens
|
||||
pub total_output: usize, // Total output tokens
|
||||
pub total_saved: usize, // Total tokens saved
|
||||
pub avg_savings_pct: f64, // Average savings percentage
|
||||
pub total_time_ms: u64, // Total execution time (ms)
|
||||
pub avg_time_ms: u64, // Average execution time (ms)
|
||||
pub by_command: Vec<(String, usize, usize, f64, u64)>, // Top 10 commands
|
||||
pub by_day: Vec<(String, usize)>, // Last 30 days
|
||||
}
|
||||
```
|
||||
|
||||
#### `DayStats`
|
||||
|
||||
Daily statistics (Serializable for JSON export).
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct DayStats {
|
||||
pub date: String, // ISO date (YYYY-MM-DD)
|
||||
pub commands: usize, // Commands executed this day
|
||||
pub input_tokens: usize, // Total input tokens
|
||||
pub output_tokens: usize, // Total output tokens
|
||||
pub saved_tokens: usize, // Total tokens saved
|
||||
pub savings_pct: f64, // Savings percentage
|
||||
pub total_time_ms: u64, // Total execution time (ms)
|
||||
pub avg_time_ms: u64, // Average execution time (ms)
|
||||
}
|
||||
```
|
||||
|
||||
#### `WeekStats`
|
||||
|
||||
Weekly statistics (Serializable for JSON export).
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct WeekStats {
|
||||
pub week_start: String, // ISO date (YYYY-MM-DD)
|
||||
pub week_end: String, // ISO date (YYYY-MM-DD)
|
||||
pub commands: usize,
|
||||
pub input_tokens: usize,
|
||||
pub output_tokens: usize,
|
||||
pub saved_tokens: usize,
|
||||
pub savings_pct: f64,
|
||||
pub total_time_ms: u64,
|
||||
pub avg_time_ms: u64,
|
||||
}
|
||||
```
|
||||
|
||||
#### `MonthStats`
|
||||
|
||||
Monthly statistics (Serializable for JSON export).
|
||||
|
||||
```rust
|
||||
#[derive(Debug, Serialize)]
|
||||
pub struct MonthStats {
|
||||
pub month: String, // YYYY-MM format
|
||||
pub commands: usize,
|
||||
pub input_tokens: usize,
|
||||
pub output_tokens: usize,
|
||||
pub saved_tokens: usize,
|
||||
pub savings_pct: f64,
|
||||
pub total_time_ms: u64,
|
||||
pub avg_time_ms: u64,
|
||||
}
|
||||
```
|
||||
|
||||
#### `CommandRecord`
|
||||
|
||||
Individual command record from history.
|
||||
|
||||
```rust
|
||||
pub struct CommandRecord {
|
||||
pub timestamp: DateTime<Utc>, // UTC timestamp
|
||||
pub rtk_cmd: String, // RTK command used
|
||||
pub saved_tokens: usize, // Tokens saved
|
||||
pub savings_pct: f64, // Savings percentage
|
||||
}
|
||||
```
|
||||
|
||||
#### `TimedExecution`
|
||||
|
||||
Helper for timing command execution (preferred API).
|
||||
|
||||
```rust
|
||||
pub struct TimedExecution {
|
||||
start: Instant,
|
||||
}
|
||||
|
||||
impl TimedExecution {
|
||||
/// Start timing a command execution
|
||||
pub fn start() -> Self;
|
||||
|
||||
/// Track command with elapsed time
|
||||
pub fn track(&self, original_cmd: &str, rtk_cmd: &str, input: &str, output: &str);
|
||||
|
||||
/// Track passthrough commands (timing-only, no token counting)
|
||||
pub fn track_passthrough(&self, original_cmd: &str, rtk_cmd: &str);
|
||||
}
|
||||
```
|
||||
|
||||
### Utility Functions
|
||||
|
||||
```rust
|
||||
/// Estimate token count (~4 chars = 1 token)
|
||||
pub fn estimate_tokens(text: &str) -> usize;
|
||||
|
||||
/// Format OsString args for display
|
||||
pub fn args_display(args: &[OsString]) -> String;
|
||||
|
||||
/// Legacy tracking function (deprecated, use TimedExecution)
|
||||
#[deprecated(note = "Use TimedExecution instead")]
|
||||
pub fn track(original_cmd: &str, rtk_cmd: &str, input: &str, output: &str);
|
||||
```
|
||||
|
||||
## Usage Examples
|
||||
|
||||
### Basic Tracking
|
||||
|
||||
```rust
|
||||
use rtk::tracking::{TimedExecution, Tracker};
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
// Start timer
|
||||
let timer = TimedExecution::start();
|
||||
|
||||
// Execute command
|
||||
let input = execute_original_command()?;
|
||||
let output = execute_rtk_command()?;
|
||||
|
||||
// Track execution
|
||||
timer.track("ls -la", "rtk ls", &input, &output);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Querying Statistics
|
||||
|
||||
```rust
|
||||
use rtk::tracking::Tracker;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let tracker = Tracker::new()?;
|
||||
|
||||
// Get overall summary
|
||||
let summary = tracker.get_summary()?;
|
||||
println!("Total commands: {}", summary.total_commands);
|
||||
println!("Total saved: {} tokens", summary.total_saved);
|
||||
println!("Average savings: {:.1}%", summary.avg_savings_pct);
|
||||
|
||||
// Get daily breakdown
|
||||
let days = tracker.get_all_days()?;
|
||||
for day in days.iter().take(7) {
|
||||
println!("{}: {} commands, {} tokens saved",
|
||||
day.date, day.commands, day.saved_tokens);
|
||||
}
|
||||
|
||||
// Get recent history
|
||||
let recent = tracker.get_recent(10)?;
|
||||
for cmd in recent {
|
||||
println!("{}: {} saved {:.1}%",
|
||||
cmd.timestamp, cmd.rtk_cmd, cmd.savings_pct);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Passthrough Commands
|
||||
|
||||
For commands that stream output or run interactively (no output capture):
|
||||
|
||||
```rust
|
||||
use rtk::tracking::TimedExecution;
|
||||
|
||||
fn main() -> anyhow::Result<()> {
|
||||
let timer = TimedExecution::start();
|
||||
|
||||
// Execute streaming command (e.g., git tag --list)
|
||||
execute_streaming_command()?;
|
||||
|
||||
// Track timing only (input_tokens=0, output_tokens=0)
|
||||
timer.track_passthrough("git tag --list", "rtk git tag --list");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Data Formats
|
||||
|
||||
### JSON Export Schema
|
||||
|
||||
#### DayStats JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"date": "2026-02-03",
|
||||
"commands": 42,
|
||||
"input_tokens": 15420,
|
||||
"output_tokens": 3842,
|
||||
"saved_tokens": 11578,
|
||||
"savings_pct": 75.08,
|
||||
"total_time_ms": 8450,
|
||||
"avg_time_ms": 201
|
||||
}
|
||||
```
|
||||
|
||||
#### WeekStats JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"week_start": "2026-01-27",
|
||||
"week_end": "2026-02-02",
|
||||
"commands": 284,
|
||||
"input_tokens": 98234,
|
||||
"output_tokens": 19847,
|
||||
"saved_tokens": 78387,
|
||||
"savings_pct": 79.80,
|
||||
"total_time_ms": 56780,
|
||||
"avg_time_ms": 200
|
||||
}
|
||||
```
|
||||
|
||||
#### MonthStats JSON
|
||||
|
||||
```json
|
||||
{
|
||||
"month": "2026-02",
|
||||
"commands": 1247,
|
||||
"input_tokens": 456789,
|
||||
"output_tokens": 91358,
|
||||
"saved_tokens": 365431,
|
||||
"savings_pct": 80.00,
|
||||
"total_time_ms": 249560,
|
||||
"avg_time_ms": 200
|
||||
}
|
||||
```
|
||||
|
||||
### CSV Export Schema
|
||||
|
||||
```csv
|
||||
date,commands,input_tokens,output_tokens,saved_tokens,savings_pct,total_time_ms,avg_time_ms
|
||||
2026-02-03,42,15420,3842,11578,75.08,8450,201
|
||||
2026-02-02,38,14230,3557,10673,75.00,7600,200
|
||||
2026-02-01,45,16890,4223,12667,75.00,9000,200
|
||||
```
|
||||
|
||||
## Integration Examples
|
||||
|
||||
### GitHub Actions - Track Savings in CI
|
||||
|
||||
```yaml
|
||||
# .github/workflows/track-rtk-savings.yml
|
||||
name: Track RTK Savings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 0 * * 1' # Weekly on Monday
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
track-savings:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Install RTK
|
||||
run: cargo install --git https://github.com/rtk-ai/rtk
|
||||
|
||||
- name: Export weekly stats
|
||||
run: |
|
||||
rtk gain --weekly --format json > rtk-weekly.json
|
||||
cat rtk-weekly.json
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v3
|
||||
with:
|
||||
name: rtk-metrics
|
||||
path: rtk-weekly.json
|
||||
|
||||
- name: Post to Slack
|
||||
if: success()
|
||||
env:
|
||||
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
|
||||
run: |
|
||||
SAVINGS=$(jq -r '.[0].saved_tokens' rtk-weekly.json)
|
||||
PCT=$(jq -r '.[0].savings_pct' rtk-weekly.json)
|
||||
curl -X POST -H 'Content-type: application/json' \
|
||||
--data "{\"text\":\"📊 RTK Weekly: ${SAVINGS} tokens saved (${PCT}%)\"}" \
|
||||
$SLACK_WEBHOOK
|
||||
```
|
||||
|
||||
### Custom Dashboard Script
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Export RTK metrics to Grafana/Datadog/etc.
|
||||
"""
|
||||
import json
|
||||
import subprocess
|
||||
from datetime import datetime
|
||||
|
||||
def get_rtk_metrics():
|
||||
"""Fetch RTK metrics as JSON."""
|
||||
result = subprocess.run(
|
||||
["rtk", "gain", "--all", "--format", "json"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
return json.loads(result.stdout)
|
||||
|
||||
def export_to_datadog(metrics):
|
||||
"""Send metrics to Datadog."""
|
||||
import datadog
|
||||
|
||||
datadog.initialize(api_key="YOUR_API_KEY")
|
||||
|
||||
for day in metrics.get("daily", []):
|
||||
datadog.api.Metric.send(
|
||||
metric="rtk.tokens_saved",
|
||||
points=[(datetime.now().timestamp(), day["saved_tokens"])],
|
||||
tags=[f"date:{day['date']}"]
|
||||
)
|
||||
|
||||
datadog.api.Metric.send(
|
||||
metric="rtk.savings_pct",
|
||||
points=[(datetime.now().timestamp(), day["savings_pct"])],
|
||||
tags=[f"date:{day['date']}"]
|
||||
)
|
||||
|
||||
if __name__ == "__main__":
|
||||
metrics = get_rtk_metrics()
|
||||
export_to_datadog(metrics)
|
||||
print(f"Exported {len(metrics.get('daily', []))} days to Datadog")
|
||||
```
|
||||
|
||||
### Rust Integration (Using RTK as Library)
|
||||
|
||||
```rust
|
||||
// In your Cargo.toml
|
||||
// [dependencies]
|
||||
// rtk = { git = "https://github.com/rtk-ai/rtk" }
|
||||
|
||||
use rtk::tracking::{Tracker, TimedExecution};
|
||||
use anyhow::Result;
|
||||
|
||||
fn main() -> Result<()> {
|
||||
// Track your own commands
|
||||
let timer = TimedExecution::start();
|
||||
|
||||
let input = run_expensive_operation()?;
|
||||
let output = run_optimized_operation()?;
|
||||
|
||||
timer.track(
|
||||
"expensive_operation",
|
||||
"optimized_operation",
|
||||
&input,
|
||||
&output
|
||||
);
|
||||
|
||||
// Query aggregated stats
|
||||
let tracker = Tracker::new()?;
|
||||
let summary = tracker.get_summary()?;
|
||||
|
||||
println!("Total savings: {} tokens ({:.1}%)",
|
||||
summary.total_saved,
|
||||
summary.avg_savings_pct
|
||||
);
|
||||
|
||||
// Export to JSON for external tools
|
||||
let days = tracker.get_all_days()?;
|
||||
let json = serde_json::to_string_pretty(&days)?;
|
||||
std::fs::write("metrics.json", json)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
## Database Schema
|
||||
|
||||
### Table: `commands`
|
||||
|
||||
```sql
|
||||
CREATE TABLE commands (
|
||||
id INTEGER PRIMARY KEY,
|
||||
timestamp TEXT NOT NULL, -- RFC3339 UTC timestamp
|
||||
original_cmd TEXT NOT NULL, -- Original command (e.g., "ls -la")
|
||||
rtk_cmd TEXT NOT NULL, -- RTK command (e.g., "rtk ls")
|
||||
input_tokens INTEGER NOT NULL, -- Estimated input tokens
|
||||
output_tokens INTEGER NOT NULL, -- Actual output tokens
|
||||
saved_tokens INTEGER NOT NULL, -- input_tokens - output_tokens
|
||||
savings_pct REAL NOT NULL, -- (saved/input) * 100
|
||||
exec_time_ms INTEGER DEFAULT 0 -- Execution time in milliseconds
|
||||
);
|
||||
|
||||
CREATE INDEX idx_timestamp ON commands(timestamp);
|
||||
```
|
||||
|
||||
### Automatic Cleanup
|
||||
|
||||
On every write operation (`Tracker::record`), records older than 90 days are deleted:
|
||||
|
||||
```rust
|
||||
fn cleanup_old(&self) -> Result<()> {
|
||||
let cutoff = Utc::now() - chrono::Duration::days(90);
|
||||
self.conn.execute(
|
||||
"DELETE FROM commands WHERE timestamp < ?1",
|
||||
params![cutoff.to_rfc3339()],
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
```
|
||||
|
||||
### Migration Support
|
||||
|
||||
The system automatically adds new columns if they don't exist (e.g., `exec_time_ms` was added later):
|
||||
|
||||
```rust
|
||||
// Safe migration on Tracker::new()
|
||||
let _ = conn.execute(
|
||||
"ALTER TABLE commands ADD COLUMN exec_time_ms INTEGER DEFAULT 0",
|
||||
[],
|
||||
);
|
||||
```
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **SQLite WAL mode**: Not enabled (may add in future for concurrent writes)
|
||||
- **Index on timestamp**: Enables fast date-range queries
|
||||
- **Automatic cleanup**: Prevents database from growing unbounded
|
||||
- **Token estimation**: ~4 chars = 1 token (simple, fast approximation)
|
||||
- **Aggregation queries**: Use SQL GROUP BY for efficient aggregation
|
||||
|
||||
## Security & Privacy
|
||||
|
||||
- **Local storage only**: Tracking database never leaves the machine
|
||||
- **Telemetry requires consent**: RTK can send a daily anonymous usage ping (version, OS, command counts, token savings). Disabled by default, requires explicit consent via `rtk init` or `rtk telemetry enable`. Manage with `rtk telemetry status/disable/forget`. Override: `RTK_TELEMETRY_DISABLED=1`
|
||||
- **User control**: Users can delete `~/.local/share/rtk/tracking.db` anytime
|
||||
- **90-day retention**: Old data automatically purged
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Database locked error
|
||||
|
||||
If you see "database is locked" errors:
|
||||
- Ensure only one RTK process writes at a time
|
||||
- Check file permissions on `~/.local/share/rtk/tracking.db`
|
||||
- Delete and recreate: `rm ~/.local/share/rtk/tracking.db && rtk gain`
|
||||
|
||||
### Missing exec_time_ms column
|
||||
|
||||
Older databases may not have the `exec_time_ms` column. RTK automatically migrates on first use, but you can force it:
|
||||
|
||||
```bash
|
||||
sqlite3 ~/.local/share/rtk/tracking.db \
|
||||
"ALTER TABLE commands ADD COLUMN exec_time_ms INTEGER DEFAULT 0"
|
||||
```
|
||||
|
||||
### Incorrect token counts
|
||||
|
||||
Token estimation uses `~4 chars = 1 token`. This is approximate. For precise counts, integrate with your LLM's tokenizer API.
|
||||
|
||||
## Future Enhancements
|
||||
|
||||
Planned improvements (contributions welcome):
|
||||
|
||||
- [ ] Export to Prometheus/OpenMetrics format
|
||||
- [ ] Support for custom retention periods (not just 90 days)
|
||||
- [ ] SQLite WAL mode for concurrent writes
|
||||
- [ ] Per-project tracking (multiple databases)
|
||||
- [ ] Integration with Claude API for precise token counts
|
||||
- [ ] Web dashboard (localhost) for visualizing trends
|
||||
|
||||
## See Also
|
||||
|
||||
- [README.md](../README.md) - Main project documentation
|
||||
- [COMMAND_AUDIT.md](../claudedocs/COMMAND_AUDIT.md) - List of all RTK commands
|
||||
- [Rust docs](https://docs.rs/) - Run `cargo doc --open` for API docs
|
||||
Reference in New Issue
Block a user