From e6b87d4eb28b9a6349ad7d805edbb5fd723cb89c Mon Sep 17 00:00:00 2001 From: Tuan Tran Date: Fri, 3 Apr 2026 14:17:27 +0700 Subject: [PATCH 01/13] fix: fix ls -la output parsing --- src/cmds/system/ls.rs | 167 +++++++++++++++++++++++++++++++++++++++--- 1 file changed, 155 insertions(+), 12 deletions(-) diff --git a/src/cmds/system/ls.rs b/src/cmds/system/ls.rs index a69c271fe..ac40ae328 100644 --- a/src/cmds/system/ls.rs +++ b/src/cmds/system/ls.rs @@ -4,8 +4,20 @@ use super::constants::NOISE_DIRS; use crate::core::runner::{self, RunOptions}; use crate::core::utils::resolved_command; use anyhow::Result; +use lazy_static::lazy_static; +use regex::Regex; use std::io::IsTerminal; +lazy_static! { + /// Matches the date+time portion in `ls -la` output, which serves as a + /// stable anchor regardless of owner/group column width. + /// E.g.: " Mar 31 16:18 " or " Dec 25 2024 " + static ref LS_DATE_RE: Regex = Regex::new( + r"\s+(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)\s+\d{1,2}\s+(?:\d{4}|\d{2}:\d{2})\s+" + ) + .unwrap(); +} + pub fn run(args: &[String], verbose: u8) -> Result { let show_all = args .iter() @@ -101,6 +113,40 @@ fn human_size(bytes: u64) -> String { } } +/// Parse a single `ls -la` line, returning `(file_type_char, size, name)`. +/// +/// Uses the date field as a stable anchor — the date format in `ls -la` is +/// always three tokens (`Mon DD HH:MM` or `Mon DD YYYY`), so we locate it +/// with a regex, then extract size (rightmost number before the date) and +/// filename (everything after the date). This handles owner/group names that +/// contain spaces, which break the old fixed-column approach. +fn parse_ls_line(line: &str) -> Option<(char, u64, String)> { + let date_match = LS_DATE_RE.find(line)?; + let name = line[date_match.end()..].to_string(); + + let before_date = &line[..date_match.start()]; + let before_parts: Vec<&str> = before_date.split_whitespace().collect(); + if before_parts.len() < 4 { + return None; + } + + let perms = before_parts[0]; + let file_type = perms.chars().next()?; + + // Size is the rightmost parseable number before the date. + // nlinks is also numeric but appears earlier; scanning from the end + // guarantees we hit the size field first. + let mut size: u64 = 0; + for part in before_parts.iter().rev() { + if let Ok(s) = part.parse::() { + size = s; + break; + } + } + + Some((file_type, size, name)) +} + /// Parse ls -la output into compact format: /// name/ (dirs) /// name size (files) @@ -113,18 +159,13 @@ fn compact_ls(raw: &str, show_all: bool) -> (String, String) { let mut by_ext: HashMap = HashMap::new(); for line in raw.lines() { - // Skip total, empty, . and .. if line.starts_with("total ") || line.is_empty() { continue; } - let parts: Vec<&str> = line.split_whitespace().collect(); - if parts.len() < 9 { + let Some((file_type, size, name)) = parse_ls_line(line) else { continue; - } - - // Filename is everything from column 9 onward (handles spaces) - let name = parts[8..].join(" "); + }; // Skip . and .. if name == "." || name == ".." { @@ -136,12 +177,9 @@ fn compact_ls(raw: &str, show_all: bool) -> (String, String) { continue; } - let is_dir = parts[0].starts_with('d'); - - if is_dir { + if file_type == 'd' { dirs.push(name); - } else if parts[0].starts_with('-') || parts[0].starts_with('l') { - let size: u64 = parts[4].parse().unwrap_or(0); + } else if file_type == '-' || file_type == 'l' { let ext = if let Some(pos) = name.rfind('.') { name[pos..].to_string() } else { @@ -325,4 +363,109 @@ mod tests { line_count ); } + + // Regression test for #948: owner/group with spaces breaks fixed-column parsing + #[test] + fn test_compact_multiline_group() { + let input = "total 8\n\ + -rw-r--r-- 1 fjeanne utilisa. du domaine 0 Mar 31 16:18 empty.txt\n\ + -rw-r--r-- 1 fjeanne utilisa. du domaine 1234 Mar 31 16:18 data.json\n"; + let (entries, _summary) = compact_ls(input, false); + assert!( + entries.contains("empty.txt"), + "should contain 'empty.txt', got: {entries}" + ); + assert!( + entries.contains("data.json"), + "should contain 'data.json', got: {entries}" + ); + assert!( + !entries.contains("16:18"), + "time should not leak into filename, got: {entries}" + ); + assert!( + entries.contains("0B"), + "empty.txt should show 0B, got: {entries}" + ); + assert!( + entries.contains("1.2K"), + "data.json should show 1.2K (1234 bytes), got: {entries}" + ); + } + + #[test] + fn test_compact_year_format_date() { + // Some systems show year instead of time for old files + let input = "total 8\n\ + -rw-r--r-- 1 user staff 5678 Dec 25 2024 archive.tar\n"; + let (entries, _summary) = compact_ls(input, false); + assert!( + entries.contains("archive.tar"), + "should contain filename, got: {entries}" + ); + assert!( + entries.contains("5.5K"), + "should show 5.5K, got: {entries}" + ); + } + + #[test] + fn test_parse_ls_line_basic() { + let (ft, size, name) = parse_ls_line( + "-rw-r--r-- 1 user staff 1234 Jan 1 12:00 file.txt", + ) + .unwrap(); + assert_eq!(ft, '-'); + assert_eq!(size, 1234); + assert_eq!(name, "file.txt"); + } + + #[test] + fn test_parse_ls_line_multiline_group() { + let (ft, size, name) = parse_ls_line( + "-rw-r--r-- 1 fjeanne utilisa. du domaine 0 Mar 31 16:18 empty.txt", + ) + .unwrap(); + assert_eq!(ft, '-'); + assert_eq!(size, 0); + assert_eq!(name, "empty.txt"); + } + + #[test] + fn test_parse_ls_line_dir_with_space_in_group() { + let (ft, size, name) = parse_ls_line( + "drwxr-xr-x 2 fjeanne utilisa. du domaine 64 Mar 31 16:18 my dir", + ) + .unwrap(); + assert_eq!(ft, 'd'); + assert_eq!(size, 64); + assert_eq!(name, "my dir"); + } + + #[test] + fn test_parse_ls_line_symlink() { + let (ft, size, name) = parse_ls_line( + "lrwxr-xr-x 1 user staff 10 Jan 1 12:00 link -> target", + ) + .unwrap(); + assert_eq!(ft, 'l'); + assert_eq!(size, 10); + assert_eq!(name, "link -> target"); + } + + #[test] + fn test_parse_ls_line_returns_none_for_total() { + assert!(parse_ls_line("total 48").is_none()); + } + + #[test] + fn test_parse_ls_line_year_format() { + let (ft, size, name) = parse_ls_line( + "-rw-r--r-- 1 user staff 5678 Dec 25 2024 old.tar.gz", + ) + .unwrap(); + assert_eq!(ft, '-'); + assert_eq!(size, 5678); + assert_eq!(name, "old.tar.gz"); + } } From 4cf039277723b78466ceec0f40669ad6e33033d0 Mon Sep 17 00:00:00 2001 From: Tuan Tran Date: Sat, 4 Apr 2026 01:07:54 +0700 Subject: [PATCH 02/13] chore: revert version bump --- .release-please-manifest.json | 2 +- Cargo.lock | 2 +- Cargo.toml | 6 ++---- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b9091c583..42b68d645 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.34.3" + ".": "0.34.1" } diff --git a/Cargo.lock b/Cargo.lock index 5df1288fe..fc463facc 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -903,7 +903,7 @@ dependencies = [ [[package]] name = "rtk" -version = "0.34.3" +version = "0.34.1" dependencies = [ "anyhow", "automod", diff --git a/Cargo.toml b/Cargo.toml index f83c3e391..89264e8c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rtk" -version = "0.34.3" +version = "0.34.1" edition = "2021" authors = ["Patrick Szymkowiak"] description = "Rust Token Killer - High-performance CLI proxy to minimize LLM token consumption" @@ -55,9 +55,7 @@ license-file = ["LICENSE", "0"] extended-description = "rtk filters and compresses command outputs before they reach your LLM context, saving 60-90% of tokens." section = "utility" priority = "optional" -assets = [ - ["target/release/rtk", "usr/bin/", "755"], -] +assets = [["target/release/rtk", "usr/bin/", "755"]] # cargo-generate-rpm configuration [package.metadata.generate-rpm] From d2ecbdb2eccef2df17e76a0ca076df83ceeedddf Mon Sep 17 00:00:00 2001 From: Tuan Tran Date: Sat, 4 Apr 2026 01:09:59 +0700 Subject: [PATCH 03/13] bet --- .release-please-manifest.json | 2 +- Cargo.toml | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.release-please-manifest.json b/.release-please-manifest.json index 42b68d645..9a986e169 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "0.34.1" + ".": "0.34.1" } diff --git a/Cargo.toml b/Cargo.toml index 89264e8c5..22b999a88 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,7 +55,9 @@ license-file = ["LICENSE", "0"] extended-description = "rtk filters and compresses command outputs before they reach your LLM context, saving 60-90% of tokens." section = "utility" priority = "optional" -assets = [["target/release/rtk", "usr/bin/", "755"]] +assets = [ + ["target/release/rtk", "usr/bin/", "755"], +] # cargo-generate-rpm configuration [package.metadata.generate-rpm] From bade4547b3dd3dfe6eef9a650acbc0fcf82df160 Mon Sep 17 00:00:00 2001 From: aesoft <43991222+aeppling@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:07:36 +0200 Subject: [PATCH 04/13] Update Cargo.toml --- Cargo.toml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 22b999a88..89264e8c5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,9 +55,7 @@ license-file = ["LICENSE", "0"] extended-description = "rtk filters and compresses command outputs before they reach your LLM context, saving 60-90% of tokens." section = "utility" priority = "optional" -assets = [ - ["target/release/rtk", "usr/bin/", "755"], -] +assets = [["target/release/rtk", "usr/bin/", "755"]] # cargo-generate-rpm configuration [package.metadata.generate-rpm] From 614c64eec2aa566a672c60b408353003e5a6dc2e Mon Sep 17 00:00:00 2001 From: aesoft <43991222+aeppling@users.noreply.github.com> Date: Sat, 4 Apr 2026 11:08:41 +0200 Subject: [PATCH 05/13] Update Cargo.toml --- Cargo.toml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 89264e8c5..0069870f3 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -55,8 +55,9 @@ license-file = ["LICENSE", "0"] extended-description = "rtk filters and compresses command outputs before they reach your LLM context, saving 60-90% of tokens." section = "utility" priority = "optional" -assets = [["target/release/rtk", "usr/bin/", "755"]] - +assets = [ + ["target/release/rtk", "usr/bin/", "755"], +] # cargo-generate-rpm configuration [package.metadata.generate-rpm] assets = [ From 9c9879c2da7ac540ca6b4170c828bc34f2c5b52d Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Sat, 4 Apr 2026 23:54:16 +0200 Subject: [PATCH 06/13] docs: add interface contract and Phase 0 stubs for docs reorganization Creates docs/README.md (interface contract defining 3-tab structure, required frontmatter, and conventions) and 4 stub pages with valid frontmatter for docs/guide/, docs/reference/, and docs/architecture/. These are the prerequisites for the prepare-docs.mjs pipeline (Plan B) and for Adrien's Phase 1 content work. No existing files modified. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Florian BRUNIAUX --- docs/README.md | 64 +++++++++++++++++++ docs/architecture/index.md | 15 +++++ docs/guide/getting-started/installation.md | 73 ++++++++++++++++++++++ docs/guide/index.md | 19 ++++++ docs/reference/index.md | 16 +++++ 5 files changed, 187 insertions(+) create mode 100644 docs/README.md create mode 100644 docs/architecture/index.md create mode 100644 docs/guide/getting-started/installation.md create mode 100644 docs/guide/index.md create mode 100644 docs/reference/index.md diff --git a/docs/README.md b/docs/README.md new file mode 100644 index 000000000..07d64eb46 --- /dev/null +++ b/docs/README.md @@ -0,0 +1,64 @@ +# RTK Documentation — Interface Contract + +This directory is the source of truth for user-facing and contributor documentation. +It feeds the RTK website via the `prepare-docs.mjs` pipeline in `rtk-ai/rtk-website`. + +## Structure + +``` +docs/ + README.md <- This file (interface contract — do not remove) + guide/ -> User-facing documentation (Tab "Guide") + reference/ -> Contributor/technical documentation (Tab "Reference") + architecture/ -> Conceptual/visual documentation (Tab "Architecture") +``` + +## Frontmatter (required on every .md) + +Every markdown file under `docs/guide/`, `docs/reference/`, and `docs/architecture/` +must include this frontmatter block at the top: + +```yaml +--- +title: string # Page title (used in sidebar + search) +description: string # One-line summary for search results and SEO +sidebar: + order: number # Position within the sidebar group (1 = first) +--- +``` + +The `prepare-docs.mjs` pipeline validates this contract at build time and **fails fast** +with a clear error message if frontmatter is missing or malformed. + +## Conventions + +- **Filenames**: kebab-case, `.md` only (e.g., `getting-started.md`, `quick-start.md`) +- **Subdirectories**: become sidebar groups in Starlight (directory name = group label) +- **Internal links**: relative within the same tab (`./foo.md`, `./getting-started/installation.md`) +- **Cross-tab links**: full path from `docs/` root (`../../reference/internals/command-routing.md`) +- **Diagrams**: Mermaid in fenced code blocks (` ```mermaid `) +- **Code samples**: always specify the language (`rust`, `toml`, `bash`, `shell`) +- **Language**: English only + +## Tabs overview + +| Tab | Path | Audience | +|-----|------|----------| +| Guide | `docs/guide/` | End users installing and using RTK | +| Reference | `docs/reference/` | Contributors, maintainers, integrators | +| Architecture | `docs/architecture/` | Readers exploring design decisions and diagrams | + +## Root files (do not move or modify structure) + +The following files live at the repository root and are **not** managed by this pipeline. +They are the canonical source for GitHub display and remain unchanged. + +- `README.md` — Project overview +- `INSTALL.md` — Installation reference (full) +- `CONTRIBUTING.md` — Contribution guide +- `SECURITY.md` — Security policy +- `ARCHITECTURE.md` — Full architecture document +- `CHANGELOG.md` — Release history + +The guide files in `docs/` are derived, English-only, structured versions intended +for the website. They reference root files as source material but do not replace them. diff --git a/docs/architecture/index.md b/docs/architecture/index.md new file mode 100644 index 000000000..b64e5a332 --- /dev/null +++ b/docs/architecture/index.md @@ -0,0 +1,15 @@ +--- +title: Architecture +description: System design, diagrams, and architectural decision records for RTK +sidebar: + order: 1 +--- + +# Architecture + +Conceptual documentation covering how RTK is designed and why key decisions were made. + +## What's in this section + +- **Diagrams** — Visual representations of the command flow and filter pipeline +- **Decisions** — Architectural decision records (ADRs) for key design choices diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md new file mode 100644 index 000000000..e52c9df32 --- /dev/null +++ b/docs/guide/getting-started/installation.md @@ -0,0 +1,73 @@ +--- +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 + +```bash +cargo install rtk +``` + +## 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 +``` diff --git a/docs/guide/index.md b/docs/guide/index.md new file mode 100644 index 000000000..4ccbf78a0 --- /dev/null +++ b/docs/guide/index.md @@ -0,0 +1,19 @@ +--- +title: RTK Guide +description: User-facing documentation for RTK, the token-saving CLI proxy for AI coding assistants +sidebar: + order: 1 +--- + +# RTK Guide + +RTK (Rust Token Killer) is a CLI proxy that reduces LLM token consumption by 60-90% on common development operations. It filters and compresses command output before it reaches your AI assistant, without changing how you work. + +## What's in this guide + +- **[Getting Started](./getting-started/installation.md)** — Install RTK, verify it works, run your first command +- **Commands** — Per-ecosystem reference: git, cargo, GitHub CLI, JavaScript, Python, and more +- **[Filters](./filters/using-filters.md)** — Create custom TOML filters for your own commands +- **[Analytics](./analytics/gain.md)** — Measure your actual token savings with `rtk gain` +- **[Configuration](./configuration.md)** — Customize RTK behavior via `~/.config/rtk/config.toml` +- **[Troubleshooting](./troubleshooting.md)** — Common issues and how to fix them diff --git a/docs/reference/index.md b/docs/reference/index.md new file mode 100644 index 000000000..4f8567b5d --- /dev/null +++ b/docs/reference/index.md @@ -0,0 +1,16 @@ +--- +title: Reference +description: Technical reference for RTK contributors, maintainers, and integrators +sidebar: + order: 1 +--- + +# Reference + +Technical documentation for people working on RTK internals or integrating it into their toolchain. + +## What's in this section + +- **Contributing** — How to contribute, coding standards, testing strategy, and security policy +- **Internals** — Command routing, filter pipeline, tracking system, and hook engine +- **TOML DSL** — Specification for custom filter files From 25438b29ae5022dac8b0e65c932cc7ceb7e25417 Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Sun, 5 Apr 2026 00:09:14 +0200 Subject: [PATCH 07/13] =?UTF-8?q?docs(guide):=20add=2011=20Phase=201=20gui?= =?UTF-8?q?de=20pages=20=E2=80=94=20full=20MVP=20content?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Creates the complete user-facing guide for the docs website: Getting started: - quick-start.md: 5-minute walkthrough (init, first commands, rtk gain) - supported-agents.md: Claude Code, Cursor, Copilot, Gemini, Cline, Windsurf, Codex, OpenCode — integration tiers, install commands, graceful degradation Commands (adapted from FEATURES.md, English, --help-first format): - git.md: status/log/diff/show/add/commit/push/pull/branch + gh CLI - cargo.md: test/nextest/build/check/clippy/install + generic test/err wrappers - files.md: ls/read/grep/find/diff/wc/smart with before/after examples - javascript.md: vitest/playwright/tsc/eslint/prettier/next/pnpm/npm/npx/prisma - python.md: pytest/ruff/mypy/pip/deps Reference: - filters/using-filters.md: 8-stage pipeline, lookup priority, TOML DSL reference, Mermaid diagram (adapted from docs/filter-workflow.md) - analytics/gain.md: rtk gain flags, daily/weekly/monthly breakdowns, export formats, token estimation, database management (from docs/AUDIT_GUIDE.md) - configuration.md: full config.toml reference, env vars, tee system, telemetry - troubleshooting.md: common issues and fixes (from docs/TROUBLESHOOTING.md) All pages carry valid frontmatter (title, description, sidebar.order). No existing files modified. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Florian BRUNIAUX --- docs/guide/analytics/gain.md | 152 ++++++++++++ docs/guide/commands/cargo.md | 142 +++++++++++ docs/guide/commands/files.md | 185 +++++++++++++++ docs/guide/commands/git.md | 221 ++++++++++++++++++ docs/guide/commands/javascript.md | 129 ++++++++++ docs/guide/commands/python.md | 55 +++++ docs/guide/configuration.md | 115 +++++++++ docs/guide/filters/using-filters.md | 121 ++++++++++ docs/guide/getting-started/quick-start.md | 88 +++++++ .../guide/getting-started/supported-agents.md | 133 +++++++++++ docs/guide/troubleshooting.md | 156 +++++++++++++ 11 files changed, 1497 insertions(+) create mode 100644 docs/guide/analytics/gain.md create mode 100644 docs/guide/commands/cargo.md create mode 100644 docs/guide/commands/files.md create mode 100644 docs/guide/commands/git.md create mode 100644 docs/guide/commands/javascript.md create mode 100644 docs/guide/commands/python.md create mode 100644 docs/guide/configuration.md create mode 100644 docs/guide/filters/using-filters.md create mode 100644 docs/guide/getting-started/quick-start.md create mode 100644 docs/guide/getting-started/supported-agents.md create mode 100644 docs/guide/troubleshooting.md diff --git a/docs/guide/analytics/gain.md b/docs/guide/analytics/gain.md new file mode 100644 index 000000000..ef0000298 --- /dev/null +++ b/docs/guide/analytics/gain.md @@ -0,0 +1,152 @@ +--- +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 -t pro # quota analysis (pro/5x/20x tiers) + +# 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 | +|---------|----------------|-----------| +| `rtk git status` | 77-93% | Compact stat format | +| `rtk eslint` | 84% | Group by rule | +| `rtk vitest run` | 94-99% | Show failures only | +| `rtk find` | 75% | Tree format | +| `rtk pnpm list` | 70-90% | Compact dependencies | +| `rtk 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 +``` + +## Troubleshooting + +**No data showing:** +```bash +ls -lh ~/.local/share/rtk/history.db +sqlite3 ~/.local/share/rtk/history.db "SELECT COUNT(*) FROM commands" +rtk git status # run a tracked command to generate data +``` + +**Incorrect statistics:** Token estimation is a heuristic. For precise counts, use `tiktoken`: +```bash +pip install tiktoken +rtk git status > output.txt +python -c " +import tiktoken +enc = tiktoken.get_encoding('cl100k_base') +print(len(enc.encode(open('output.txt').read())), 'actual tokens') +" +``` diff --git a/docs/guide/commands/cargo.md b/docs/guide/commands/cargo.md new file mode 100644 index 000000000..dc2df8f77 --- /dev/null +++ b/docs/guide/commands/cargo.md @@ -0,0 +1,142 @@ +--- +title: Cargo (Rust) +description: RTK filters for cargo — build, test, check, clippy, and nextest +sidebar: + order: 2 +--- + +# Cargo (Rust) + +RTK filters the most verbose cargo outputs and passes through unrecognized subcommands unchanged. + +## cargo test — 90% savings + +```bash +rtk cargo test [args...] +``` + +Shows failures only. On success, shows a compact summary. + +**Before (200+ lines on failure):** +``` +running 15 tests +test utils::test_parse ... ok +test utils::test_format ... ok +test utils::test_edge_case ... FAILED + +failures: + +---- utils::test_edge_case stdout ---- +thread 'utils::test_edge_case' panicked at 'assertion failed: ...' +...150 lines of backtrace... +``` + +**After (~5 lines):** +``` +FAILED: 2/15 tests + test_edge_case: assertion failed at utils.rs:42 + test_overflow: panic at utils.rs:18 +[full output: ~/.local/share/rtk/tee/cargo_test_1234.log] +``` + +The tee file path lets you (or your AI assistant) read the full output if needed without re-running the command. + +## cargo nextest — failures only + +```bash +rtk cargo nextest [run|list|--lib] [args...] +``` + +Same behavior as `cargo test` — filters to failures only. + +## cargo build — 80% savings + +```bash +rtk cargo build [args...] +``` + +Removes all "Compiling..." lines, keeps errors and the final result. + +**Before:** +``` + Compiling proc-macro2 v1.0.79 + Compiling unicode-ident v1.0.12 + Compiling quote v1.0.35 + ...200 crate lines... + Compiling rtk v0.28.0 + Finished release [optimized] target(s) in 12.34s +``` + +**After:** +``` +Finished release [optimized] in 12.34s +``` + +## cargo check — 80% savings + +```bash +rtk cargo check [args...] +``` + +Removes "Checking..." lines, keeps errors. + +## cargo clippy — 80% savings + +```bash +rtk cargo clippy [args...] +``` + +Groups warnings by lint rule. + +**Before (50 lines for 3 warnings):** +``` +warning: unused variable `x` + --> src/main.rs:42:9 + | +42 | let x = 5; + | ^ help: if this is intentional, prefix it with an underscore: `_x` + = note: `#[warn(unused_variables)]` on by default +... +``` + +**After:** +``` +src/main.rs — 2 warnings + unused_variables (x2): src/main.rs:42, src/main.rs:67 +``` + +## cargo install + +```bash +rtk cargo install [args...] +``` + +Removes dependency compilation noise, keeps the install result and any errors. + +## Generic test wrapper + +```bash +rtk test +``` + +Runs any test command and shows failures only. Works with any test runner: + +```bash +rtk test cargo test +rtk test npm test +rtk test bun test +rtk test pytest +``` + +## Error-only wrapper + +```bash +rtk err +``` + +Runs any command and shows errors and warnings only: + +```bash +rtk err cargo build +rtk err npm run build +``` diff --git a/docs/guide/commands/files.md b/docs/guide/commands/files.md new file mode 100644 index 000000000..53d652398 --- /dev/null +++ b/docs/guide/commands/files.md @@ -0,0 +1,185 @@ +--- +title: File System Commands +description: RTK filters for ls, read, grep, find, diff, and wc +sidebar: + order: 3 +--- + +# File System Commands + +RTK replaces common file and search commands with compact, token-optimized equivalents. + +## ls — 80% savings + +```bash +rtk ls [args...] +``` + +Replaces `ls` and `tree` with a compact directory tree. All native `ls` flags are supported (`-l`, `-a`, `-h`, `-R`, etc.). + +**Before (45 lines):** +``` +drwxr-xr-x 15 user staff 480 ... +-rw-r--r-- 1 user staff 1234 ... +...40 more lines... +``` + +**After (12 lines):** +``` +my-project/ ++-- src/ (8 files) +| +-- main.rs ++-- Cargo.toml ++-- README.md +``` + +## read — up to 74% savings + +```bash +rtk read [options] +rtk read - # read from stdin +``` + +Replaces `cat`, `head`, `tail` with intelligent content filtering. + +| Option | Short | Default | Description | +|--------|-------|---------|-------------| +| `--level` | `-l` | `minimal` | Filtering level: `none`, `minimal`, `aggressive` | +| `--max-lines` | `-m` | unlimited | Maximum number of lines | +| `--line-numbers` | `-n` | off | Show line numbers | + +**Filtering levels:** + +| Level | Description | Savings | +|-------|-------------|---------| +| `none` | Raw output, no filtering | 0% | +| `minimal` | Removes excessive blank lines and comments | ~30% | +| `aggressive` | Signatures only — removes function bodies | ~74% | + +**Before — `cat main.rs` (~200 lines):** +```rust +fn main() -> Result<()> { + let config = Config::load()?; + let data = process_data(&input); + for item in data { + println!("{}", item); + } + Ok(()) +} +... +``` + +**After — `rtk read main.rs -l aggressive` (~50 lines):** +```rust +fn main() -> Result<()> { ... } +fn process_data(input: &str) -> Vec { ... } +struct Config { ... } +impl Config { fn load() -> Result { ... } } +``` + +Supported languages for filtering: Rust, Python, JavaScript, TypeScript, Go, C, C++, Java, Ruby, Shell. + +## grep — 80% savings + +```bash +rtk grep [path] [options] +``` + +Replaces `grep` and `rg` with results grouped by file and truncated. + +| Option | Short | Default | Description | +|--------|-------|---------|-------------| +| `--max-len` | `-l` | 80 | Maximum line length | +| `--max` | `-m` | 50 | Maximum number of results | +| `--context-only` | `-c` | off | Show only match context | +| `--file-type` | `-t` | all | Filter by type (ts, py, rust, etc.) | + +Additional arguments are passed to `rg` (ripgrep). + +**Before (20 lines):** +``` +src/git.rs:45:pub fn run(...) +src/git.rs:120:fn run_status(...) +src/ls.rs:12:pub fn run(...) +src/ls.rs:25:fn run_tree(...) +``` + +**After (10 lines):** +``` +src/git.rs + 45: pub fn run(...) + 120: fn run_status(...) +src/ls.rs + 12: pub fn run(...) + 25: fn run_tree(...) +``` + +## find — 80% savings + +```bash +rtk find [args...] +``` + +Replaces `find` and `fd` with results grouped by directory. Both RTK syntax and native `find` flags (`-name`, `-type`, etc.) are supported. + +**Before (30 lines):** +``` +./src/main.rs +./src/git.rs +./src/config.rs +./src/tracking.rs +./src/filter.rs +./src/utils.rs +...24 more lines... +``` + +**After (8 lines):** +``` +src/ (12 .rs) + main.rs, git.rs, config.rs + tracking.rs, filter.rs, utils.rs + ...6 more +tests/ (3 .rs) + test_git.rs, test_ls.rs, test_filter.rs +``` + +## tree + +```bash +rtk tree [args...] +``` + +Proxy to native `tree` with filtered output. All native flags supported (`-L`, `-d`, `-a`, etc.). + +**Savings:** ~80% + +## diff — 60% savings + +```bash +rtk diff +rtk diff # stdin as second file +``` + +Ultra-compact diff showing only changed lines. + +## wc + +```bash +rtk wc [args...] +``` + +Replaces `wc` with compact output (removes paths and padding). All native flags supported (`-l`, `-w`, `-c`, etc.). + +## smart — 95% savings + +```bash +rtk smart +``` + +Generates a 2-line technical summary of a source file using heuristics. + +```bash +$ rtk smart src/tracking.rs +SQLite-based token tracking system for command executions. +Records input/output tokens, savings %, execution times with 90-day retention. +``` diff --git a/docs/guide/commands/git.md b/docs/guide/commands/git.md new file mode 100644 index 000000000..dc1b37a1d --- /dev/null +++ b/docs/guide/commands/git.md @@ -0,0 +1,221 @@ +--- +title: Git Commands +description: RTK filters for git and GitHub CLI — compact status, log, diff, and more +sidebar: + order: 1 +--- + +# Git Commands + +RTK supports all git subcommands. Unrecognized subcommands pass through to git unchanged. + +## git status — 80% savings + +```bash +rtk git status [args...] +``` + +Replaces the verbose multi-section output with a compact one-line summary. + +**Before (20 lines):** +``` +On branch main +Your branch is up to date with 'origin/main'. + +Changes not staged for commit: + (use "git add ..." to update what will be committed) + modified: src/main.rs + modified: src/git.rs + +Untracked files: + (use "git add ..." to include in what will be committed) + new_file.txt +... +``` + +**After (5 lines):** +``` +main | 3M 1? 1A +M src/main.rs +M src/git.rs +? new_file.txt +A staged_file.rs +``` + +## git log — 80% savings + +```bash +rtk git log [args...] # supports --oneline, --graph, --all, -n, etc. +``` + +Shows hash + subject only, one line per commit. + +**Before (50+ lines for 5 commits):** +``` +commit abc123def... (HEAD -> main) +Author: User +Date: Mon Jan 15 10:30:00 2024 + + Fix token counting bug + +commit def456... +... +``` + +**After (5 lines):** +``` +abc123 Fix token counting bug +def456 Add vitest support +789abc Refactor filter engine +012def Update README +345ghi Initial commit +``` + +## git diff — 75% savings + +```bash +rtk git diff [args...] # supports --stat, --cached, --staged, etc. +``` + +Shows changed files with line counts and condensed hunks. + +**Before (~100 lines):** +``` +diff --git a/src/main.rs b/src/main.rs +index abc123..def456 100644 +--- a/src/main.rs ++++ b/src/main.rs +@@ -10,6 +10,8 @@ + fn main() { ++ let config = Config::load()?; ++ config.validate()?; +...30 lines of headers and context... +``` + +**After (~25 lines):** +``` +src/main.rs (+5/-2) + + let config = Config::load()?; + + config.validate()?; + - // old code + - let x = 42; +src/git.rs (+1/-1) + ~ format!("ok {}", branch) +``` + +## git show — 80% savings + +```bash +rtk git show [args...] +``` + +Shows commit summary + stat + compact diff. + +## git add — 92% savings + +```bash +rtk git add [args...] # supports -A, -p, --all, etc. +``` + +Output: `ok` (single word). + +## git commit — 92% savings + +```bash +rtk git commit -m "message" [args...] # supports -a, --amend, --allow-empty, etc. +``` + +Output: `ok abc1234` (confirmation + short hash). + +## git push — 92% savings + +```bash +rtk git push [args...] # supports -u, remote, branch, etc. +``` + +**Before (15 lines):** +``` +Enumerating objects: 5, done. +Counting objects: 100% (5/5), done. +Delta compression using up to 8 threads +... +``` + +**After (1 line):** +``` +ok main +``` + +## git pull — 92% savings + +```bash +rtk git pull [args...] +``` + +Output: `ok 3 files +10 -2` + +## git branch + +```bash +rtk git branch [args...] # supports -d, -D, -m, etc. +``` + +Shows current branch, local branches, and remote branches in compact form. + +## git fetch + +```bash +rtk git fetch [args...] +``` + +Output: `ok fetched (N new refs)` + +## git stash + +```bash +rtk git stash [list|show|pop|apply|drop|push] [args...] +``` + +## git worktree + +```bash +rtk git worktree [add|remove|prune|list] [args...] +``` + +## Passthrough + +Any git subcommand without a specific RTK filter runs unchanged: + +```bash +rtk git rebase main # runs git rebase main +rtk git cherry-pick abc # runs git cherry-pick abc +rtk git tag v1.0.0 # runs git tag v1.0.0 +``` + +## GitHub CLI + +```bash +rtk gh pr list +rtk gh pr view # 87% savings +rtk gh pr checks # 79% savings +rtk gh issue list +rtk gh run list # 82% savings +rtk gh api +``` + +**Before (30 lines for pr list):** +``` +Showing 10 of 15 pull requests in org/repo + +#42 feat: add vitest support + user opened about 2 days ago + labels: enhancement +... +``` + +**After (10 lines):** +``` +#42 feat: add vitest (open, 2d) +#41 fix: git diff crash (open, 3d) +#40 chore: update deps (merged, 5d) +``` diff --git a/docs/guide/commands/javascript.md b/docs/guide/commands/javascript.md new file mode 100644 index 000000000..6b27cdc43 --- /dev/null +++ b/docs/guide/commands/javascript.md @@ -0,0 +1,129 @@ +--- +title: JavaScript / TypeScript +description: RTK filters for pnpm, npm, vitest, tsc, ESLint, Next.js, Playwright, and Prisma +sidebar: + order: 4 +--- + +# JavaScript / TypeScript + +RTK covers the full JS/TS toolchain: package managers, test runners, type checking, linting, build tools, and ORM. + +## vitest run — 99.5% savings + +```bash +rtk vitest run [args...] +``` + +Shows failures only. One of the highest savings rates in RTK — vitest verbose output can be massive. + +## Playwright — 94% savings + +```bash +rtk playwright [args...] +``` + +Shows failures and summaries only, strips progress output. + +## tsc — 83% savings + +```bash +rtk tsc [args...] +``` + +Groups TypeScript errors by file and error code. + +**Before (50 lines for 4 errors):** +``` +src/api.ts(12,5): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. +src/api.ts(15,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. +src/api.ts(20,3): error TS7006: Parameter 'x' implicitly has an 'any' type. +src/utils.ts(5,1): error TS2304: Cannot find name 'foo'. +``` + +**After (15 lines):** +``` +src/api.ts (3 errors) + TS2345: Argument type mismatch (x2) + TS7006: Parameter implicitly has 'any' +src/utils.ts (1 error) + TS2304: Cannot find name 'foo' +``` + +## ESLint / Biome — 84% savings + +```bash +rtk lint [args...] +rtk lint biome [args...] +``` + +Groups violations by rule and file. Auto-detects the linter. + +## prettier + +```bash +rtk prettier --check . # 70% savings +rtk prettier --write src/ +``` + +Shows only files that need formatting. + +## Next.js build — 87% savings + +```bash +rtk next [args...] +rtk pnpm build # delegates to Next.js filter +``` + +Compact output with route metrics. + +## pnpm + +| Command | Description | Savings | +|---------|-------------|---------| +| `rtk pnpm list [-d N]` | Compact dependency tree | ~70% | +| `rtk pnpm outdated` | Outdated packages: `pkg: old -> new` | ~80% | +| `rtk pnpm install [pkgs...]` | Filters progress bars | ~60% | +| `rtk pnpm build` | Delegates to Next.js filter | ~87% | +| `rtk pnpm typecheck` | Delegates to tsc filter | ~83% | + +Unrecognized subcommands pass through to pnpm directly. + +## npm + +```bash +rtk npm [args...] # e.g. rtk npm run build +``` + +Filters npm boilerplate (progress bars, headers, audit notices). + +## npx — smart routing + +```bash +rtk npx [args...] +``` + +Routes automatically to specialized filters: + +- `rtk npx tsc` → tsc filter +- `rtk npx eslint` → lint filter +- `rtk npx prisma` → prisma filter +- Other → passthrough filter + +## Prisma + +| Command | Description | +|---------|-------------| +| `rtk prisma generate` | Client generation (removes ASCII art) | +| `rtk prisma migrate dev [--name N]` | Create and apply a migration | +| `rtk prisma migrate status` | Migration status | +| `rtk prisma migrate deploy` | Deploy to production | +| `rtk prisma db-push` | Push schema | + +## Universal format detector + +```bash +rtk format [args...] +``` + +Auto-detects the project formatter (prettier, black, ruff format, rustfmt) and applies a unified compact filter. diff --git a/docs/guide/commands/python.md b/docs/guide/commands/python.md new file mode 100644 index 000000000..4444dcc26 --- /dev/null +++ b/docs/guide/commands/python.md @@ -0,0 +1,55 @@ +--- +title: Python +description: RTK filters for pytest, ruff, mypy, and pip +sidebar: + order: 5 +--- + +# Python + +RTK covers the core Python toolchain: testing, linting/formatting, type checking, and package management. + +## pytest — 90% savings + +```bash +rtk pytest [args...] +``` + +Shows failures only. On success, shows a compact summary. + +## ruff — 80% savings + +```bash +rtk ruff check [args...] +rtk ruff format --check [args...] +``` + +Compressed JSON output grouped by file and rule. + +## mypy — type errors grouped by file + +```bash +rtk mypy [args...] +``` + +Groups type errors by file and error code, similar to the tsc filter. + +## pip / uv + +```bash +rtk pip list # package list +rtk pip outdated # outdated packages +rtk pip install # installation +``` + +Auto-detects `uv` if available and uses it instead of pip. + +## deps — project overview + +```bash +rtk deps [path] # default: current directory +``` + +Compact summary of project dependencies. Auto-detects `Cargo.toml`, `package.json`, `pyproject.toml`, `go.mod`, `Gemfile`, and others. + +**Savings:** ~70% diff --git a/docs/guide/configuration.md b/docs/guide/configuration.md new file mode 100644 index 000000000..053eb351e --- /dev/null +++ b/docs/guide/configuration.md @@ -0,0 +1,115 @@ +--- +title: Configuration +description: Customize RTK behavior via config.toml, environment variables, and per-project filters +sidebar: + order: 7 +--- + +# 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/tracking.db" # optional override + +[display] +colors = true # colored output +emoji = true # use emojis in output +max_width = 120 # maximum output width + +[filters] +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 (opt-out below) + +[hooks] +exclude_commands = [] # commands to never auto-rewrite +``` + +## 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"] +``` + +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 +``` + +## Per-project filters + +Create `.rtk/filters.toml` in your project root to add custom filters or override built-ins. See [Using Filters](./filters/using-filters.md) for the TOML DSL reference. diff --git a/docs/guide/filters/using-filters.md b/docs/guide/filters/using-filters.md new file mode 100644 index 000000000..a23bd80d9 --- /dev/null +++ b/docs/guide/filters/using-filters.md @@ -0,0 +1,121 @@ +--- +title: Using Filters +description: How RTK filters work, the lookup priority, and how to create project-local filters +sidebar: + order: 1 +--- + +# Using Filters + +RTK filters are TOML files that define how a command's output should be compressed. They are the core mechanism behind every `rtk ` savings number. + +## Filter lookup priority + +When you run `rtk my-tool args`, RTK checks three locations in order: + +``` +1. .rtk/filters.toml (project-local) +2. ~/.config/rtk/filters.toml (user-global) + macOS alt: ~/Library/Application Support/rtk/filters.toml +3. BUILTIN_TOML (embedded in binary) +``` + +First match wins. A project filter with the same name as a built-in shadows the built-in and triggers a warning: + +``` +[rtk] warning: filter 'make' is shadowing a built-in filter +``` + +## The 8-stage filter pipeline + +Every matched filter runs output through this pipeline: + +``` +strip_ansi + -> replace + -> match_output (short-circuit: if pattern matches, emit message and stop) + -> strip/keep_lines + -> truncate_lines_at + -> tail_lines + -> max_lines + -> on_empty (if output is empty after all stages, emit this message) +``` + +Stages you don't configure are skipped. + +## Creating a project-local filter + +Create `.rtk/filters.toml` in your project root: + +```toml +[[filters]] +name = "my-tool" +match_command = "^my-tool\\b" + +strip_lines = [ + "^Loading", + "^Scanning", +] + +max_lines = 50 +on_empty = "my-tool: nothing to do" + +[[tests.my-tool]] +input = "Loading plugins...\nScan complete: 3 issues\nWarning: foo at line 42" +expected = "Scan complete: 3 issues\nWarning: foo at line 42" +``` + +Verify it works: + +```bash +rtk verify +``` + +## Filter fields reference + +| Field | Type | Description | +|-------|------|-------------| +| `name` | string | Unique identifier (used in logs and `rtk verify`) | +| `match_command` | regex | Pattern matched against the full command string | +| `strip_lines` | regex[] | Remove lines matching any of these patterns | +| `keep_lines` | regex[] | Keep only lines matching any of these patterns | +| `replace` | `[[from, to]]` | String replacements applied before line filtering | +| `match_output` | regex | If matched, emit `on_match` and stop pipeline | +| `on_match` | string | Message emitted when `match_output` matches | +| `truncate_lines_at` | number | Truncate lines longer than N characters | +| `tail_lines` | number | Keep only the last N lines | +| `max_lines` | number | Keep only the first N lines after all other stages | +| `on_empty` | string | Message emitted when pipeline produces empty output | + +## How built-in filters are compiled + +Built-in filters live in `src/filters/*.toml` in the RTK source. At build time, `build.rs` concatenates all TOML files alphabetically and embeds the result in the binary as a constant. No external files are needed at runtime. + +This means: +- Built-in filters have zero filesystem overhead +- Project filters override built-ins by name +- New built-in filters require a new RTK release + +## Verifying filters + +```bash +rtk verify +``` + +Runs every `[[tests.*]]` entry in all filter files and reports pass/fail. Use this to validate your custom filters before committing. + +## Flow diagram + +```mermaid +flowchart LR + CMD["rtk my-tool args"] --> P1 + P1{"1. .rtk/filters.toml\n(project-local)"} + P1 -->|"match"| WIN["apply filter"] + P1 -->|"no match"| P2 + P2{"2. ~/.config/rtk/filters.toml\n(user-global)"} + P2 -->|"match"| WIN + P2 -->|"no match"| P3 + P3{"3. BUILTIN_TOML\n(binary)"} + P3 -->|"match"| WIN + P3 -->|"no match"| P4[["exec raw\n(passthrough)"]] +``` diff --git a/docs/guide/getting-started/quick-start.md b/docs/guide/getting-started/quick-start.md new file mode 100644 index 000000000..8f3b6ed42 --- /dev/null +++ b/docs/guide/getting-started/quick-start.md @@ -0,0 +1,88 @@ +--- +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. + +## Step 2: Run your first RTK commands + +You can use RTK directly or let the hook rewrite commands transparently. + +```bash +# Git — compact status and log +rtk git status +rtk git log -10 + +# Rust — build and test with failures only +rtk cargo build +rtk cargo test + +# JavaScript — type errors grouped by file +rtk tsc +rtk vitest run +``` + +## Step 3: Check your savings + +After a few commands, see how much you saved: + +```bash +rtk gain +``` + +Output: + +``` +Total commands : 12 +Input tokens : 45,230 +Output tokens : 4,890 +Saved : 40,340 (89.2%) +``` + +## Step 4: Use the proxy for unsupported commands + +Any command RTK doesn't know about runs through passthrough — the output is unchanged but usage is tracked: + +```bash +rtk proxy make install +``` + +## What the hook does + +Once installed, the hook intercepts every command your AI assistant runs and rewrites it transparently. You don't need to type `rtk` — the hook does it automatically. + +For example, when Claude Code executes `cargo test`, the hook rewrites it to `rtk cargo test` before it runs. The filtered output is what the LLM sees. + +## Next steps + +- [Supported agents](./supported-agents.md) — Claude Code, Cursor, Copilot, and more +- [Commands](../commands/git.md) — full reference for each ecosystem +- [Configuration](../configuration.md) — customize RTK behavior diff --git a/docs/guide/getting-started/supported-agents.md b/docs/guide/getting-started/supported-agents.md new file mode 100644 index 000000000..52e5b520c --- /dev/null +++ b/docs/guide/getting-started/supported-agents.md @@ -0,0 +1,133 @@ +--- +title: Supported Agents +description: How to integrate RTK with Claude Code, Cursor, Copilot, Cline, Windsurf, Codex, and OpenCode +sidebar: + order: 3 +--- + +# Supported Agents + +RTK supports 9 AI coding agents across 3 integration tiers. + +## 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 | Rust binary | Yes | +| GitHub Copilot CLI | Rust binary (deny-with-suggestion) | No (agent retries) | +| Cursor | Shell hook (`preToolUse`) | Yes | +| Gemini CLI | Rust binary | Yes | +| Cline / Roo Code | Rules file (prompt-level) | N/A | +| Windsurf | Rules file (prompt-level) | N/A | +| Codex CLI | AGENTS.md instructions | N/A | +| OpenCode | TypeScript plugin | Yes | + +## 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 --cursor +``` + +Restart Cursor. The hook uses `preToolUse` with Cursor's `updated_input` format. + +### VS Code Copilot Chat + +```bash +rtk init --global --copilot +``` + +### Gemini CLI + +```bash +rtk init --global --gemini +``` + +### OpenCode + +```bash +rtk init --global --opencode +``` + +Restart OpenCode. The plugin uses the `tool.execute.before` event. + +### Cline / Roo Code + +```bash +rtk init --cline # creates .clinerules in current project +``` + +Cline reads `.clinerules` as custom instructions. RTK adds guidance telling Cline to prefer `rtk ` over raw commands. + +### Windsurf + +```bash +rtk init --windsurf # creates .windsurfrules in current project +``` + +### Codex CLI + +```bash +rtk init --codex # creates AGENTS.md or patches existing one +``` + +## 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/JS in agent's plugin system | Transparent — in-place mutation | +| **Rules file** | Prompt-level instructions | Guidance only — agent is told to prefer `rtk ` | + +Rules file integrations (Cline, Windsurf, Codex) rely on the model following instructions. Full hook integrations (Claude Code, Cursor, Gemini) are guaranteed — the command is rewritten before the agent sees it. + +## 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"] +``` diff --git a/docs/guide/troubleshooting.md b/docs/guide/troubleshooting.md new file mode 100644 index 000000000..5efcabd9b --- /dev/null +++ b/docs/guide/troubleshooting.md @@ -0,0 +1,156 @@ +--- +title: Troubleshooting +description: Common RTK issues and how to fix them +sidebar: + order: 8 +--- + +# 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 not working on Windows + +**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 From 974db2ff0b31997f9de9d29af3273ed17cb4d7a7 Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Sun, 5 Apr 2026 00:18:59 +0200 Subject: [PATCH 08/13] docs(phase-4): complete reference, architecture tabs and remaining commands MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Guide — 10 new command and analytics pages: - commands/go.md: go test (NDJSON), golangci-lint - commands/ruby.md: rspec, rubocop, rake - commands/dotnet.md: dotnet build/test, binlog, format - commands/containers.md: docker ps/images/logs/compose, kubectl pods/services/logs - commands/github-cli.md: gh pr/issue/run, Graphite gt commands - commands/data.md: json, env, log, curl, wget, aws, psql, summary - commands/utilities.md: proxy passthrough, global flags, RTK_DISABLED - filters/creating-filters.md: TOML DSL guide, field reference, inline tests - analytics/discover.md: rtk discover — missed savings analysis - analytics/economics.md: rtk cc-economics — dollar savings vs ccusage Reference tab (9 pages): - contributing/guide.md: design philosophy, PR process, TOML vs Rust decision - contributing/security.md: vulnerability reporting, dangerous patterns, dep criteria - contributing/coding-standards.md: Rust rules, error handling, lazy_static, fallback pattern - contributing/testing.md: snapshot tests, token accuracy, cross-platform, benchmarks - internals/command-routing.md: 6-phase lifecycle, exit codes, verbosity, module map - internals/filter-pipeline.md: 12 filtering strategies, TOML stages, savings by ecosystem - internals/tracking-system.md: SQLite schema, data flow, token estimation, Tracker API - internals/hook-engine.md: rewrite registry, compound commands, exit code contract, rtk init - toml-dsl/specification.md: complete field reference, pipeline order, build compilation Architecture tab (4 pages): - diagrams/command-flow.md: end-to-end Mermaid — hook → RTK → LLM - diagrams/filter-pipeline.md: build pipeline + 8-stage runtime Mermaid - decisions/why-no-async.md: ADR — single-threaded, <10ms startup constraint - decisions/proxy-architecture.md: ADR — why CLI proxy over aliases/LD_PRELOAD/hooks-only All pages carry valid frontmatter (title, description, sidebar.order). No existing files modified. Co-Authored-By: Claude Sonnet 4.6 Signed-off-by: Florian BRUNIAUX --- .../decisions/proxy-architecture.md | 68 +++++++++ docs/architecture/decisions/why-no-async.md | 52 +++++++ docs/architecture/diagrams/command-flow.md | 65 ++++++++ docs/architecture/diagrams/filter-pipeline.md | 71 +++++++++ docs/guide/analytics/discover.md | 49 ++++++ docs/guide/analytics/economics.md | 54 +++++++ docs/guide/commands/containers.md | 90 +++++++++++ docs/guide/commands/data.md | 103 +++++++++++++ docs/guide/commands/dotnet.md | 71 +++++++++ docs/guide/commands/github-cli.md | 94 ++++++++++++ docs/guide/commands/go.md | 54 +++++++ docs/guide/commands/ruby.md | 45 ++++++ docs/guide/commands/utilities.md | 66 ++++++++ docs/guide/filters/creating-filters.md | 105 +++++++++++++ .../contributing/coding-standards.md | 128 ++++++++++++++++ docs/reference/contributing/guide.md | 102 +++++++++++++ docs/reference/contributing/security.md | 83 ++++++++++ docs/reference/contributing/testing.md | 142 ++++++++++++++++++ docs/reference/internals/command-routing.md | 130 ++++++++++++++++ docs/reference/internals/filter-pipeline.md | 81 ++++++++++ docs/reference/internals/hook-engine.md | 102 +++++++++++++ docs/reference/internals/tracking-system.md | 120 +++++++++++++++ docs/reference/toml-dsl/specification.md | 124 +++++++++++++++ 23 files changed, 1999 insertions(+) create mode 100644 docs/architecture/decisions/proxy-architecture.md create mode 100644 docs/architecture/decisions/why-no-async.md create mode 100644 docs/architecture/diagrams/command-flow.md create mode 100644 docs/architecture/diagrams/filter-pipeline.md create mode 100644 docs/guide/analytics/discover.md create mode 100644 docs/guide/analytics/economics.md create mode 100644 docs/guide/commands/containers.md create mode 100644 docs/guide/commands/data.md create mode 100644 docs/guide/commands/dotnet.md create mode 100644 docs/guide/commands/github-cli.md create mode 100644 docs/guide/commands/go.md create mode 100644 docs/guide/commands/ruby.md create mode 100644 docs/guide/commands/utilities.md create mode 100644 docs/guide/filters/creating-filters.md create mode 100644 docs/reference/contributing/coding-standards.md create mode 100644 docs/reference/contributing/guide.md create mode 100644 docs/reference/contributing/security.md create mode 100644 docs/reference/contributing/testing.md create mode 100644 docs/reference/internals/command-routing.md create mode 100644 docs/reference/internals/filter-pipeline.md create mode 100644 docs/reference/internals/hook-engine.md create mode 100644 docs/reference/internals/tracking-system.md create mode 100644 docs/reference/toml-dsl/specification.md diff --git a/docs/architecture/decisions/proxy-architecture.md b/docs/architecture/decisions/proxy-architecture.md new file mode 100644 index 000000000..7c2ccd9ba --- /dev/null +++ b/docs/architecture/decisions/proxy-architecture.md @@ -0,0 +1,68 @@ +--- +title: Proxy Architecture +description: ADR — why RTK uses a CLI proxy pattern instead of shell aliases or wrappers +sidebar: + order: 2 +--- + +# ADR: Proxy Architecture + +## Decision + +RTK is a CLI proxy: a single binary that intercepts commands, executes them as subprocesses, filters the output, and exits. Users invoke `rtk git status` instead of `git status`. + +Hooks extend this by making the interception transparent — the AI agent's command is rewritten before execution, so neither the agent nor the user types `rtk`. + +## Alternatives considered + +### Shell aliases + +```bash +alias git='rtk git' +alias cargo='rtk cargo' +``` + +**Rejected because:** +- Requires shell configuration per-user, per-machine, per-shell +- Doesn't work for non-interactive contexts (scripts, CI, AI agents) +- Can't be installed programmatically without modifying shell dotfiles +- Breaks if the user has other aliases or functions with the same name + +### Shell function wrappers + +Similar to aliases but more fragile. Same problems. + +### `LD_PRELOAD` / dynamic linking interception + +**Rejected because:** +- Platform-specific (Linux only with glibc) +- Security restrictions (macOS SIP, container environments) +- Complex to implement, maintain, and debug + +### Hook-only approach (no explicit `rtk` prefix) + +Make RTK entirely invisible — install hooks and never expose `rtk ` as a user-facing interface. + +**Rejected because:** +- Users need a way to invoke RTK explicitly for debugging (`rtk git status -vvv`) +- `rtk gain` and `rtk discover` need a namespace +- Transparent hooks are additive, not a replacement for the explicit interface + +## Why the proxy pattern works + +**Single binary, no configuration:** `rtk git status` works identically on macOS, Linux, and Windows. No dotfiles. No shell-specific setup. + +**Explicit and debuggable:** `-v`/`-vv`/`-vvv` flags expose what RTK is doing at each phase. `RTK_DISABLED=1` bypasses it for one command. + +**Exit code preservation:** RTK propagates the underlying tool's exit code. CI pipelines that check `$?` work correctly. + +**Fail-safe:** If RTK's filter fails, it falls back to raw output. The user always gets a result. + +**Hook interception as an enhancement:** The hook layer adds transparency on top of the proxy pattern — it rewrites `git status` to `rtk git status` before the agent sees it. But the proxy interface remains available for direct use, debugging, and tools that can't be hooked. + +## Consequences + +- Every supported command needs a module in `src/cmds/` +- Unsupported commands pass through transparently (no breakage) +- The binary grows as new commands are added (~5MB currently, well within the `<5MB` soft target after stripping) +- Adding a new command = adding a module + registering in `main.rs` + adding rewrite pattern in `discover/registry.rs` diff --git a/docs/architecture/decisions/why-no-async.md b/docs/architecture/decisions/why-no-async.md new file mode 100644 index 000000000..299c51a9f --- /dev/null +++ b/docs/architecture/decisions/why-no-async.md @@ -0,0 +1,52 @@ +--- +title: Why No Async +description: ADR — why RTK is single-threaded and does not use tokio or async runtimes +sidebar: + order: 1 +--- + +# ADR: Why No Async + +## Decision + +RTK is single-threaded. No `tokio`, `async-std`, or `futures`. All I/O is blocking. + +## Context + +RTK is a CLI proxy that runs for milliseconds and exits. The typical invocation: + +1. Parse CLI arguments (~0.1ms) +2. Spawn one subprocess and capture its output (~2-5ms) +3. Filter the output (~0.1ms) +4. Print and exit + +There is no concurrent I/O, no network server, no parallel request handling. + +## Consequences + +**Why async would hurt:** + +- `tokio` adds 5-10ms to startup time from runtime initialization. RTK's target is `<10ms` total. Async would consume half the budget before the first useful line of code. +- The entire value proposition of RTK is zero-overhead transparency. If developers perceive any delay, they disable it. +- One subprocess. One output stream. No concurrency needed. + +**Why blocking I/O is correct here:** + +- `std::process::Command::output()` captures stdout + stderr in one blocking call. This is exactly what RTK needs. +- No event loop required. No `.await` noise in filter code. +- Binary stays under 5MB. No runtime dependencies. + +## Tradeoffs + +**What we give up:** +- Hypothetical future parallelism (e.g., running multiple filters in parallel). Not needed today. +- Async ecosystem crates (reqwest, sqlx). RTK uses `rusqlite` (sync) and `ureq` (sync) instead. + +**What we gain:** +- `<10ms` startup, always. +- Simple, readable filter code with no `.await` punctuation. +- No runtime initialization path that can fail. + +## Rule + +If you add a dependency that pulls in `tokio` or any async runtime, the PR will be rejected. Check before adding: `cargo tree | grep tokio`. diff --git a/docs/architecture/diagrams/command-flow.md b/docs/architecture/diagrams/command-flow.md new file mode 100644 index 000000000..6d8f06267 --- /dev/null +++ b/docs/architecture/diagrams/command-flow.md @@ -0,0 +1,65 @@ +--- +title: Command Flow +description: End-to-end diagram of how a command flows through RTK from agent to LLM +sidebar: + order: 1 +--- + +# Command Flow + +End-to-end flow from the AI agent issuing a command to the filtered output reaching the LLM. + +## With hook (transparent rewrite) + +```mermaid +flowchart TD + A["AI Agent\n(Claude Code, Cursor, etc.)"] -->|"runs: cargo test"| B + + subgraph HOOK ["Hook Interception (PreToolUse)"] + B["Hook reads JSON input\nextract command string"] --> C + C["rtk rewrite 'cargo test'"] --> D + D{"Registry match?"} + D -->|"yes"| E["returns 'rtk cargo test'"] + D -->|"no match"| F["returns original unchanged"] + end + + E --> G + F --> G + + subgraph RTK ["RTK Binary"] + G["Phase 1: Parse\nClap → Commands::Cargo"] --> H + H["Phase 2: Route\ncargo::run(args)"] --> I + I["Phase 3: Execute\nstd::process::Command::new('cargo')\n.args(['test'])"] --> J + J["Phase 4: Filter\nfailures only\n200 lines → 5 lines"] --> K + K["Phase 5: Print\nprintln!(filtered)"] --> L + L["Phase 6: Track\nSQLite INSERT\n(input=5000tok, output=50tok)"] + end + + K -->|"filtered output"| M["LLM Context\n~90% fewer tokens"] +``` + +## Without hook (direct usage) + +```mermaid +flowchart LR + A["Developer\ntype: rtk git status"] --> B["RTK Binary"] + B --> C["git status (subprocess)"] + C -->|"20 lines raw"| B + B -->|"5 lines filtered"| D["Terminal\n(or LLM context)"] +``` + +## Filter lookup (TOML path) + +```mermaid +flowchart LR + CMD["rtk my-tool args"] --> P1 + P1{"1. .rtk/filters.toml\n(project-local)"} + P1 -->|"match"| WIN["apply filter → print"] + P1 -->|"no match"| P2 + P2{"2. ~/.config/rtk/filters.toml\n(user-global)"} + P2 -->|"match"| WIN + P2 -->|"no match"| P3 + P3{"3. BUILTIN_TOML\n(binary)"} + P3 -->|"match"| WIN + P3 -->|"no match"| P4[["exec raw\n(passthrough)"]] +``` diff --git a/docs/architecture/diagrams/filter-pipeline.md b/docs/architecture/diagrams/filter-pipeline.md new file mode 100644 index 000000000..dda20e9d4 --- /dev/null +++ b/docs/architecture/diagrams/filter-pipeline.md @@ -0,0 +1,71 @@ +--- +title: Filter Pipeline +description: How a TOML filter goes from file to execution — build pipeline and runtime stages +sidebar: + order: 2 +--- + +# Filter Pipeline + +## Build pipeline + +```mermaid +flowchart TD + A[["src/filters/my-tool.toml\n(new file)"]] --> B + + subgraph BUILD ["cargo build"] + B["build.rs\n1. ls src/filters/*.toml\n2. sort alphabetically\n3. concat → BUILTIN_TOML"] --> C + C{"TOML valid?\nDuplicate names?"} -->|"❌ panic"| D[["Build fails\nerror points to bad file"]] + C -->|"✅ ok"| E[["OUT_DIR/builtin_filters.toml\n(generated)"]] + E --> F["rustc embeds via include_str!"] + F --> G[["rtk binary\nBUILTIN_TOML embedded"]] + end + + subgraph TESTS ["cargo test"] + H["test_builtin_filter_count\nassert_eq!(filters.len(), N)"] -->|"❌ wrong count"| I[["FAIL"]] + J["test_builtin_all_filters_present\nassert!(names.contains('my-tool'))"] -->|"❌ name missing"| K[["FAIL"]] + L["test_builtin_all_filters_have_inline_tests\nassert!(tested.contains(name))"] -->|"❌ no tests"| M[["FAIL"]] + end + + subgraph VERIFY ["rtk verify"] + N["runs [[tests.my-tool]]\ninput → filter → compare expected"] + N -->|"❌ mismatch"| O[["FAIL\nshows actual vs expected"]] + N -->|"✅ pass"| P[["All tests passed"]] + end + + G --> H & J & L & N +``` + +## Runtime stages + +```mermaid +flowchart TD + CMD["rtk my-tool args"] --> LOOKUP + + subgraph LOOKUP ["Filter Lookup"] + L1{".rtk/filters.toml"} -->|"match"| APPLY + L1 -->|"no match"| L2 + L2{"~/.config/rtk/filters.toml"} -->|"match"| APPLY + L2 -->|"no match"| L3 + L3{"BUILTIN_TOML"} -->|"match"| APPLY + L3 -->|"no match"| RAW[["exec raw (passthrough)"]] + end + + APPLY --> EXEC["exec command\ncapture stdout"] + EXEC --> PIPE + + subgraph PIPE ["8-stage filter pipeline"] + S1["1. strip_ansi"] --> S2 + S2["2. replace"] --> S3 + S3{"3. match_output\nshort-circuit?"} + S3 -->|"✅ match"| MSG[["emit on_match\nstop"]] + S3 -->|"no match"| S4 + S4["4. strip/keep_lines"] --> S5 + S5["5. truncate_lines_at"] --> S6 + S6["6. tail_lines"] --> S7 + S7["7. max_lines"] --> S8 + S8{"8. output empty?"} + S8 -->|"yes"| EMPTY[["emit on_empty"]] + S8 -->|"no"| OUT[["print filtered output\n+ exit code"]] + end +``` diff --git a/docs/guide/analytics/discover.md b/docs/guide/analytics/discover.md new file mode 100644 index 000000000..bcbd248ec --- /dev/null +++ b/docs/guide/analytics/discover.md @@ -0,0 +1,49 @@ +--- +title: Discover Missed Savings +description: Find commands that ran without RTK and could have saved tokens +sidebar: + order: 2 +--- + +# Discover Missed Savings + +`rtk discover` analyzes Claude Code's command history to find commands that ran without RTK and could have been optimized. + +## Usage + +```bash +rtk discover # current project, last 30 days +rtk discover --all --since 7 # all projects, last 7 days +rtk discover -p /path/to/project # filter by project path +rtk discover --limit 20 # max commands per section +rtk discover --format json # JSON export +``` + +## Options + +| Option | Short | Description | +|--------|-------|-------------| +| `--project` | `-p` | Filter by project path | +| `--limit` | `-l` | Max commands per section (default: 15) | +| `--all` | `-a` | Scan all projects | +| `--since` | `-s` | Last N days (default: 30) | +| `--format` | `-f` | Output format: `text`, `json` | + +## Example output + +``` +RTK Missed Opportunities (last 30 days) + +Commands that could have used RTK: + git log --oneline -20 ×12 (est. 80% savings each) + cargo test ×8 (est. 90% savings each) + pnpm list ×5 (est. 70% savings each) + +Estimated savings missed: ~340K tokens +``` + +## How it works + +RTK reads Claude Code's command history database (the same one that backs `rtk gain --history`). It matches raw commands against the RTK rewrite registry and flags instances where RTK was not used but a filter exists. + +Use this after setting up RTK to see how much you were leaving on the table before. diff --git a/docs/guide/analytics/economics.md b/docs/guide/analytics/economics.md new file mode 100644 index 000000000..3287d6915 --- /dev/null +++ b/docs/guide/analytics/economics.md @@ -0,0 +1,54 @@ +--- +title: Claude Code Economics +description: Compare your Claude Code spending against RTK token savings +sidebar: + order: 3 +--- + +# Claude Code Economics + +`rtk cc-economics` compares your Claude Code API spending (via ccusage) against the token savings RTK has generated. It answers: "what is RTK actually saving me in dollars?" + +## Requirements + +Requires [ccusage](https://github.com/ryoppippi/ccusage) to be installed and have data. ccusage tracks Claude Code API costs from your usage history. + +## Usage + +```bash +rtk cc-economics # summary +rtk cc-economics --daily # day-by-day breakdown +rtk cc-economics --weekly # week-by-week +rtk cc-economics --monthly # month-by-month +rtk cc-economics --all # all breakdowns at once +rtk cc-economics --format json +``` + +## Example output + +``` +Claude Code Economics +════════════════════════════════════════ +Total API cost $12.40 (30 days) +RTK tokens saved 1.2M (30 days) +Estimated savings $3.20 (26% of bill) + +At current savings rate: + Monthly reduction: ~$3.20/mo + Annual reduction: ~$38/yr +``` + +## How savings are estimated + +RTK estimates dollar savings by applying Claude's input token pricing to the tokens it prevented from reaching the LLM: + +``` +Saved tokens × (input price per token) = estimated dollar savings +``` + +This is an estimate — actual savings depend on which model was used for each request. + +## See also + +- [Token Savings Analytics](./gain.md) — the `rtk gain` command for raw token counts +- [Discover Missed Savings](./discover.md) — find commands that ran without RTK diff --git a/docs/guide/commands/containers.md b/docs/guide/commands/containers.md new file mode 100644 index 000000000..c5d6a52c1 --- /dev/null +++ b/docs/guide/commands/containers.md @@ -0,0 +1,90 @@ +--- +title: Containers +description: RTK filters for Docker and Kubernetes +sidebar: + order: 9 +--- + +# Containers + +RTK compresses Docker and Kubernetes command output into compact, token-efficient summaries. + +## Docker + +### docker ps — 80% savings + +```bash +rtk docker ps [args...] +``` + +**Before:** +``` +CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES +abc123def456 nginx:1.25 "/docker-entrypoint.…" 2 days ago Up 2 days (healthy) 0.0.0.0:80->80/tcp web +789012345678 postgres:16 "docker-entrypoint.s…" 2 days ago Up 2 days (healthy) 0.0.0.0:5432->5432/tcp db +``` + +**After:** +``` +web nginx:1.25 Up 2d (healthy) +db postgres:16 Up 2d (healthy) +``` + +### docker images — 80% savings + +```bash +rtk docker images [args...] +``` + +Compact list: name, tag, size, age. + +### docker logs — 70% savings + +```bash +rtk docker logs [args...] +``` + +Deduplicates repeated log lines: `[ERROR] Connection refused (×42)`. + +### Docker Compose + +```bash +rtk docker compose ps # compact service list — 80% +rtk docker compose logs [service] # deduplicated logs — 70% +rtk docker compose build [service] # build summary — 60% +``` + +Unrecognized `docker compose` subcommands pass through. + +## Kubernetes + +### kubectl pods — 75%+ savings + +```bash +rtk kubectl pods [-n namespace] [-A] +``` + +Compact pod list with status. + +### kubectl services + +```bash +rtk kubectl services [-n namespace] [-A] +``` + +### kubectl logs — 70% savings + +```bash +rtk kubectl logs [-c container] +``` + +Deduplicates repeated log lines. + +### Passthrough + +All other `kubectl` subcommands pass through to kubectl: + +```bash +rtk kubectl apply -f deployment.yaml # passes through +rtk kubectl describe pod # passes through +``` diff --git a/docs/guide/commands/data.md b/docs/guide/commands/data.md new file mode 100644 index 000000000..239e1aa2e --- /dev/null +++ b/docs/guide/commands/data.md @@ -0,0 +1,103 @@ +--- +title: Data & Network +description: RTK filters for JSON, environment variables, logs, curl, and wget +sidebar: + order: 11 +--- + +# Data & Network + +RTK compresses structured data, network output, and log streams. + +## json — 60% savings + +```bash +rtk json [--depth N] # default: depth 5 +rtk json - # from stdin +``` + +Shows JSON structure (keys and types) without values. Useful for exploring large API responses or config files. + +**Before — `cat package.json` (50 lines):** +```json +{ + "name": "my-app", + "version": "1.0.0", + "dependencies": { + "react": "^18.2.0", + "next": "^14.0.0", + ...15 dependencies... + } +} +``` + +**After — `rtk json package.json` (10 lines):** +``` +{ + name: string + version: string + dependencies: { 15 keys } + devDependencies: { 8 keys } + scripts: { 6 keys } +} +``` + +## env — sensitive values masked + +```bash +rtk env # all variables (sensitive values masked) +rtk env -f AWS # filter by name +rtk env --show-all # include sensitive values +``` + +Sensitive variables (tokens, secrets, passwords) are masked by default: `AWS_SECRET_ACCESS_KEY=***`. + +## log — 60-80% savings + +```bash +rtk log # from a file +rtk log # from stdin (pipe) +``` + +Deduplicates repeated log lines: `[ERROR] Connection refused (×42)`. Savings depend on how repetitive the log is. + +## curl — HTTP with JSON detection + +```bash +rtk curl [args...] +``` + +Auto-detects JSON responses and shows schema instead of full content. Falls back to raw output for non-JSON. + +## wget + +```bash +rtk wget [args...] +rtk wget -O - # output to stdout +``` + +Removes progress bars and download noise. + +## aws + +```bash +rtk aws [args...] +``` + +Forces JSON output mode and compresses the result. Supports all AWS services (sts, s3, ec2, ecs, rds, cloudformation, etc.). + +## psql + +```bash +rtk psql [args...] +``` + +Removes table borders and compresses query output. + +## summary + +```bash +rtk summary +``` + +Runs any command and generates a heuristic summary of the output. Useful for commands that don't have a dedicated RTK filter. diff --git a/docs/guide/commands/dotnet.md b/docs/guide/commands/dotnet.md new file mode 100644 index 000000000..08d93d05e --- /dev/null +++ b/docs/guide/commands/dotnet.md @@ -0,0 +1,71 @@ +--- +title: .NET +description: RTK filters for dotnet build, test, and MSBuild logs +sidebar: + order: 8 +--- + +# .NET + +RTK covers .NET build, test, and diagnostic outputs. + +## dotnet build — 70-80% savings + +```bash +rtk dotnet build [args...] +``` + +Removes per-project compilation lines, keeps errors and build summary. + +**Before:** +``` +Build started... +Microsoft (R) Build Engine version 17.x + Restore complete (1.2s) + MyLib -> bin/Debug/net8.0/MyLib.dll + MyApp -> bin/Debug/net8.0/MyApp.dll + +Build succeeded. + 0 Warning(s) + 0 Error(s) +Time Elapsed 00:00:04.23 +``` + +**After:** +``` +Build succeeded. 0 warnings, 0 errors (4.23s) +``` + +## dotnet test — 85% savings + +```bash +rtk dotnet test [args...] +``` + +Shows failures only. On success, compact summary. + +## MSBuild binary logs + +```bash +rtk dotnet binlog [path/to/file.binlog] +``` + +Parses `.binlog` binary log files and displays a compact error/warning summary. + +## dotnet format + +```bash +rtk dotnet format [args...] +``` + +Shows only files that were reformatted or have formatting issues. + +## Passthrough + +Other `dotnet` subcommands pass through unchanged: + +```bash +rtk dotnet run # passes through +rtk dotnet publish # passes through +rtk dotnet ef migrate # passes through +``` diff --git a/docs/guide/commands/github-cli.md b/docs/guide/commands/github-cli.md new file mode 100644 index 000000000..5367942f8 --- /dev/null +++ b/docs/guide/commands/github-cli.md @@ -0,0 +1,94 @@ +--- +title: GitHub CLI +description: RTK filters for gh — pull requests, issues, checks, and workflow runs +sidebar: + order: 10 +--- + +# GitHub CLI + +RTK filters `gh` output by removing ASCII art, verbose metadata, and decorative formatting, keeping the information that matters. + +## pr list — 80% savings + +```bash +rtk gh pr list [args...] +``` + +**Before (~30 lines):** +``` +Showing 3 of 3 pull requests in org/repo + +#42 feat: add vitest support + user1 opened about 2 days ago + Labels: enhancement + Review: 0 approving, 0 requesting changes + +#41 fix: git diff crash + user2 opened about 3 days ago + ... +``` + +**After (~6 lines):** +``` +#42 feat: add vitest (open, 2d) +#41 fix: git diff crash (open, 3d) +#40 chore: update deps (merged, 5d) +``` + +## pr view — 87% savings + +```bash +rtk gh pr view [args...] +``` + +Compact PR summary: title, status, author, description excerpt, and CI checks in one block. + +## pr checks — 79% savings + +```bash +rtk gh pr checks [args...] +``` + +Shows check name + status only, strips URLs and timestamps. + +## issue list — 80% savings + +```bash +rtk gh issue list [args...] +``` + +Same compact format as pr list. + +## run list — 82% savings + +```bash +rtk gh run list [args...] +``` + +Workflow run name + status + duration, one line each. + +## api + +```bash +rtk gh api [args...] +``` + +~26% savings — strips HTTP headers and formats JSON output. + +## Stacked PRs (Graphite) + +```bash +rtk gt log # compact stack log +rtk gt submit # compact submit output +rtk gt sync # compact sync +rtk gt restack # compact restack +rtk gt create # compact create +rtk gt branch # compact branch info +``` + +Unrecognized `gt` subcommands pass through to Graphite or git. + +## Passthrough + +Other `gh` subcommands pass through to the GitHub CLI unchanged. diff --git a/docs/guide/commands/go.md b/docs/guide/commands/go.md new file mode 100644 index 000000000..e0b9d8ce5 --- /dev/null +++ b/docs/guide/commands/go.md @@ -0,0 +1,54 @@ +--- +title: Go +description: RTK filters for go test and golangci-lint +sidebar: + order: 6 +--- + +# Go + +RTK covers Go's two most verbose outputs: test runs and the linter. + +## go test — 90% savings + +```bash +rtk go test [args...] +``` + +Parses Go's NDJSON streaming output for precise failure filtering. Shows failures only on failure, compact summary on success. + +Uses `go test -json` internally for reliable event-by-event parsing. + +## golangci-lint — 85% savings + +```bash +rtk golangci-lint run [args...] +``` + +Compressed JSON output grouped by linter and file. + +**Before (verbose lint output):** +``` +src/main.go:42:5: Error return value of `fmt.Fprintf` is not checked (errcheck) +src/main.go:67:1: exported function `ProcessData` should have comment (godot) +src/handler.go:15:9: G104: Errors unhandled. (gosec) +... +``` + +**After:** +``` +src/main.go (2 issues) + errcheck: Error return not checked (x1) + godot: Missing comment on exported func (x1) +src/handler.go (1 issue) + gosec/G104: Errors unhandled (x1) +``` + +## go build + +Unrecognized `go` subcommands pass through directly: + +```bash +rtk go build ./... # passes through to go build +rtk go vet ./... # passes through to go vet +``` diff --git a/docs/guide/commands/ruby.md b/docs/guide/commands/ruby.md new file mode 100644 index 000000000..5e1a4ed28 --- /dev/null +++ b/docs/guide/commands/ruby.md @@ -0,0 +1,45 @@ +--- +title: Ruby +description: RTK filters for rake, rspec, and rubocop +sidebar: + order: 7 +--- + +# Ruby + +RTK covers Ruby's core development tools: build tasks, test output, and linting. + +## rspec — 60-90% savings + +```bash +rtk rspec [args...] +``` + +Shows failures only. On success, compact summary. + +## rubocop — 80%+ savings + +```bash +rtk rubocop [args...] +``` + +Groups violations by cop and file. + +## rake — 60-80% savings + +```bash +rtk rake [args...] +rtk rake test +rtk rake spec +``` + +Filters task execution noise, keeps errors and final result. + +## Passthrough + +Unrecognized rake tasks pass through to rake directly: + +```bash +rtk rake db:migrate # passes through unchanged +rtk rake assets:precompile +``` diff --git a/docs/guide/commands/utilities.md b/docs/guide/commands/utilities.md new file mode 100644 index 000000000..02ff3dc31 --- /dev/null +++ b/docs/guide/commands/utilities.md @@ -0,0 +1,66 @@ +--- +title: Utilities +description: RTK utility commands — proxy passthrough, analytics, global flags +sidebar: + order: 12 +--- + +# Utilities + +Utility commands that apply across all ecosystems. + +## proxy — passthrough with tracking + +```bash +rtk proxy +``` + +Runs any command without filtering but records it in the token savings database. Useful when you need raw output but still want usage tracked. + +```bash +rtk proxy git log --oneline -20 # full git log, no filtering +rtk proxy npm install express # raw npm output +rtk proxy curl https://api.example.com/data +``` + +All proxy commands appear in `rtk gain --history` with 0% savings (input = output). + +## Global flags + +These flags apply to every RTK command: + +| Flag | Description | +|------|-------------| +| `-v` | Debug messages | +| `-vv` | Show command being executed | +| `-vvv` | Show raw output before filtering | +| `-u` / `--ultra-compact` | Maximum compression (ASCII icons, inline format) | + +**Verbosity example:** +```bash +rtk git status -vvv +# shows the raw git status output before RTK filters it +``` + +**Ultra-compact example:** +```bash +rtk git push -u +# output: ✓ main (vs "ok main" in normal mode) +``` + +## RTK_DISABLED — per-command override + +```bash +RTK_DISABLED=1 git status # runs raw git status, no rewrite +``` + +## Passthrough behavior + +Any command RTK doesn't recognize is executed unchanged and the output passes through: + +```bash +rtk make install # runs make install verbatim +rtk terraform plan # runs terraform plan verbatim (unless a TOML filter matches) +``` + +The exit code from the underlying command is always preserved. diff --git a/docs/guide/filters/creating-filters.md b/docs/guide/filters/creating-filters.md new file mode 100644 index 000000000..2203d3a8d --- /dev/null +++ b/docs/guide/filters/creating-filters.md @@ -0,0 +1,105 @@ +--- +title: Creating Filters +description: Write TOML filters to compress output from any command RTK doesn't cover yet +sidebar: + order: 2 +--- + +# Creating Filters + +TOML filters let you add RTK support for any command without writing Rust. They work well for tools with predictable, line-by-line text output where regex filtering achieves 60%+ savings. + +## When to use a TOML filter + +TOML works well for: +- Install/update logs (brew, composer, poetry) — strip `Using ...` / `Already installed` lines +- System monitoring (df, ps, systemctl) — keep essential rows, drop headers +- Simple linters (shellcheck, yamllint, hadolint) — strip context, keep findings +- Infra tools (terraform plan, helm, rsync) — strip progress, keep summary + +For commands with structured JSON output, state machine parsing, or complex routing, a Rust module is needed instead. + +## Create a project-local filter + +Create `.rtk/filters.toml` in your project root: + +```toml +[filters.my-tool] +description = "Strip progress noise from my-tool output" +match_command = "^my-tool\\b" +strip_ansi = true +strip_lines_matching = [ + "^Loading", + "^Scanning", + "^\\s*$", +] +max_lines = 50 +on_empty = "my-tool: nothing to report" + +[[tests.my-tool]] +name = "strips progress lines" +input = "Loading plugins...\nScan complete: 3 issues\nWarning: foo at line 42" +expected = "Scan complete: 3 issues\nWarning: foo at line 42" +``` + +Verify: + +```bash +rtk verify +``` + +## Filter fields reference + +| Field | Type | Description | +|-------|------|-------------| +| `description` | string | Human-readable description | +| `match_command` | regex | Matched against full command string | +| `strip_ansi` | bool | Strip ANSI escape codes first | +| `strip_lines_matching` | regex[] | Drop lines matching any of these | +| `keep_lines_matching` | regex[] | Keep only lines matching at least one | +| `replace` | array | Regex substitutions (`{ pattern, replacement }`) | +| `match_output` | array | Short-circuit rules (`{ pattern, message }`) | +| `truncate_lines_at` | int | Truncate lines longer than N chars | +| `max_lines` | int | Keep only the first N lines | +| `tail_lines` | int | Keep only the last N lines | +| `on_empty` | string | Message when output is empty after filtering | + +## Naming convention + +Use the command name as the filter key: `terraform-plan`, `docker-inspect`, `mix-compile`. For commands with subcommands, prefer `cmd-subcommand` over grouping multiple filters together. + +## Filter lookup order + +Project filters override user-global filters, which override built-ins: + +``` +1. .rtk/filters.toml (project-local — highest priority) +2. ~/.config/rtk/filters.toml (user-global) +3. BUILTIN_TOML (embedded in binary — lowest priority) +``` + +A project filter with the same key as a built-in shadows the built-in with a warning: + +``` +[rtk] warning: filter 'make' is shadowing a built-in filter +``` + +## Writing inline tests + +Always add at least one `[[tests.my-tool]]` entry. Tests run via `rtk verify` and also during `cargo test` (for built-in filters). + +```toml +[[tests.my-tool]] +name = "normal output" +input = "Progress: 10%\nDone. 3 findings." +expected = "Done. 3 findings." + +[[tests.my-tool]] +name = "empty output" +input = "Progress: 10%\nProgress: 100%" +expected = "my-tool: nothing to report" +``` + +## Contributing a filter upstream + +To add a filter to RTK's built-in set, see the [Contributing guide](../../reference/contributing/guide.md) for the full checklist (register in `discover/rules.rs`, update filter count in tests, write fixture). diff --git a/docs/reference/contributing/coding-standards.md b/docs/reference/contributing/coding-standards.md new file mode 100644 index 000000000..0670d563c --- /dev/null +++ b/docs/reference/contributing/coding-standards.md @@ -0,0 +1,128 @@ +--- +title: Coding Standards +description: Rust patterns, error handling rules, and anti-patterns for RTK development +sidebar: + order: 3 +--- + +# Coding Standards + +RTK-specific Rust constraints. These override general Rust conventions. + +## Non-negotiable rules + +1. **No async** — Zero `tokio`, `async-std`, `futures`. Single-threaded by design. Async adds 5-10ms startup. +2. **No `.unwrap()` in production** — Use `.context("description")?`. Tests: use `.expect("reason")`. +3. **Lazy regex** — `Regex::new()` inside a function recompiles on every call. Always `lazy_static!`. +4. **Fallback pattern** — If filter fails, execute raw command unchanged. Never block the user. +5. **Exit code propagation** — `std::process::exit(code)` if underlying command fails. + +## Error handling + +```rust +use anyhow::{Context, Result}; + +// ✅ Correct +fn read_config(path: &Path) -> Result { + let content = fs::read_to_string(path) + .with_context(|| format!("Failed to read config: {}", path.display()))?; + toml::from_str(&content).context("Failed to parse config TOML") +} + +// ❌ Wrong — no context +fn read_config(path: &Path) -> Result { + let content = fs::read_to_string(path)?; + Ok(toml::from_str(&content)?) +} +``` + +## Fallback pattern (mandatory for all filters) + +```rust +pub fn run(args: MyArgs) -> Result<()> { + let output = execute_command("mycmd", &args.to_cmd_args()) + .context("Failed to execute mycmd")?; + + let filtered = filter_output(&output.stdout) + .unwrap_or_else(|e| { + eprintln!("rtk: filter warning: {}", e); + output.stdout.clone() // passthrough on failure + }); + + tracking::record("mycmd", &output.stdout, &filtered)?; + print!("{}", filtered); + + if !output.status.success() { + std::process::exit(output.status.code().unwrap_or(1)); + } + Ok(()) +} +``` + +## Regex — always lazy_static + +```rust +use lazy_static::lazy_static; +use regex::Regex; + +lazy_static! { + static ref ERROR_RE: Regex = Regex::new(r"^error\[").unwrap(); + static ref HASH_RE: Regex = Regex::new(r"^[0-9a-f]{7,40}").unwrap(); +} + +// ✅ Compiled once at first use +fn is_error_line(line: &str) -> bool { + ERROR_RE.is_match(line) +} + +// ❌ Recompiles on every call +fn is_error_line(line: &str) -> bool { + let re = Regex::new(r"^error\[").unwrap(); + re.is_match(line) +} +``` + +Note: `lazy_static!` with `.unwrap()` for initialization is the established RTK pattern — acceptable because a bad regex literal is a programming error caught at first use. + +## Module structure + +Every `*_cmd.rs` follows this pattern: + +```rust +// 1. Imports +use anyhow::{Context, Result}; +use lazy_static::lazy_static; +use regex::Regex; + +// 2. Args struct +pub struct MyArgs { ... } + +// 3. Lazy regexes +lazy_static! { static ref MY_RE: Regex = ...; } + +// 4. Public entry point +pub fn run(args: MyArgs) -> Result<()> { ... } + +// 5. Private filter functions +fn filter_output(input: &str) -> Result { ... } + +// 6. Tests (always present) +#[cfg(test)] +mod tests { + use super::*; + fn count_tokens(s: &str) -> usize { s.split_whitespace().count() } + // snapshot tests, savings tests... +} +``` + +## Anti-patterns + +| Pattern | Problem | Fix | +|---------|---------|-----| +| `Regex::new()` in function | Recompiles every call | `lazy_static!` | +| `.unwrap()` in production | Panic breaks user workflow | `.context()?` | +| `tokio::main` or `async fn` | +5-10ms startup | Blocking I/O only | +| `Err(_) => {}` | User gets no output | Log warning + fallback | +| `println!` in filter path | Debug artifact in output | Use `eprintln!` | +| Early return without exit code | CI thinks command succeeded | `std::process::exit(code)` | +| `.clone()` of large strings in hot path | Extra allocation | Borrow with `&str` | diff --git a/docs/reference/contributing/guide.md b/docs/reference/contributing/guide.md new file mode 100644 index 000000000..e37a6a48c --- /dev/null +++ b/docs/reference/contributing/guide.md @@ -0,0 +1,102 @@ +--- +title: Contributing Guide +description: How to contribute to RTK — design philosophy, PR process, testing, and documentation +sidebar: + order: 1 +--- + +# Contributing Guide + +## Design philosophy + +Four principles guide every RTK design decision. + +### Correctness over savings + +When a user explicitly requests detailed output via flags (e.g., `--nocapture`, `--verbose`, `-la`), respect that intent. Filters should be flag-aware: default output gets compressed, explicit verbose flags pass through more content. + +> `rtk cargo test` shows failures only (90% savings). `rtk cargo test -- --nocapture` preserves all output. + +### Transparency + +RTK's output must be a valid, useful subset of the original tool's output. The filtered version should be indistinguishable from "a shorter version of the real command." Don't invent new formats or add RTK-specific headers in default output. + +### Never block + +If a filter fails, fall back to raw output. RTK must never prevent a command from executing. Every filter needs a fallback path. Every hook must handle malformed input gracefully and exit 0. + +### Zero overhead + +`<10ms` startup. No async runtime. No config file I/O on the critical path. Use `lazy_static!` for all regex. No network calls in the hot path. Benchmark before/after with `hyperfine`. + +## What belongs in RTK? + +**In scope:** Commands that produce text output (typically 100+ tokens) compressible 60%+ without losing essential information for the LLM. + +- Test runners (vitest, pytest, cargo test, go test) +- Linters and type checkers (eslint, ruff, tsc, mypy) +- Build tools (cargo build, dotnet build, make, next build) +- VCS operations (git status/log/diff, gh pr/issue) +- Package managers (pnpm, pip, cargo install) +- File operations (ls, tree, grep, find) +- Infrastructure tools with text output (docker, kubectl, terraform) + +**Out of scope:** Interactive TUIs, binary output, trivial commands, non-text output. + +## TOML filter vs Rust module + +| Use **TOML filter** when | Use **Rust module** when | +|--------------------------|--------------------------| +| Plain text with predictable line structure | Structured output (JSON, NDJSON) | +| Regex line filtering achieves 60%+ savings | State machine parsing needed | +| No CLI flag injection needed | Must inject flags like `--format json` | +| No cross-command routing | Routes to other commands | + +## Branch naming + +| Prefix | Semver | When to use | +|--------|--------|-------------| +| `fix/` | Patch | Bug fixes, filter corrections | +| `feat/` | Minor | New filters, new command support | +| `chore/` | Major | Breaking changes, API changes | + +Examples: `fix/git-log-drops-merge-commits`, `feat/kubectl-pod-filter` + +## PR process + +1. Branch from `develop` +2. Make changes + add tests + update docs +3. Run pre-commit gate: `cargo fmt --all --check && cargo clippy --all-targets && cargo test` +4. Open PR targeting `develop` +5. Sign CLA (CLA Assistant will prompt on first PR) +6. Address review feedback +7. Maintainer merges to `develop` → eventual release to `master` + +## Testing requirements + +Every PR must include tests. Follow TDD (Red-Green-Refactor): write failing test first. + +| Type | Location | Runner | +|------|----------|--------| +| Unit tests | `#[cfg(test)] mod tests` in each module | `cargo test` | +| Snapshot tests | `assert_snapshot!()` via `insta` | `cargo test` + `cargo insta review` | +| Smoke tests | `scripts/test-all.sh` | `bash scripts/test-all.sh` | +| Integration tests | `#[ignore]` tests | `cargo test --ignored` | + +**PR testing checklist:** +- [ ] Unit tests added/updated +- [ ] Snapshot tests reviewed (`cargo insta review`) +- [ ] Token savings ≥60% verified +- [ ] `cargo fmt --all --check && cargo clippy --all-targets && cargo test` passes + +## Documentation requirements + +Every filter addition requires: +- Update `docs/guide/commands/.md` with the new command +- Update `CHANGELOG.md` under `[Unreleased]` + +For the full step-by-step checklist for adding a new command filter, see [src/cmds/README.md](https://github.com/rtk-ai/rtk/blob/master/src/cmds/README.md#adding-a-new-command-filter). + +## Contributor License Agreement + +All contributions require signing the CLA. CLA Assistant will post a comment on your first PR with a link to sign. You only need to sign once. diff --git a/docs/reference/contributing/security.md b/docs/reference/contributing/security.md new file mode 100644 index 000000000..2b7299e22 --- /dev/null +++ b/docs/reference/contributing/security.md @@ -0,0 +1,83 @@ +--- +title: Security Policy +description: How to report vulnerabilities, the PR security review process, and dangerous patterns to avoid +sidebar: + order: 2 +--- + +# Security Policy + +## Reporting a vulnerability + +Report security issues privately — do not open public GitHub issues for vulnerabilities. + +- **Email**: security@rtk-ai.dev (or create a private GitHub security advisory) +- **Response time**: Acknowledgment within 48 hours +- **Disclosure**: 90-day embargo, responsible disclosure + +## Automated security checks + +Every PR triggers `security-check.yml`: + +1. **Dependency audit** (`cargo audit`) — detects known CVEs +2. **Critical files alert** — flags modifications to high-risk files +3. **Dangerous pattern scan** — regex detection of shell execution, env manipulation, network ops, unsafe code, `.unwrap()` in production +4. **Clippy security lints** + +## High-risk files + +These files require enhanced review (2 maintainers for Tier 1): + +**Tier 1 — Shell execution & system interaction:** +- `src/runner.rs` — shell command execution engine +- `src/tracking.rs` — SQLite database operations +- `src/discover/registry.rs` — command rewrite logic +- `hooks/rtk-rewrite.sh` — intercepts all Claude Code commands + +**Tier 2 — Input validation:** +- `src/pnpm_cmd.rs` — package name validation +- `src/container.rs` — Docker/container operations + +**Tier 3 — Supply chain & CI/CD:** +- `Cargo.toml` — dependency manifest +- `.github/workflows/*.yml` — CI/CD pipelines + +## Dangerous patterns + +| Pattern | Risk | +|---------|------| +| `Command::new("sh")` | Shell injection | +| `.env("LD_PRELOAD")` | Library hijacking | +| `reqwest::`, `std::net::` | Data exfiltration | +| `unsafe {` | Memory safety bypass | +| `.unwrap()` in `src/` | DoS via panic | +| `SystemTime::now() > ...` | Logic bombs | + +**Avoid — shell injection:** +```rust +// ❌ Never do this +Command::new("sh").arg("-c").arg(format!("echo {}", user_input)).output(); + +// ✅ Direct binary execution +Command::new("echo").arg(user_input).output(); +``` + +## Dependency criteria for new crates + +- Downloads: >10,000 on crates.io +- Maintainer: verified GitHub profile + track record +- License: MIT or Apache-2.0 +- Activity: commits within 6 months +- No typosquatting (verify against similar crate names) + +## Disclosure timeline + +| Day | Action | +|-----|--------| +| 0 | Acknowledgment to reporter | +| 7 | Severity assessment | +| 14 | Patch development | +| 30 | Patch released + CVE filed (if applicable) | +| 90 | Public disclosure | + +Critical vulnerabilities (RCE, data exfiltration) may be fast-tracked. diff --git a/docs/reference/contributing/testing.md b/docs/reference/contributing/testing.md new file mode 100644 index 000000000..54054f334 --- /dev/null +++ b/docs/reference/contributing/testing.md @@ -0,0 +1,142 @@ +--- +title: Testing Strategy +description: Snapshot tests, token accuracy tests, cross-platform tests, and performance benchmarks +sidebar: + order: 4 +--- + +# Testing Strategy + +## Snapshot tests (critical) + +Use the `insta` crate for output validation. This is the primary testing strategy for RTK filters. + +```rust +use insta::assert_snapshot; + +#[test] +fn test_git_log_output() { + let input = include_str!("../tests/fixtures/git_log_raw.txt"); + let output = filter_git_log(input); + assert_snapshot!(output); +} +``` + +**Workflow:** +1. Write test with `assert_snapshot!(output)` +2. `cargo test` (creates new snapshot on first run) +3. `cargo insta review` (interactive review — press `a` to accept) +4. Snapshot saved in `src/cmds//snapshots/` + +## Token accuracy tests (critical) + +All filters must verify 60-90% savings claims with real fixtures. + +```rust +fn count_tokens(text: &str) -> usize { + text.split_whitespace().count() +} + +#[test] +fn test_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); +} +``` + +**Savings targets:** + +| Filter | Minimum | Mechanism | +|--------|---------|-----------| +| `git log` | 80% | Condense commits to hash + message | +| `cargo test` | 90% | Show failures only | +| `gh pr view` | 87% | Remove ASCII art + verbose metadata | +| `pnpm list` | 70% | Compact dependency tree | +| `docker ps` | 60% | Essential fields only | + +## Creating fixtures + +Use real command output, not synthetic data: + +```bash +git log -20 > tests/fixtures/git_log_raw.txt +cargo test 2>&1 > tests/fixtures/cargo_test_raw.txt +gh pr view 123 > tests/fixtures/gh_pr_view_raw.txt +``` + +## Cross-platform tests + +RTK must work on macOS (zsh), Linux (bash), and Windows (PowerShell). Test shell escaping with `cfg` guards: + +```rust +#[test] +fn test_shell_escaping() { + let escaped = escape_for_shell("test"); + #[cfg(target_os = "windows")] + assert_eq!(escaped, "\"test\""); + #[cfg(not(target_os = "windows"))] + assert_eq!(escaped, "test"); +} +``` + +## Performance tests + +RTK targets `<10ms` startup and `<5MB` memory. + +```bash +# Benchmark before/after changes +hyperfine 'rtk git log -10' --warmup 3 + +# Memory usage (macOS) +/usr/bin/time -l rtk git status +# "maximum resident set size" should be <5MB +``` + +## Integration tests + +Run against an installed binary with `#[ignore]`: + +```rust +#[test] +#[ignore] +fn test_real_git_log() { + let output = std::process::Command::new("rtk") + .args(&["git", "log", "-10"]) + .output() + .expect("Failed to run rtk"); + assert!(output.status.success()); + let stdout = String::from_utf8_lossy(&output.stdout); + assert!(stdout.len() < 5000, "Output too large, filter not working"); +} +``` + +Run with: `cargo test --ignored` + +## Test organization + +``` +src/cmds// + .rs # filter + embedded unit tests + snapshots/ # insta snapshots (auto-generated) +tests/ + common/mod.rs # count_tokens + shared helpers + fixtures/ # real command output (txt files) + integration_test.rs # #[ignore] end-to-end tests +``` + +## Pre-commit gate + +All three must pass before any commit: + +```bash +cargo fmt --all --check && cargo clippy --all-targets && cargo test +``` + +## Anti-patterns + +- **Don't** test with hardcoded synthetic strings — use real fixture files +- **Don't** skip cross-platform tests — use `cfg` guards +- **Don't** ignore savings drops below 60% — investigate and fix +- **Don't** commit without running `cargo insta review` diff --git a/docs/reference/internals/command-routing.md b/docs/reference/internals/command-routing.md new file mode 100644 index 000000000..3656880f8 --- /dev/null +++ b/docs/reference/internals/command-routing.md @@ -0,0 +1,130 @@ +--- +title: Command Routing +description: How RTK parses, routes, and dispatches commands through the 6-phase execution lifecycle +sidebar: + order: 1 +--- + +# Command Routing + +## 6-phase execution lifecycle + +Every RTK command goes through six phases: + +### Phase 1: Parse + +Clap parses the CLI arguments into a typed `Commands` enum: + +``` +$ rtk git log --oneline -5 -v + +Commands::Git { args: ["log", "--oneline", "-5"], verbose: 1 } +``` + +### Phase 2: Route + +`main.rs` matches the enum variant and dispatches to the module: + +```rust +match cli.command { + Commands::Git { args, .. } => git::run(&args, verbose)?, + Commands::Cargo { args, .. } => cargo::run(&args, verbose)?, + Commands::Ls { args } => ls_cmd::run(&args)?, + // ... +} +``` + +### Phase 3: Execute + +The module spawns the underlying process: + +```rust +std::process::Command::new("git") + .args(["log", "--oneline", "-5"]) + .output()? +// stdout: "abc123 Fix bug\ndef456 Add feature\n..." +// exit_code: 0 +``` + +### Phase 4: Filter + +The module applies its filtering strategy to the captured output: + +```rust +git::format_git_output(stdout, "log", verbose) +// Strategy: Stats Extraction +// Filtered: "5 commits, +142/-89" (96% reduction) +``` + +### Phase 5: Print + +```rust +println!("{}", colored_output); +// eprintln! for debug output when verbose > 0 +``` + +### Phase 6: Track + +```rust +tracking::track( + original_cmd: "git log --oneline -5", + rtk_cmd: "rtk git log --oneline -5", + input: &raw_output, // 500 chars + output: &filtered, // 20 chars +) +// SQLite INSERT: input_tokens=125, output_tokens=5, savings_pct=96.0 +``` + +## Exit code preservation + +RTK always propagates the exit code from the underlying tool: + +```rust +if !output.status.success() { + let stderr = String::from_utf8_lossy(&output.stderr); + eprintln!("{}", stderr); + std::process::exit(output.status.code().unwrap_or(1)); +} +``` + +RTK exit codes: `0` = success, `1` = RTK internal error, `N` = preserved from underlying tool. + +## Verbosity levels + +| Flag | Behavior | +|------|----------| +| (none) | Compact output only | +| `-v` | + Debug messages (`eprintln!`) | +| `-vv` | + Command being executed | +| `-vvv` | + Raw output before filtering | + +## Ultra-compact mode + +`-u` / `--ultra-compact`: maximum compression — ASCII icons instead of words, single-line summaries. + +```bash +rtk git push -u # ✓ main (vs "ok main" normally) +``` + +## Module organization + +``` +src/ + main.rs ← Commands enum + routing + cmds/ + git/ ← git, gh, gt, diff + rust/ ← cargo, runner (err/test) + js/ ← pnpm, vitest, tsc, next, playwright, prisma, lint + python/ ← ruff, pytest, mypy, pip + go/ ← go, golangci-lint + dotnet/ ← dotnet, binlog + cloud/ ← aws, docker/kubectl, curl, wget, psql + system/ ← ls, read, grep, find, json, env, log, deps, summary + ruby/ ← rake, rspec, rubocop + core/ ← utils, tracking, tee, config, toml_filter, filter + hooks/ ← init, rewrite, hook_cmd, verify, integrity + analytics/ ← gain, cc_economics, ccusage + discover/ ← rtk discover, registry +``` + +Total: 64 modules (42 command + 22 infrastructure). diff --git a/docs/reference/internals/filter-pipeline.md b/docs/reference/internals/filter-pipeline.md new file mode 100644 index 000000000..0e52432ba --- /dev/null +++ b/docs/reference/internals/filter-pipeline.md @@ -0,0 +1,81 @@ +--- +title: Filter Pipeline +description: The 12 filtering strategies RTK uses to compress command output +sidebar: + order: 2 +--- + +# Filter Pipeline + +## Filtering strategies + +RTK uses 12 distinct filtering strategies depending on the command type: + +| # | Strategy | Reduction | Used by | +|---|----------|-----------|---------| +| 1 | **Stats Extraction** | 90-99% | git status, git log, git diff, pnpm list | +| 2 | **Error Only** | 60-80% | runner (err mode), test failures | +| 3 | **Grouping by Pattern** | 80-90% | lint, tsc, grep (group by file/rule/code) | +| 4 | **Deduplication** | 70-85% | log_cmd (count occurrences) | +| 5 | **Structure Only** | 80-95% | json_cmd (keys + types, strip values) | +| 6 | **Code Filtering** | 0-90% | read, smart (language-aware, 3 levels) | +| 7 | **Failure Focus** | 94-99% | vitest, playwright, runner (test mode) | +| 8 | **Tree Compression** | 50-70% | ls (directory tree with counts) | +| 9 | **Progress Filtering** | 85-95% | wget, pnpm install (strip ANSI bars) | +| 10 | **JSON/Text Dual Mode** | 80%+ | ruff (JSON when available, text fallback) | +| 11 | **State Machine Parsing** | 90%+ | pytest (text state machine: name → result) | +| 12 | **NDJSON Streaming** | 90%+ | go test (line-by-line JSON event parsing) | + +## TOML filter pipeline (8 stages) + +TOML filters run output through this pipeline in order: + +``` +1. strip_ansi — remove terminal color codes +2. replace — regex substitutions +3. match_output — short-circuit: if pattern matches, emit message and stop +4. strip/keep_lines — keep or remove lines by regex +5. truncate_lines_at — truncate lines longer than N chars +6. tail_lines — keep only the last N lines +7. max_lines — keep only the first N lines +8. on_empty — if output is empty, emit this message +``` + +Stages not configured in the filter definition are skipped. First match wins in TOML filter lookup (project → user-global → built-in). + +## Code filtering levels (src/core/filter.rs) + +The `read` and `smart` commands use language-aware filtering with three levels: + +- **`none`**: Keep everything (0%) +- **`minimal`**: Strip comments and excessive blank lines (20-40%) +- **`aggressive`**: Keep signatures only, remove function bodies (60-90%) + +Supported languages: Rust, Python, JavaScript, TypeScript, Go, C, C++, Java, Ruby, Shell. + +## Savings by ecosystem + +``` +GIT (cmds/git/) 85-99% status, diff, log, gh, gt +JS/TS (cmds/js/) 70-99% lint, tsc, next, vitest, playwright, pnpm, prisma +PYTHON (cmds/python/) 70-90% ruff, pytest, mypy, pip +GO (cmds/go/) 75-90% go test, golangci-lint +RUBY (cmds/ruby/) 60-90% rake, rspec, rubocop +DOTNET (cmds/dotnet/) 70-85% dotnet build/test, binlog +CLOUD (cmds/cloud/) 60-80% aws, docker, kubectl, curl, wget, psql +SYSTEM (cmds/system/) 50-90% ls, read, grep, find, json, log, env +RUST (cmds/rust/) 60-99% cargo test/build/clippy, err +``` + +## Shared infrastructure (src/core/) + +| Module | Responsibility | +|--------|----------------| +| `utils.rs` | `strip_ansi`, `truncate`, `execute_command` | +| `filter.rs` | Language-aware code filtering engine | +| `toml_filter.rs` | TOML DSL filter engine (runtime) | +| `tracking.rs` | SQLite token metrics recording | +| `tee.rs` | Raw output recovery on failure | +| `config.rs` | `~/.config/rtk/config.toml` loading | +| `display_helpers.rs` | Terminal formatting helpers | +| `telemetry.rs` | Anonymous daily ping | diff --git a/docs/reference/internals/hook-engine.md b/docs/reference/internals/hook-engine.md new file mode 100644 index 000000000..0e958d902 --- /dev/null +++ b/docs/reference/internals/hook-engine.md @@ -0,0 +1,102 @@ +--- +title: Hook Engine +description: How RTK hooks intercept and rewrite agent commands before execution +sidebar: + order: 4 +--- + +# Hook Engine + +## Architecture + +RTK hooks are thin delegates: they parse agent-specific JSON, call `rtk rewrite` as a subprocess, and format the agent-specific response. All rewrite logic lives in the Rust binary (`src/discover/registry.rs`). + +``` +Agent runs "cargo test" + → Hook intercepts (PreToolUse / plugin event) + → Reads JSON input, extracts command string + → Calls `rtk rewrite "cargo test"` + → Registry matches pattern, returns "rtk cargo test" + → Hook formats agent-specific response + → Agent executes "rtk cargo test" + → Filtered output reaches LLM +``` + +## Integration tiers + +| Tier | Mechanism | Examples | +|------|-----------|---------| +| **Full hook** | Shell script or Rust binary, intercepts via agent hook API | Claude Code, Cursor, Copilot, Gemini | +| **Plugin** | TypeScript/JS in agent's plugin system | OpenCode | +| **Rules file** | Prompt-level instructions | Cline, Windsurf, Codex | + +## Rewrite registry + +`src/discover/registry.rs` holds 70+ rewrite patterns organized by category: + +| Category | Savings | +|----------|---------| +| Test runners (vitest, pytest, cargo test, go test, playwright) | 90-99% | +| Build tools (cargo build, npm, pnpm, dotnet, make) | 70-90% | +| VCS (git status/log/diff/show) | 70-80% | +| Language servers (tsc, mypy) | 80-83% | +| Linters (eslint, ruff, golangci-lint, biome) | 80-85% | +| Package managers (pip, cargo install, pnpm list) | 75-80% | +| File operations (ls, find, grep) | 60-75% | +| Infrastructure (docker, kubectl, aws) | 75-85% | + +## Compound command handling + +| Operator | Behavior | +|----------|----------| +| `&&`, `\|\|`, `;` | Both sides rewritten independently | +| `\|` (pipe) | Left side only (right side consumes output format) | +| `find`/`fd` in pipes | Never rewritten (incompatible with xargs/wc/grep) | + +Example: `cargo fmt --all && cargo test` → `rtk cargo fmt --all && rtk cargo test` + +## Exit code contract + +**Hooks must never block command execution.** All error paths must exit 0. A hook that exits non-zero prevents the user's command from running. + +Failure modes handled gracefully: +- RTK binary not found → warning to stderr, exit 0 +- Invalid JSON input → pass through unchanged +- RTK version too old (< 0.23.0) → warning + exit 0 +- `rtk rewrite` crashes → hook exits 0 + +## rtk init + +`src/hooks/init.rs` installs hook files for each supported agent: + +```bash +rtk init --global # Claude Code +rtk init --global --cursor # Cursor +rtk init --global --copilot # VS Code Copilot +rtk init --global --gemini # Gemini CLI +rtk init --global --opencode # OpenCode +rtk init --cline # Cline (project-local) +rtk init --windsurf # Windsurf (project-local) +rtk init --codex # Codex CLI +``` + +## rtk verify + +`src/hooks/verify_cmd.rs` runs inline TOML filter tests and checks hook integrity via SHA-256: + +```bash +rtk verify # all filters pass / fail with diff +rtk init --show # hook status per agent +``` + +## Override controls + +```bash +RTK_DISABLED=1 git status # skip rewrite for one command +``` + +```toml +# ~/.config/rtk/config.toml +[hooks] +exclude_commands = ["git rebase", "docker exec"] +``` diff --git a/docs/reference/internals/tracking-system.md b/docs/reference/internals/tracking-system.md new file mode 100644 index 000000000..076b3a660 --- /dev/null +++ b/docs/reference/internals/tracking-system.md @@ -0,0 +1,120 @@ +--- +title: Tracking System +description: How RTK records token savings in SQLite and exposes aggregation APIs +sidebar: + order: 3 +--- + +# Tracking System + +## Overview + +Every RTK command execution is recorded in a local SQLite database. This data powers `rtk gain` and `rtk cc-economics`. + +**Storage locations:** +- Linux: `~/.local/share/rtk/tracking.db` +- macOS: `~/Library/Application Support/rtk/tracking.db` +- Windows: `%APPDATA%\rtk\tracking.db` + +**Retention:** Records older than 90 days are automatically deleted on each write. + +## 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 INSERT + ↓ +Aggregation APIs (get_summary, get_all_days, etc.) + ↓ +rtk gain output +``` + +## Core API + +```rust +pub struct Tracker { + conn: Connection, // SQLite connection +} + +impl Tracker { + pub fn new() -> Result; + + pub fn record( + &self, + original_cmd: &str, // e.g., "ls -la" + rtk_cmd: &str, // e.g., "rtk ls" + input_tokens: usize, // estimate_tokens(raw_output) + output_tokens: usize, // estimate_tokens(filtered_output) + exec_time_ms: u64, + ) -> Result<()>; + + pub fn get_summary(&self) -> Result; + pub fn get_all_days(&self) -> Result>; + pub fn get_weekly(&self) -> Result>; + pub fn get_monthly(&self) -> Result>; +} +``` + +## Token estimation + +``` +estimate_tokens(text) = text.len() / 4 +``` + +~4 characters per token average. Accuracy: ±10% vs actual LLM tokenization. + +## Database schema + +```sql +CREATE TABLE commands ( + id INTEGER PRIMARY KEY, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + original_cmd TEXT NOT NULL, + rtk_cmd TEXT NOT NULL, + input_tokens INTEGER NOT NULL, + output_tokens INTEGER NOT NULL, + saved_tokens INTEGER GENERATED ALWAYS AS (input_tokens - output_tokens), + savings_pct REAL GENERATED ALWAYS AS ( + CASE WHEN input_tokens > 0 + THEN (1.0 - CAST(output_tokens AS REAL) / input_tokens) * 100 + ELSE 0 END + ), + exec_time_ms INTEGER +); +``` + +## Reporting query + +```sql +SELECT + COUNT(*) as total_commands, + SUM(saved_tokens) as total_saved, + AVG(savings_pct) as avg_savings, + SUM(exec_time_ms) as total_time_ms +FROM commands +WHERE timestamp > datetime('now', '-90 days') +``` + +## Configuration + +```toml +[tracking] +enabled = true +history_days = 90 +database_path = "/custom/path/tracking.db" # optional override +``` + +Environment variable override: `RTK_DB_PATH=/custom/path.db` + +## Thread safety + +Single-threaded execution with `Mutex>` for future-proofing. No multi-threading currently used. diff --git a/docs/reference/toml-dsl/specification.md b/docs/reference/toml-dsl/specification.md new file mode 100644 index 000000000..9b4c065b4 --- /dev/null +++ b/docs/reference/toml-dsl/specification.md @@ -0,0 +1,124 @@ +--- +title: TOML DSL Specification +description: Complete specification for RTK's TOML filter format +sidebar: + order: 1 +--- + +# TOML DSL Specification + +TOML filters are the declarative way to add RTK support for commands with line-by-line text output. They are embedded in the binary at build time and can also be used as project-local or user-global overrides. + +## File format + +```toml +[filters.my-tool] +description = "Short description of what this filter does" +match_command = "^my-tool\\b" # regex matched against full command string +strip_ansi = true # optional: strip ANSI codes first + +strip_lines_matching = [ # optional: drop lines matching any regex + "^\\s*$", + "^noise pattern", +] + +keep_lines_matching = [ # optional: keep only matching lines + "^error", + "^warning", +] + +max_lines = 40 # optional: keep only first N lines after filtering +tail_lines = 20 # optional: keep only last N lines +truncate_lines_at = 120 # optional: truncate lines longer than N chars +on_empty = "my-tool: nothing to do" # optional: message when output is empty + +[[tests.my-tool]] +name = "descriptive test name" +input = "raw command output here" +expected = "expected filtered output" +``` + +## Field reference + +| Field | Type | Required | Description | +|-------|------|----------|-------------| +| `description` | string | yes | Human-readable description | +| `match_command` | regex | yes | Matched against the full command string | +| `strip_ansi` | bool | no | Strip ANSI escape codes before processing | +| `strip_lines_matching` | regex[] | no | Drop lines matching any of these patterns | +| `keep_lines_matching` | regex[] | no | Keep only lines matching at least one pattern | +| `replace` | array | no | Regex substitutions: `{ pattern, replacement }` | +| `match_output` | array | no | Short-circuit rules: `{ pattern, message }` | +| `truncate_lines_at` | int | no | Truncate lines longer than N characters | +| `max_lines` | int | no | Keep only the first N lines (after other stages) | +| `tail_lines` | int | no | Keep only the last N lines | +| `on_empty` | string | no | Message emitted when pipeline produces empty output | + +## Pipeline execution order + +When a filter matches, output passes through these stages in order: + +1. `strip_ansi` — remove terminal color codes +2. `replace` — regex substitutions +3. `match_output` — short-circuit: if pattern matches, emit message and stop +4. `strip_lines_matching` / `keep_lines_matching` +5. `truncate_lines_at` +6. `tail_lines` +7. `max_lines` +8. `on_empty` — if output is now empty, emit this message + +Stages not defined in the filter are skipped. + +## Inline tests + +Every filter must have at least one `[[tests.]]` entry. Tests run during `cargo test` and `rtk verify`. + +```toml +[[tests.my-tool]] +name = "strips progress lines" +input = "Loading...\nDone: 3 issues" +expected = "Done: 3 issues" + +[[tests.my-tool]] +name = "empty output fallback" +input = "Loading...\nProgress: 100%" +expected = "my-tool: nothing to do" +``` + +## Filter lookup priority + +``` +1. .rtk/filters.toml (project-local — highest priority) +2. ~/.config/rtk/filters.toml (user-global) +3. BUILTIN_TOML (embedded in binary — lowest priority) +``` + +First match wins. A project filter with the same name as a built-in triggers a warning: + +``` +[rtk] warning: filter 'make' is shadowing a built-in filter +``` + +## Built-in filter compilation + +Built-in filters live in `src/filters/*.toml`. At build time, `build.rs`: + +1. Lists all `*.toml` files alphabetically +2. Concatenates them into a single TOML blob +3. Validates syntax and checks for duplicate names (build fails on error) +4. Embeds the result in the binary via `include_str!(concat!(env!(OUT_DIR), "/builtin_filters.toml"))` + +New built-in filters require a new RTK release. Project and user filters take effect immediately without rebuilding. + +## Naming convention + +Use the command name as the filter key: `terraform-plan`, `docker-inspect`, `mix-compile`. For subcommands, prefer `cmd-subcommand` over grouping: `docker-ps.toml`, not `docker.toml` with multiple filters. + +## Regex syntax + +Patterns use Rust's `regex` crate (RE2-compatible). Backslashes must be doubled in TOML strings: + +```toml +match_command = "^my-tool\\b" # matches "my-tool" as a word +strip_lines_matching = ["^\\s*$"] # matches blank lines +``` From a94e9493b34b4053585d0bf18d8492f545e8077b Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Sun, 5 Apr 2026 21:26:07 +0200 Subject: [PATCH 09/13] docs: consolidate to user-facing guide, remove duplicates and legacy flat docs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove docs/reference/ (10 files): duplicates CONTRIBUTING.md, SECURITY.md, ARCHITECTURE.md, src/*/README.md (distributed pattern from PR #869) - Remove docs/architecture/ (5 files): duplicates ARCHITECTURE.md ADRs and diagrams - Remove docs/guide/commands/ (12 files): showed rtk syntax users never type (hooks rewrite transparently); content was derived from FEATURES.md - Remove docs/guide/filters/ (2 files): contributor content, belongs with src/filters/ - Remove docs/guide/analytics/discover.md, economics.md: implementation details - Remove legacy flat docs/ (6 files): FEATURES.md (FR), TECHNICAL.md, TROUBLESHOOTING.md, AUDIT_GUIDE.md, tracking.md, filter-workflow.md — all superseded by guide/ rewrites or codebase distributed docs - Add docs/guide/what-rtk-covers.md: replaces 12 command pages with one user-facing overview of 60+ commands by ecosystem, no rtk syntax - Enrich docs/guide/analytics/gain.md: absorb advanced workflows from AUDIT_GUIDE.md (CI integration, pandas analysis, cron snapshots) - Adapt docs/guide/getting-started/quick-start.md: remove rtk examples, explain hook transparency instead - Update docs/README.md: single-tab contract, pointer to codebase for technical docs - Update docs/guide/index.md: clean navigation matching final structure - Add Mermaid diagrams to src/filters/README.md: build pipeline + filter lookup (preserves content from filter-workflow.md in its natural home) Result: 9 files in docs/guide/ (was 39), zero legacy flat docs, zero duplicates. Codebase distributed docs (ARCHITECTURE.md, CONTRIBUTING.md, src/*/README.md) remain the source of truth for technical and contributor content. Signed-off-by: Florian BRUNIAUX --- docs/AUDIT_GUIDE.md | 432 ----- docs/FEATURES.md | 1410 ----------------- docs/TECHNICAL.md | 432 ----- docs/TROUBLESHOOTING.md | 337 ---- .../decisions/proxy-architecture.md | 68 - docs/architecture/decisions/why-no-async.md | 52 - docs/architecture/diagrams/command-flow.md | 65 - docs/architecture/diagrams/filter-pipeline.md | 71 - docs/architecture/index.md | 15 - docs/filter-workflow.md | 102 -- docs/guide/analytics/discover.md | 49 - docs/guide/analytics/economics.md | 54 - docs/guide/commands/cargo.md | 142 -- docs/guide/commands/containers.md | 90 -- docs/guide/commands/data.md | 103 -- docs/guide/commands/dotnet.md | 71 - docs/guide/commands/files.md | 185 --- docs/guide/commands/git.md | 221 --- docs/guide/commands/github-cli.md | 94 -- docs/guide/commands/go.md | 54 - docs/guide/commands/javascript.md | 129 -- docs/guide/commands/python.md | 55 - docs/guide/commands/ruby.md | 45 - docs/guide/commands/utilities.md | 66 - docs/guide/filters/creating-filters.md | 105 -- docs/guide/filters/using-filters.md | 121 -- docs/guide/what-rtk-covers.md | 133 ++ .../contributing/coding-standards.md | 128 -- docs/reference/contributing/guide.md | 102 -- docs/reference/contributing/security.md | 83 - docs/reference/contributing/testing.md | 142 -- docs/reference/index.md | 16 - docs/reference/internals/command-routing.md | 130 -- docs/reference/internals/filter-pipeline.md | 81 - docs/reference/internals/hook-engine.md | 102 -- docs/reference/internals/tracking-system.md | 120 -- docs/reference/toml-dsl/specification.md | 124 -- docs/tracking.md | 583 ------- 38 files changed, 133 insertions(+), 6179 deletions(-) delete mode 100644 docs/AUDIT_GUIDE.md delete mode 100644 docs/FEATURES.md delete mode 100644 docs/TECHNICAL.md delete mode 100644 docs/TROUBLESHOOTING.md delete mode 100644 docs/architecture/decisions/proxy-architecture.md delete mode 100644 docs/architecture/decisions/why-no-async.md delete mode 100644 docs/architecture/diagrams/command-flow.md delete mode 100644 docs/architecture/diagrams/filter-pipeline.md delete mode 100644 docs/architecture/index.md delete mode 100644 docs/filter-workflow.md delete mode 100644 docs/guide/analytics/discover.md delete mode 100644 docs/guide/analytics/economics.md delete mode 100644 docs/guide/commands/cargo.md delete mode 100644 docs/guide/commands/containers.md delete mode 100644 docs/guide/commands/data.md delete mode 100644 docs/guide/commands/dotnet.md delete mode 100644 docs/guide/commands/files.md delete mode 100644 docs/guide/commands/git.md delete mode 100644 docs/guide/commands/github-cli.md delete mode 100644 docs/guide/commands/go.md delete mode 100644 docs/guide/commands/javascript.md delete mode 100644 docs/guide/commands/python.md delete mode 100644 docs/guide/commands/ruby.md delete mode 100644 docs/guide/commands/utilities.md delete mode 100644 docs/guide/filters/creating-filters.md delete mode 100644 docs/guide/filters/using-filters.md create mode 100644 docs/guide/what-rtk-covers.md delete mode 100644 docs/reference/contributing/coding-standards.md delete mode 100644 docs/reference/contributing/guide.md delete mode 100644 docs/reference/contributing/security.md delete mode 100644 docs/reference/contributing/testing.md delete mode 100644 docs/reference/index.md delete mode 100644 docs/reference/internals/command-routing.md delete mode 100644 docs/reference/internals/filter-pipeline.md delete mode 100644 docs/reference/internals/hook-engine.md delete mode 100644 docs/reference/internals/tracking-system.md delete mode 100644 docs/reference/toml-dsl/specification.md delete mode 100644 docs/tracking.md diff --git a/docs/AUDIT_GUIDE.md b/docs/AUDIT_GUIDE.md deleted file mode 100644 index 8bcebdffe..000000000 --- a/docs/AUDIT_GUIDE.md +++ /dev/null @@ -1,432 +0,0 @@ -# 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 -``` - -## 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 ` | Quota tier: pro, 5x, 20x (default: 20x) | - -### 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' - - - -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 vitest run` | 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](../ARCHITECTURE.md) - Technical architecture diff --git a/docs/FEATURES.md b/docs/FEATURES.md deleted file mode 100644 index 061a604a9..000000000 --- a/docs/FEATURES.md +++ /dev/null @@ -1,1410 +0,0 @@ -# RTK - Documentation fonctionnelle complete - -> **rtk (Rust Token Killer)** -- Proxy CLI haute performance qui reduit la consommation de tokens LLM de 60 a 90%. - -Binaire Rust unique, zero dependances externes, overhead < 10ms par commande. - ---- - -## Table des matieres - -1. [Vue d'ensemble](#vue-densemble) -2. [Drapeaux globaux](#drapeaux-globaux) -3. [Commandes Fichiers](#commandes-fichiers) -4. [Commandes Git](#commandes-git) -5. [Commandes GitHub CLI](#commandes-github-cli) -6. [Commandes Test](#commandes-test) -7. [Commandes Build et Lint](#commandes-build-et-lint) -8. [Commandes Formatage](#commandes-formatage) -9. [Gestionnaires de paquets](#gestionnaires-de-paquets) -10. [Conteneurs et orchestration](#conteneurs-et-orchestration) -11. [Donnees et reseau](#donnees-et-reseau) -12. [Cloud et bases de donnees](#cloud-et-bases-de-donnees) -13. [Stacked PRs (Graphite)](#stacked-prs-graphite) -14. [Analytique et suivi](#analytique-et-suivi) -15. [Systeme de hooks](#systeme-de-hooks) -16. [Configuration](#configuration) -17. [Systeme Tee (recuperation de sortie)](#systeme-tee) -18. [Telemetrie](#telemetrie) - ---- - -## Vue d'ensemble - -rtk agit comme un proxy entre un LLM (Claude Code, Gemini CLI, etc.) et les commandes systeme. Quatre strategies de filtrage sont appliquees selon le type de commande : - -| Strategie | Description | Exemple | -|-----------|-------------|---------| -| **Filtrage intelligent** | Supprime le bruit (commentaires, espaces, boilerplate) | `ls -la` -> arbre compact | -| **Regroupement** | Agregation par repertoire, par type d'erreur, par regle | Tests groupes par fichier | -| **Troncature** | Conserve le contexte pertinent, supprime la redondance | Diff condense | -| **Deduplication** | Fusionne les lignes de log repetees avec compteurs | `error x42` | - -### Mecanisme de fallback - -Si rtk ne reconnait pas une sous-commande, il execute la commande brute (passthrough) et enregistre l'evenement dans la base de suivi. Cela garantit que rtk est **toujours sur** a utiliser -- aucune commande ne sera bloquee. - ---- - -## Drapeaux globaux - -Ces drapeaux s'appliquent a **toutes** les sous-commandes : - -| Drapeau | Court | Description | -|---------|-------|-------------| -| `--verbose` | `-v` | Augmenter la verbosite (-v, -vv, -vvv). Montre les details de filtrage. | -| `--ultra-compact` | `-u` | Mode ultra-compact : icones ASCII, format inline. Economies supplementaires. | -| `--skip-env` | -- | Definit `SKIP_ENV_VALIDATION=1` pour les processus enfants (Next.js, tsc, lint, prisma). | - -**Exemples :** - -```bash -rtk -v git status # Status compact + details de filtrage sur stderr -rtk -vvv cargo test # Verbosite maximale (debug) -rtk -u git log # Log ultra-compact, icones ASCII -rtk --skip-env next build # Desactive la validation d'env de Next.js -``` - ---- - -## Commandes Fichiers - -### `rtk ls` -- Listage de repertoire - -**Objectif :** Remplace `ls` et `tree` avec une sortie optimisee en tokens. - -**Syntaxe :** -```bash -rtk ls [args...] -``` - -Tous les drapeaux natifs de `ls` sont supportes (`-l`, `-a`, `-h`, `-R`, etc.). - -**Economies :** ~80% de reduction de tokens - -**Avant / Apres :** -``` -# ls -la (45 lignes, ~800 tokens) # rtk ls (12 lignes, ~150 tokens) -drwxr-xr-x 15 user staff 480 ... my-project/ --rw-r--r-- 1 user staff 1234 ... +-- src/ (8 files) --rw-r--r-- 1 user staff 567 ... | +-- main.rs -...40 lignes de plus... +-- Cargo.toml - +-- README.md -``` - ---- - -### `rtk tree` -- Arbre de repertoire - -**Objectif :** Proxy vers `tree` natif avec sortie filtree. - -**Syntaxe :** -```bash -rtk tree [args...] -``` - -Supporte tous les drapeaux natifs de `tree` (`-L`, `-d`, `-a`, etc.). - -**Economies :** ~80% - ---- - -### `rtk read` -- Lecture de fichier - -**Objectif :** Remplace `cat`, `head`, `tail` avec un filtrage intelligent du contenu. - -**Syntaxe :** -```bash -rtk read [options] -rtk read - [options] # Lecture depuis stdin -``` - -**Options :** - -| Option | Court | Defaut | Description | -|--------|-------|--------|-------------| -| `--level` | `-l` | `minimal` | Niveau de filtrage : `none`, `minimal`, `aggressive` | -| `--max-lines` | `-m` | illimite | Nombre maximum de lignes | -| `--line-numbers` | `-n` | non | Afficher les numeros de ligne | - -**Niveaux de filtrage :** - -| Niveau | Description | Economies | -|--------|-------------|-----------| -| `none` | Aucun filtrage, sortie brute | 0% | -| `minimal` | Supprime commentaires et lignes vides excessives | ~30% | -| `aggressive` | Signatures uniquement (supprime les corps de fonctions) | ~74% | - -**Avant / Apres (mode aggressive) :** -``` -# cat main.rs (~200 lignes) # rtk read main.rs -l aggressive (~50 lignes) -fn main() -> Result<()> { fn main() -> Result<()> { ... } - let config = Config::load()?; fn process_data(input: &str) -> Vec { ... } - let data = process_data(&input); struct Config { ... } - for item in data { impl Config { fn load() -> Result { ... } } - println!("{}", item); - } - Ok(()) -} -... -``` - -**Langages supportes pour le filtrage :** Rust, Python, JavaScript, TypeScript, Go, C, C++, Java, Ruby, Shell. - ---- - -### `rtk smart` -- Resume heuristique - -**Objectif :** Genere un resume technique de 2 lignes pour un fichier source. - -**Syntaxe :** -```bash -rtk smart [--model heuristic] [--force-download] -``` - -**Economies :** ~95% - -**Exemple :** -``` -$ rtk smart src/tracking.rs -SQLite-based token tracking system for command executions. -Records input/output tokens, savings %, execution times with 90-day retention. -``` - ---- - -### `rtk find` -- Recherche de fichiers - -**Objectif :** Remplace `find` et `fd` avec une sortie compacte groupee par repertoire. - -**Syntaxe :** -```bash -rtk find [args...] -``` - -Supporte a la fois la syntaxe RTK et la syntaxe native `find` (`-name`, `-type`, etc.). - -**Economies :** ~80% - -**Avant / Apres :** -``` -# find . -name "*.rs" (30 lignes) # rtk find "*.rs" . (8 lignes) -./src/main.rs src/ (12 .rs) -./src/git.rs main.rs, git.rs, config.rs -./src/config.rs tracking.rs, filter.rs, utils.rs -./src/tracking.rs ...6 more -./src/filter.rs tests/ (3 .rs) -./src/utils.rs test_git.rs, test_ls.rs, test_filter.rs -...24 lignes de plus... -``` - ---- - -### `rtk grep` -- Recherche dans le contenu - -**Objectif :** Remplace `grep` et `rg` avec une sortie groupee par fichier, tronquee. - -**Syntaxe :** -```bash -rtk grep [chemin] [options] -``` - -**Options :** - -| Option | Court | Defaut | Description | -|--------|-------|--------|-------------| -| `--max-len` | `-l` | 80 | Longueur maximale de ligne | -| `--max` | `-m` | 50 | Nombre maximum de resultats | -| `--context-only` | `-c` | non | Afficher uniquement le contexte du match | -| `--file-type` | `-t` | tous | Filtrer par type (ts, py, rust, etc.) | -| `--line-numbers` | `-n` | oui | Numeros de ligne (toujours actif) | - -Les arguments supplementaires sont transmis a `rg` (ripgrep). - -**Economies :** ~80% - -**Avant / Apres :** -``` -# rg "fn run" (20 lignes) # rtk grep "fn run" (10 lignes) -src/git.rs:45:pub fn run(...) src/git.rs -src/git.rs:120:fn run_status(...) 45: pub fn run(...) -src/ls.rs:12:pub fn run(...) 120: fn run_status(...) -src/ls.rs:25:fn run_tree(...) src/ls.rs -... 12: pub fn run(...) - 25: fn run_tree(...) -``` - ---- - -### `rtk diff` -- Diff condense - -**Objectif :** Diff ultra-condense entre deux fichiers (uniquement les lignes modifiees). - -**Syntaxe :** -```bash -rtk diff -rtk diff # Stdin comme second fichier -``` - -**Economies :** ~60% - ---- - -### `rtk wc` -- Comptage compact - -**Objectif :** Remplace `wc` avec une sortie compacte (supprime les chemins et le padding). - -**Syntaxe :** -```bash -rtk wc [args...] -``` - -Supporte tous les drapeaux natifs de `wc` (`-l`, `-w`, `-c`, etc.). - ---- - -## Commandes Git - -### Vue d'ensemble - -Toutes les sous-commandes git sont supportees. Les commandes non reconnues sont transmises directement a git (passthrough). - -**Options globales git :** - -| Option | Description | -|--------|-------------| -| `-C ` | Changer de repertoire avant execution | -| `-c ` | Surcharger une config git | -| `--git-dir ` | Chemin vers le repertoire .git | -| `--work-tree ` | Chemin vers le working tree | -| `--no-pager` | Desactiver le pager | -| `--no-optional-locks` | Ignorer les locks optionnels | -| `--bare` | Traiter comme repo bare | -| `--literal-pathspecs` | Pathspecs literals | - ---- - -### `rtk git status` -- Status compact - -**Economies :** ~80% - -```bash -rtk git status [args...] # Supporte tous les drapeaux git status -``` - -**Avant / Apres :** -``` -# git status (~20 lignes, ~400 tokens) # rtk git status (~5 lignes, ~80 tokens) -On branch main main | 3M 1? 1A -Your branch is up to date with M src/main.rs - 'origin/main'. M src/git.rs - M tests/test_git.rs -Changes not staged for commit: ? new_file.txt - (use "git add ..." to update) A staged_file.rs - modified: src/main.rs - modified: src/git.rs - ... -``` - ---- - -### `rtk git log` -- Historique compact - -**Economies :** ~80% - -```bash -rtk git log [args...] # Supporte --oneline, --graph, --all, -n, etc. -``` - -**Avant / Apres :** -``` -# git log (50+ lignes) # rtk git log -n 5 (5 lignes) -commit abc123def... (HEAD -> main) abc123 Fix token counting bug -Author: User def456 Add vitest support -Date: Mon Jan 15 10:30:00 2024 789abc Refactor filter engine - 012def Update README - Fix token counting bug 345ghi Initial commit -... -``` - ---- - -### `rtk git diff` -- Diff compact - -**Economies :** ~75% - -```bash -rtk git diff [args...] # Supporte --stat, --cached, --staged, etc. -``` - -**Avant / Apres :** -``` -# git diff (~100 lignes) # rtk git diff (~25 lignes) -diff --git a/src/main.rs b/src/main.rs src/main.rs (+5/-2) -index abc123..def456 100644 + let config = Config::load()?; ---- a/src/main.rs + config.validate()?; -+++ b/src/main.rs - // old code -@@ -10,6 +10,8 @@ - let x = 42; - fn main() { src/git.rs (+1/-1) -+ let config = Config::load()?; ~ format!("ok {}", branch) -...30 lignes de headers et contexte... -``` - ---- - -### `rtk git show` -- Show compact - -**Economies :** ~80% - -```bash -rtk git show [args...] -``` - -Affiche le resume du commit + stat + diff compact. - ---- - -### `rtk git add` -- Add ultra-compact - -**Economies :** ~92% - -```bash -rtk git add [args...] # Supporte -A, -p, --all, etc. -``` - -**Sortie :** `ok` (un seul mot) - ---- - -### `rtk git commit` -- Commit ultra-compact - -**Economies :** ~92% - -```bash -rtk git commit -m "message" [args...] # Supporte -a, --amend, --allow-empty, etc. -``` - -**Sortie :** `ok abc1234` (confirmation + hash court) - ---- - -### `rtk git push` -- Push ultra-compact - -**Economies :** ~92% - -```bash -rtk git push [args...] # Supporte -u, remote, branch, etc. -``` - -**Avant / Apres :** -``` -# git push (15 lignes, ~200 tokens) # rtk git push (1 ligne, ~10 tokens) -Enumerating objects: 5, done. ok main -Counting objects: 100% (5/5), done. -Delta compression using up to 8 threads -... -``` - ---- - -### `rtk git pull` -- Pull ultra-compact - -**Economies :** ~92% - -```bash -rtk git pull [args...] -``` - -**Sortie :** `ok 3 files +10 -2` - ---- - -### `rtk git branch` -- Branches compact - -```bash -rtk git branch [args...] # Supporte -d, -D, -m, etc. -``` - -Affiche branche courante, branches locales, branches distantes de facon compacte. - ---- - -### `rtk git fetch` -- Fetch compact - -```bash -rtk git fetch [args...] -``` - -**Sortie :** `ok fetched (N new refs)` - ---- - -### `rtk git stash` -- Stash compact - -```bash -rtk git stash [list|show|pop|apply|drop|push] [args...] -``` - ---- - -### `rtk git worktree` -- Worktree compact - -```bash -rtk git worktree [add|remove|prune|list] [args...] -``` - ---- - -### Passthrough git - -Toute sous-commande git non listee ci-dessus est executee directement : - -```bash -rtk git rebase main # Execute git rebase main -rtk git cherry-pick abc # Execute git cherry-pick abc -rtk git tag v1.0.0 # Execute git tag v1.0.0 -``` - ---- - -## Commandes GitHub CLI - -### `rtk gh` -- GitHub CLI compact - -**Objectif :** Remplace `gh` avec une sortie optimisee. - -**Syntaxe :** -```bash -rtk gh [args...] -``` - -**Sous-commandes supportees :** - -| Commande | Description | Economies | -|----------|-------------|-----------| -| `rtk gh pr list` | Liste des PRs compacte | ~80% | -| `rtk gh pr view ` | Details d'une PR + checks | ~87% | -| `rtk gh pr checks` | Status des checks CI | ~79% | -| `rtk gh issue list` | Liste des issues compacte | ~80% | -| `rtk gh run list` | Status des workflow runs | ~82% | -| `rtk gh api ` | Reponse API compacte | ~26% | - -**Avant / Apres :** -``` -# gh pr list (~30 lignes) # rtk gh pr list (~10 lignes) -Showing 10 of 15 pull requests in org/repo #42 feat: add vitest (open, 2d) - #41 fix: git diff crash (open, 3d) -#42 feat: add vitest support #40 chore: update deps (merged, 5d) - user opened about 2 days ago #39 docs: add guide (merged, 1w) - ... labels: enhancement -... -``` - ---- - -## Commandes Test - -### `rtk test` -- Wrapper de tests generique - -**Objectif :** Execute n'importe quelle commande de test et affiche uniquement les echecs. - -**Syntaxe :** -```bash -rtk test -``` - -**Economies :** ~90% - -**Exemple :** -```bash -rtk test cargo test -rtk test npm test -rtk test bun test -rtk test pytest -``` - -**Avant / Apres :** -``` -# cargo test (200+ lignes en cas d'echec) # rtk test cargo test (~20 lignes) -running 15 tests FAILED: 2/15 tests -test utils::test_parse ... ok test_edge_case: assertion failed -test utils::test_format ... ok test_overflow: panic at utils.rs:18 -test utils::test_edge_case ... FAILED -...150 lignes de backtrace... -``` - ---- - -### `rtk err` -- Erreurs/avertissements uniquement - -**Objectif :** Execute une commande et ne montre que les erreurs et avertissements. - -**Syntaxe :** -```bash -rtk err -``` - -**Economies :** ~80% - -**Exemple :** -```bash -rtk err npm run build -rtk err cargo build -``` - ---- - -### `rtk cargo test` -- Tests Rust - -**Economies :** ~90% - -```bash -rtk cargo test [args...] -``` - -N'affiche que les echecs. Supporte tous les arguments de `cargo test`. - ---- - -### `rtk cargo nextest` -- Tests Rust (nextest) - -```bash -rtk cargo nextest [run|list|--lib] [args...] -``` - -Filtre la sortie de `cargo nextest` pour n'afficher que les echecs. - ---- - -### `rtk vitest run` -- Tests Vitest - -**Economies :** ~99.5% - -```bash -rtk vitest run [args...] -``` - ---- - -### `rtk playwright test` -- Tests E2E Playwright - -**Economies :** ~94% - -```bash -rtk playwright [args...] -``` - ---- - -### `rtk pytest` -- Tests Python - -**Economies :** ~90% - -```bash -rtk pytest [args...] -``` - ---- - -### `rtk go test` -- Tests Go - -**Economies :** ~90% - -```bash -rtk go test [args...] -``` - -Utilise le streaming JSON NDJSON de Go pour un filtrage precis. - ---- - -## Commandes Build et Lint - -### `rtk cargo build` -- Build Rust - -**Economies :** ~80% - -```bash -rtk cargo build [args...] -``` - -Supprime les lignes "Compiling...", ne conserve que les erreurs et le resultat final. - ---- - -### `rtk cargo check` -- Check Rust - -**Economies :** ~80% - -```bash -rtk cargo check [args...] -``` - -Supprime les lignes "Checking...", ne conserve que les erreurs. - ---- - -### `rtk cargo clippy` -- Clippy Rust - -**Economies :** ~80% - -```bash -rtk cargo clippy [args...] -``` - -Regroupe les avertissements par regle de lint. - ---- - -### `rtk cargo install` -- Install Rust - -```bash -rtk cargo install [args...] -``` - -Supprime la compilation des dependances, ne conserve que le resultat d'installation et les erreurs. - ---- - -### `rtk tsc` -- TypeScript Compiler - -**Economies :** ~83% - -```bash -rtk tsc [args...] -``` - -Regroupe les erreurs TypeScript par fichier et par code d'erreur. - -**Avant / Apres :** -``` -# tsc --noEmit (50 lignes) # rtk tsc (15 lignes) -src/api.ts(12,5): error TS2345: ... src/api.ts (3 errors) -src/api.ts(15,10): error TS2345: ... TS2345: Argument type mismatch (x2) -src/api.ts(20,3): error TS7006: ... TS7006: Parameter implicitly has 'any' -src/utils.ts(5,1): error TS2304: ... src/utils.ts (1 error) -... TS2304: Cannot find name 'foo' -``` - ---- - -### `rtk lint` -- ESLint / Biome - -**Economies :** ~84% - -```bash -rtk lint [args...] -rtk lint biome [args...] -``` - -Regroupe les violations par regle et par fichier. Auto-detecte le linter. - ---- - -### `rtk prettier` -- Verification du formatage - -**Economies :** ~70% - -```bash -rtk prettier [args...] # ex: rtk prettier --check . -``` - -Affiche uniquement les fichiers necessitant un formatage. - ---- - -### `rtk format` -- Formateur universel - -```bash -rtk format [args...] -``` - -Auto-detecte le formateur du projet (prettier, black, ruff format) et applique un filtre compact. - ---- - -### `rtk next build` -- Build Next.js - -**Economies :** ~87% - -```bash -rtk next [args...] -``` - -Sortie compacte avec metriques de routes. - ---- - -### `rtk ruff` -- Linter/formateur Python - -**Economies :** ~80% - -```bash -rtk ruff check [args...] -rtk ruff format --check [args...] -``` - -Sortie JSON compressee. - ---- - -### `rtk mypy` -- Type checker Python - -```bash -rtk mypy [args...] -``` - -Regroupe les erreurs de type par fichier. - ---- - -### `rtk golangci-lint` -- Linter Go - -**Economies :** ~85% - -```bash -rtk golangci-lint run [args...] -``` - -Sortie JSON compressee. - ---- - -## Commandes Formatage - -### `rtk prettier` -- Prettier - -```bash -rtk prettier --check . -rtk prettier --write src/ -``` - ---- - -### `rtk format` -- Detecteur universel - -```bash -rtk format [args...] -``` - -Detecte automatiquement : prettier, black, ruff format, rustfmt. Applique un filtre compact unifie. - ---- - -## Gestionnaires de paquets - -### `rtk pnpm` -- pnpm - -| Commande | Description | Economies | -|----------|-------------|-----------| -| `rtk pnpm list [-d N]` | Arbre de dependances compact | ~70% | -| `rtk pnpm outdated` | Paquets obsoletes : `pkg: old -> new` | ~80% | -| `rtk pnpm install [pkgs...]` | Filtre les barres de progression | ~60% | -| `rtk pnpm build` | Delegue au filtre Next.js | ~87% | -| `rtk pnpm typecheck` | Delegue au filtre tsc | ~83% | - -Les sous-commandes non reconnues sont transmises directement a pnpm (passthrough). - ---- - -### `rtk npm` -- npm - -```bash -rtk npm [args...] # ex: rtk npm run build -``` - -Filtre le boilerplate npm (barres de progression, en-tetes, etc.). - ---- - -### `rtk npx` -- npx avec routage intelligent - -```bash -rtk npx [args...] -``` - -Route intelligemment vers les filtres specialises : -- `rtk npx tsc` -> filtre tsc -- `rtk npx eslint` -> filtre lint -- `rtk npx prisma` -> filtre prisma -- Autres -> passthrough filtre - ---- - -### `rtk pip` -- pip / uv - -```bash -rtk pip list # Liste des paquets (auto-detecte uv) -rtk pip outdated # Paquets obsoletes -rtk pip install # Installation -``` - -Auto-detecte `uv` si disponible et l'utilise a la place de `pip`. - ---- - -### `rtk deps` -- Resume des dependances - -**Objectif :** Resume compact des dependances du projet. - -```bash -rtk deps [chemin] # Defaut: repertoire courant -``` - -Auto-detecte : `Cargo.toml`, `package.json`, `pyproject.toml`, `go.mod`, `Gemfile`, etc. - -**Economies :** ~70% - ---- - -### `rtk prisma` -- ORM Prisma - -| Commande | Description | -|----------|-------------| -| `rtk prisma generate` | Generation du client (supprime l'ASCII art) | -| `rtk prisma migrate dev [--name N]` | Creer et appliquer une migration | -| `rtk prisma migrate status` | Status des migrations | -| `rtk prisma migrate deploy` | Deployer en production | -| `rtk prisma db-push` | Push du schema | - ---- - -## Conteneurs et orchestration - -### `rtk docker` -- Docker - -| Commande | Description | Economies | -|----------|-------------|-----------| -| `rtk docker ps` | Liste compacte des conteneurs | ~80% | -| `rtk docker images` | Liste compacte des images | ~80% | -| `rtk docker logs ` | Logs dedupliques | ~70% | -| `rtk docker compose ps` | Services Compose compacts | ~80% | -| `rtk docker compose logs [service]` | Logs Compose dedupliques | ~70% | -| `rtk docker compose build [service]` | Resume du build | ~60% | - -Les sous-commandes non reconnues sont transmises directement (passthrough). - -**Avant / Apres :** -``` -# docker ps (lignes longues, ~30 tokens/ligne) # rtk docker ps (~10 tokens/ligne) -CONTAINER ID IMAGE COMMAND ... web nginx:1.25 Up 2d (healthy) -abc123def456 nginx:1.25 "/dock..." ... db postgres:16 Up 2d (healthy) -789012345678 postgres:16 "docker..." redis redis:7 Up 1d -``` - ---- - -### `rtk kubectl` -- Kubernetes - -| Commande | Description | Options | -|----------|-------------|---------| -| `rtk kubectl pods [-n ns] [-A]` | Liste compacte des pods | Namespace ou tous | -| `rtk kubectl services [-n ns] [-A]` | Liste compacte des services | Namespace ou tous | -| `rtk kubectl logs [-c container]` | Logs dedupliques | Container specifique | - -Les sous-commandes non reconnues sont transmises directement (passthrough). - ---- - -## Donnees et reseau - -### `rtk json` -- Structure JSON - -**Objectif :** Affiche la structure d'un fichier JSON sans les valeurs. - -```bash -rtk json [--depth N] # Defaut: profondeur 5 -rtk json - # Depuis stdin -``` - -**Economies :** ~60% - -**Avant / Apres :** -``` -# cat package.json (50 lignes) # rtk json package.json (10 lignes) -{ { - "name": "my-app", name: string - "version": "1.0.0", version: string - "dependencies": { dependencies: { 15 keys } - "react": "^18.2.0", devDependencies: { 8 keys } - "next": "^14.0.0", scripts: { 6 keys } - ...15 dependances... } - }, - ... -} -``` - ---- - -### `rtk env` -- Variables d'environnement - -```bash -rtk env # Toutes les variables (sensibles masquees) -rtk env -f AWS # Filtrer par nom -rtk env --show-all # Inclure les valeurs sensibles -``` - -Les variables sensibles (tokens, secrets, mots de passe) sont masquees par defaut : `AWS_SECRET_ACCESS_KEY=***`. - ---- - -### `rtk log` -- Logs dedupliques - -**Objectif :** Filtre et deduplique la sortie de logs. - -```bash -rtk log # Depuis un fichier -rtk log # Depuis stdin (pipe) -``` - -Les lignes repetees sont fusionnees : `[ERROR] Connection refused (x42)`. - -**Economies :** ~60-80% (selon la repetitivite) - ---- - -### `rtk curl` -- HTTP avec detection JSON - -```bash -rtk curl [args...] -``` - -Auto-detecte les reponses JSON et affiche le schema au lieu du contenu complet. - ---- - -### `rtk wget` -- Telechargement compact - -```bash -rtk wget [args...] -rtk wget -O - # Sortie vers stdout -``` - -Supprime les barres de progression et le bruit. - ---- - -### `rtk summary` -- Resume heuristique - -**Objectif :** Execute une commande et genere un resume heuristique de la sortie. - -```bash -rtk summary -``` - -Utile pour les commandes longues dont la sortie n'a pas de filtre dedie. - ---- - -### `rtk proxy` -- Passthrough avec suivi - -**Objectif :** Execute une commande **sans filtrage** mais enregistre l'utilisation pour le suivi. - -```bash -rtk proxy -``` - -Utile pour le debug : comparer la sortie brute avec la sortie filtree. - ---- - -## Cloud et bases de donnees - -### `rtk aws` -- AWS CLI - -```bash -rtk aws [args...] -``` - -Force la sortie JSON et compresse le resultat. Supporte tous les services AWS (sts, s3, ec2, ecs, rds, cloudformation, etc.). - ---- - -### `rtk psql` -- PostgreSQL - -```bash -rtk psql [args...] -``` - -Supprime les bordures de tableaux et compresse la sortie. - ---- - -## Stacked PRs (Graphite) - -### `rtk gt` -- Graphite - -| Commande | Description | -|----------|-------------| -| `rtk gt log` | Stack log compact | -| `rtk gt submit` | Submit compact | -| `rtk gt sync` | Sync compact | -| `rtk gt restack` | Restack compact | -| `rtk gt create` | Create compact | -| `rtk gt branch` | Branch info compact | - -Les sous-commandes non reconnues sont transmises directement ou detectees comme passthrough git. - ---- - -## Analytique et suivi - -### Systeme de tracking - -RTK enregistre chaque execution de commande dans une base SQLite : - -- **Emplacement :** `~/.local/share/rtk/tracking.db` (Linux), `~/Library/Application Support/rtk/tracking.db` (macOS) -- **Retention :** 90 jours automatique -- **Metriques :** tokens entree/sortie, pourcentage d'economies, temps d'execution, projet - ---- - -### `rtk gain` -- Statistiques d'economies - -```bash -rtk gain # Resume global -rtk gain -p # Filtre par projet courant -rtk gain --graph # Graphe ASCII (30 derniers jours) -rtk gain --history # Historique recent des commandes -rtk gain --daily # Ventilation jour par jour -rtk gain --weekly # Ventilation par semaine -rtk gain --monthly # Ventilation par mois -rtk gain --all # Toutes les ventilations -rtk gain --quota -t pro # Estimation d'economies sur le quota mensuel -rtk gain --failures # Log des echecs de parsing (commandes en fallback) -rtk gain --format json # Export JSON (pour dashboards) -rtk gain --format csv # Export CSV -``` - -**Options :** - -| Option | Court | Description | -|--------|-------|-------------| -| `--project` | `-p` | Filtrer par repertoire courant | -| `--graph` | `-g` | Graphe ASCII des 30 derniers jours | -| `--history` | `-H` | Historique recent des commandes | -| `--quota` | `-q` | Estimation d'economies sur le quota mensuel | -| `--tier` | `-t` | Tier d'abonnement : `pro`, `5x`, `20x` (defaut: `20x`) | -| `--daily` | `-d` | Ventilation quotidienne | -| `--weekly` | `-w` | Ventilation hebdomadaire | -| `--monthly` | `-m` | Ventilation mensuelle | -| `--all` | `-a` | Toutes les ventilations | -| `--format` | `-f` | Format de sortie : `text`, `json`, `csv` | -| `--failures` | `-F` | Affiche les commandes en fallback | - -**Exemple de sortie :** -``` -$ rtk gain -RTK Token Savings Summary - Total commands: 1,247 - Total input: 2,341,000 tokens - Total output: 468,200 tokens - Total saved: 1,872,800 tokens (80%) - Avg per command: 1,501 tokens saved - -Top commands: - git status 312x -82% - cargo test 156x -91% - git diff 98x -76% -``` - ---- - -### `rtk discover` -- Opportunites manquees - -**Objectif :** Analyse l'historique Claude Code pour trouver les commandes qui auraient pu etre optimisees par rtk. - -```bash -rtk discover # Projet courant, 30 derniers jours -rtk discover --all --since 7 # Tous les projets, 7 derniers jours -rtk discover -p /chemin/projet # Filtrer par projet -rtk discover --limit 20 # Max commandes par section -rtk discover --format json # Export JSON -``` - -**Options :** - -| Option | Court | Description | -|--------|-------|-------------| -| `--project` | `-p` | Filtrer par chemin de projet | -| `--limit` | `-l` | Max commandes par section (defaut: 15) | -| `--all` | `-a` | Scanner tous les projets | -| `--since` | `-s` | Derniers N jours (defaut: 30) | -| `--format` | `-f` | Format : `text`, `json` | - ---- - -### `rtk learn` -- Apprendre des erreurs - -**Objectif :** Analyse l'historique d'erreurs CLI de Claude Code pour detecter les corrections recurrentes. - -```bash -rtk learn # Projet courant -rtk learn --all --since 7 # Tous les projets -rtk learn --write-rules # Generer .claude/rules/cli-corrections.md -rtk learn --min-confidence 0.8 # Seuil de confiance (defaut: 0.6) -rtk learn --min-occurrences 3 # Occurrences minimales (defaut: 1) -rtk learn --format json # Export JSON -``` - ---- - -### `rtk cc-economics` -- Analyse economique Claude Code - -**Objectif :** Compare les depenses Claude Code (via ccusage) avec les economies RTK. - -```bash -rtk cc-economics # Resume -rtk cc-economics --daily # Ventilation quotidienne -rtk cc-economics --weekly # Ventilation hebdomadaire -rtk cc-economics --monthly # Ventilation mensuelle -rtk cc-economics --all # Toutes les ventilations -rtk cc-economics --format json # Export JSON -``` - ---- - -### `rtk hook-audit` -- Metriques du hook - -**Prerequis :** Necessite `RTK_HOOK_AUDIT=1` dans l'environnement. - -```bash -rtk hook-audit # 7 derniers jours (defaut) -rtk hook-audit --since 30 # 30 derniers jours -rtk hook-audit --since 0 # Tout l'historique -``` - ---- - -## Systeme de hooks - -### Fonctionnement - -Le hook RTK intercepte les commandes Bash dans Claude Code **avant leur execution** et les reecrit automatiquement en equivalent RTK. - -**Flux :** -``` -Claude Code "git status" - | - v -settings.json -> PreToolUse hook - | - v -rtk-rewrite.sh (bash) - | - v -rtk rewrite "git status" -> "rtk git status" - | - v -Claude Code execute "rtk git status" - | - v -Sortie filtree retournee a Claude (~10 tokens vs ~200) -``` - -**Points cles :** -- Claude ne voit jamais la recriture -- il recoit simplement une sortie optimisee -- Le hook est un delegateur leger (~50 lignes bash) qui appelle `rtk rewrite` -- Toute la logique de recriture est dans le registre Rust (`src/discover/registry.rs`) -- Les commandes deja prefixees par `rtk` passent sans modification -- Les heredocs (`<<`) ne sont pas modifies -- Les commandes non reconnues passent sans modification - -### Installation - -```bash -rtk init -g # Installation recommandee (hook + RTK.md) -rtk init -g --auto-patch # Non-interactif (CI/CD) -rtk init -g --hook-only # Hook seul, sans RTK.md -rtk init --show # Verifier l'installation -rtk init -g --uninstall # Desinstaller -``` - -### Fichiers installes - -| Fichier | Description | -|---------|-------------| -| `~/.claude/hooks/rtk-rewrite.sh` | Script hook (delegue a `rtk rewrite`) | -| `~/.claude/RTK.md` | Instructions minimales pour le LLM | -| `~/.claude/settings.json` | Enregistrement du hook PreToolUse | - -### `rtk rewrite` -- Recriture de commande - -Commande interne utilisee par le hook. Imprime la commande reecrite sur stdout (exit 0) ou sort avec exit 1 si aucun equivalent RTK n'existe. - -```bash -rtk rewrite "git status" # -> "rtk git status" (exit 0) -rtk rewrite "terraform plan" # -> (exit 1, pas de recriture) -rtk rewrite "rtk git status" # -> "rtk git status" (exit 0, inchange) -``` - -### `rtk verify` -- Verification d'integrite - -Verifie l'integrite du hook installe via un controle SHA-256. - -```bash -rtk verify -``` - -### Commandes reecrites automatiquement - -| Commande brute | Reecrite en | -|----------------|-------------| -| `git status/diff/log/add/commit/push/pull` | `rtk git ...` | -| `gh pr/issue/run` | `rtk gh ...` | -| `cargo test/build/clippy/check` | `rtk cargo ...` | -| `cat/head/tail ` | `rtk read ` | -| `rg/grep ` | `rtk grep ` | -| `ls` | `rtk ls` | -| `tree` | `rtk tree` | -| `wc` | `rtk wc` | -| `vitest/jest` | `rtk vitest run` | -| `tsc` | `rtk tsc` | -| `eslint/biome` | `rtk lint` | -| `prettier` | `rtk prettier` | -| `playwright` | `rtk playwright` | -| `prisma` | `rtk prisma` | -| `ruff check/format` | `rtk ruff ...` | -| `pytest` | `rtk pytest` | -| `mypy` | `rtk mypy` | -| `pip list/install` | `rtk pip ...` | -| `go test/build/vet` | `rtk go ...` | -| `golangci-lint` | `rtk golangci-lint` | -| `docker ps/images/logs` | `rtk docker ...` | -| `kubectl get/logs` | `rtk kubectl ...` | -| `curl` | `rtk curl` | -| `pnpm list/outdated` | `rtk pnpm ...` | - -### Exclusion de commandes - -Pour empecher certaines commandes d'etre reecrites, ajoutez-les dans `config.toml` : - -```toml -[hooks] -exclude_commands = ["curl", "playwright"] -``` - ---- - -## Configuration - -### Fichier de configuration - -**Emplacement :** `~/.config/rtk/config.toml` (Linux) ou `~/Library/Application Support/rtk/config.toml` (macOS) - -**Commandes :** -```bash -rtk config # Afficher la configuration actuelle -rtk config --create # Creer le fichier avec les valeurs par defaut -``` - -### Structure complete - -```toml -[tracking] -enabled = true # Activer/desactiver le suivi -history_days = 90 # Jours de retention (nettoyage automatique) -database_path = "/custom/path/tracking.db" # Chemin personnalise (optionnel) - -[display] -colors = true # Sortie coloree -emoji = true # Utiliser les emojis -max_width = 120 # Largeur maximale de sortie - -[filters] -ignore_dirs = [".git", "node_modules", "target", "__pycache__", ".venv", "vendor"] -ignore_files = ["*.lock", "*.min.js", "*.min.css"] - -[tee] -enabled = true # Activer la sauvegarde de sortie brute -mode = "failures" # "failures" (defaut), "always", ou "never" -max_files = 20 # Rotation : garder les N derniers fichiers -# directory = "/custom/tee/path" # Chemin personnalise (optionnel) - -[telemetry] -enabled = true # Telemetrie anonyme (1 ping/jour, opt-out possible) - -[hooks] -exclude_commands = [] # Commandes a exclure de la recriture automatique -``` - -### Variables d'environnement - -| Variable | Description | -|----------|-------------| -| `RTK_TEE_DIR` | Surcharge le repertoire tee | -| `RTK_TELEMETRY_DISABLED=1` | Desactiver la telemetrie | -| `RTK_HOOK_AUDIT=1` | Activer l'audit du hook | -| `SKIP_ENV_VALIDATION=1` | Desactiver la validation d'env (Next.js, etc.) | - ---- - -## Systeme Tee - -### Recuperation de sortie brute - -Quand une commande echoue, RTK sauvegarde automatiquement la sortie brute complete dans un fichier log. Cela permet au LLM de lire la sortie sans re-executer la commande. - -**Fonctionnement :** -1. La commande echoue (exit code != 0) -2. RTK sauvegarde la sortie brute dans `~/.local/share/rtk/tee/` -3. Le chemin du fichier est affiche dans la sortie filtree -4. Le LLM peut lire le fichier si besoin de plus de details - -**Sortie :** -``` -FAILED: 2/15 tests -[full output: ~/.local/share/rtk/tee/1707753600_cargo_test.log] -``` - -**Configuration :** - -| Parametre | Defaut | Description | -|-----------|--------|-------------| -| `tee.enabled` | `true` | Activer/desactiver | -| `tee.mode` | `"failures"` | `"failures"`, `"always"`, `"never"` | -| `tee.max_files` | `20` | Rotation : garder les N derniers | -| Taille min | 500 octets | Les sorties trop courtes ne sont pas sauvegardees | -| Taille max fichier | 1 Mo | Troncature au-dela | - ---- - -## Telemetrie - -RTK envoie un ping anonyme une fois par jour (23h d'intervalle) pour des statistiques d'utilisation. - -**Donnees envoyees :** hash de device, version, OS, architecture, nombre de commandes/24h, top commandes, pourcentage d'economies. - -**Desactiver :** -```bash -# Via variable d'environnement -export RTK_TELEMETRY_DISABLED=1 - -# Via config.toml -[telemetry] -enabled = false -``` - -Aucune donnee personnelle, aucun contenu de commande, aucun chemin de fichier n'est transmis. - ---- - -## Resume des economies par categorie - -| Categorie | Commandes | Economies typiques | -|-----------|-----------|-------------------| -| **Fichiers** | ls, tree, read, find, grep, diff | 60-80% | -| **Git** | status, log, diff, show, add, commit, push, pull | 75-92% | -| **GitHub** | pr, issue, run, api | 26-87% | -| **Tests** | cargo test, vitest, playwright, pytest, go test | 90-99% | -| **Build/Lint** | cargo build, tsc, eslint, prettier, next, ruff, clippy | 70-87% | -| **Paquets** | pnpm, npm, pip, deps, prisma | 60-80% | -| **Conteneurs** | docker, kubectl | 70-80% | -| **Donnees** | json, env, log, curl, wget | 60-80% | -| **Analytique** | gain, discover, learn, cc-economics | N/A (meta) | - ---- - -## Nombre total de commandes - -RTK supporte **45+ commandes** reparties en 9 categories, avec passthrough automatique pour les sous-commandes non reconnues. Cela en fait un proxy universel : il est toujours sur a utiliser en prefixe. diff --git a/docs/TECHNICAL.md b/docs/TECHNICAL.md deleted file mode 100644 index 04522683d..000000000 --- a/docs/TECHNICAL.md +++ /dev/null @@ -1,432 +0,0 @@ -# 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 | Shell hook | `preToolUse` hook | 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. diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md deleted file mode 100644 index cf52f026d..000000000 --- a/docs/TROUBLESHOOTING.md +++ /dev/null @@ -1,337 +0,0 @@ -# RTK Troubleshooting Guide - -## Problem: "rtk gain" command not found - -### Symptom -```bash -$ rtk --version -rtk 1.0.0 # (or similar) - -$ rtk gain -rtk: 'gain' is not a rtk command. See 'rtk --help'. -``` - -### Root Cause -You installed the **wrong rtk package**. You have **Rust Type Kit** (reachingforthejack/rtk) instead of **Rust Token Killer** (rtk-ai/rtk). - -### Solution - -**1. Uninstall the wrong package:** -```bash -cargo uninstall rtk -``` - -**2. Install the correct one (Token Killer):** - -#### Quick Install (Linux/macOS) -```bash -curl -fsSL https://github.com/rtk-ai/rtk/blob/master/install.sh | sh -``` - -#### Alternative: Manual Installation -```bash -cargo install --git https://github.com/rtk-ai/rtk -``` - -**3. Verify installation:** -```bash -rtk --version -rtk gain # MUST show token savings stats, not error -``` - -If `rtk gain` now works, installation is correct. - ---- - -## Problem: Confusion Between Two "rtk" Projects - -### The Two Projects - -| Project | Repository | Purpose | Key Command | -|---------|-----------|---------|-------------| -| **Rust Token Killer** ✅ | rtk-ai/rtk | LLM token optimizer for Claude Code | `rtk gain` | -| **Rust Type Kit** ❌ | reachingforthejack/rtk | Rust codebase query and type generator | `rtk query` | - -### How to Identify Which One You Have - -```bash -# Check if "gain" command exists -rtk gain - -# Token Killer → Shows token savings stats -# Type Kit → Error: "gain is not a rtk command" -``` - ---- - -## Problem: cargo install rtk installs wrong package - -### Why This Happens -If **Rust Type Kit** is published to crates.io under the name `rtk`, running `cargo install rtk` will install the wrong package. - -### Solution -**NEVER use** `cargo install rtk` without verifying. - -**Always use explicit repository URLs:** - -```bash -# CORRECT - Token Killer -cargo install --git https://github.com/rtk-ai/rtk - -# OR install from fork -git clone https://github.com/rtk-ai/rtk.git -cd rtk && git checkout feat/all-features -cargo install --path . --force -``` - -**After any installation, ALWAYS verify:** -```bash -rtk gain # Must work if you want Token Killer -``` - ---- - -## Problem: RTK not working in Claude Code - -### Symptom -Claude Code doesn't seem to be using rtk, outputs are verbose. - -### Checklist - -**1. Verify rtk is installed and correct:** -```bash -rtk --version -rtk gain # Must show stats -``` - -**2. Initialize rtk for Claude Code:** -```bash -# Global (all projects) -rtk init --global - -# Per-project -cd /your/project -rtk init -``` - -**3. Verify CLAUDE.md file exists:** -```bash -# Check global -cat ~/.claude/CLAUDE.md | grep rtk - -# Check project -cat ./CLAUDE.md | grep rtk -``` - -**4. Install auto-rewrite hook (recommended for automatic RTK usage):** - -**Option A: Automatic (recommended)** -```bash -rtk init -g -# → Installs hook + RTK.md automatically -# → Follow printed instructions to add hook to ~/.claude/settings.json -# → Restart Claude Code - -# Verify installation -rtk init --show # Should show "✅ Hook: executable, with guards" -``` - -**Option B: Manual (fallback)** -```bash -# Copy hook to Claude Code hooks directory -mkdir -p ~/.claude/hooks -cp .claude/hooks/rtk-rewrite.sh ~/.claude/hooks/ -chmod +x ~/.claude/hooks/rtk-rewrite.sh -``` - -Then add to `~/.claude/settings.json` (replace `~` with full path): -```json -{ - "hooks": { - "PreToolUse": [ - { - "matcher": "Bash", - "hooks": [ - { - "type": "command", - "command": "/Users/yourname/.claude/hooks/rtk-rewrite.sh" - } - ] - } - ] - } -} -``` - -**Note**: Use absolute path in `settings.json`, not `~/.claude/...` - ---- - -## Problem: RTK not working in OpenCode - -### Symptom -OpenCode runs commands without rtk, outputs are verbose. - -### Checklist - -**1. Verify rtk is installed and correct:** -```bash -rtk --version -rtk gain # Must show stats -``` - -**2. Install the OpenCode plugin (global only):** -```bash -rtk init -g --opencode -``` - -**3. Verify plugin file exists:** -```bash -ls -la ~/.config/opencode/plugins/rtk.ts -``` - -**4. Restart OpenCode** -OpenCode must be restarted to load the plugin. - -**5. Verify status:** -```bash -rtk init --show # Should show "OpenCode: plugin installed" -``` - ---- - -## Problem: RTK commands fail on Windows ("program not found" or "No such file") - -### Symptom -``` -rtk vitest --run -# Error: program not found -# Or: The system cannot find the file specified - -rtk lint . -# Error: No such file or directory -``` - -### Root Cause -On Windows, Node.js tools (vitest, eslint, tsc, etc.) are installed as `.CMD` or `.BAT` wrapper scripts, not as native `.exe` binaries. Rust's `std::process::Command::new("vitest")` does not honor the Windows `PATHEXT` environment variable, so it cannot find `vitest.CMD` even when it's on PATH. - -### Solution -Update to rtk v0.23.1+ which resolves this via the `which` crate for proper PATH+PATHEXT resolution. All 16+ command modules now use `resolved_command()` instead of `Command::new()`. - -```bash -cargo install --git https://github.com/rtk-ai/rtk -rtk --version # Should be 0.23.1+ -``` - -### Affected Commands -All commands that spawn external tools: `rtk vitest`, `rtk lint`, `rtk tsc`, `rtk pnpm`, `rtk playwright`, `rtk prisma`, `rtk next`, `rtk prettier`, `rtk ruff`, `rtk pytest`, `rtk pip`, `rtk mypy`, `rtk golangci-lint`, and others. - ---- - -## Problem: "command not found: rtk" after installation - -### Symptom -```bash -$ cargo install --path . --force - Compiling rtk v0.7.1 - Finished release [optimized] target(s) - Installing ~/.cargo/bin/rtk - -$ rtk --version -zsh: command not found: rtk -``` - -### Root Cause -`~/.cargo/bin` is not in your PATH. - -### Solution - -**1. Check if cargo bin is in PATH:** -```bash -echo $PATH | grep -o '[^:]*\.cargo[^:]*' -``` - -**2. If not found, add to PATH:** - -For **bash** (`~/.bashrc`): -```bash -export PATH="$HOME/.cargo/bin:$PATH" -``` - -For **zsh** (`~/.zshrc`): -```bash -export PATH="$HOME/.cargo/bin:$PATH" -``` - -For **fish** (`~/.config/fish/config.fish`): -```fish -set -gx PATH $HOME/.cargo/bin $PATH -``` - -**3. Reload shell config:** -```bash -source ~/.bashrc # or ~/.zshrc or restart terminal -``` - -**4. Verify:** -```bash -which rtk -rtk --version -rtk gain -``` - ---- - -## Problem: Compilation errors during installation - -### Symptom -```bash -$ cargo install --path . -error: failed to compile rtk v0.7.1 -``` - -### Solutions - -**1. Update Rust toolchain:** -```bash -rustup update stable -rustup default stable -``` - -**2. Clean and rebuild:** -```bash -cargo clean -cargo build --release -cargo install --path . --force -``` - -**3. Check Rust version (minimum required):** -```bash -rustc --version # Should be 1.70+ for most features -``` - -**4. If still fails, report issue:** -- GitHub: https://github.com/rtk-ai/rtk/issues - ---- - -## Need More Help? - -**Report issues:** -- Fork-specific: https://github.com/rtk-ai/rtk/issues -- Upstream: https://github.com/rtk-ai/rtk/issues - -**Run the diagnostic script:** -```bash -# From the rtk repository root -bash scripts/check-installation.sh -``` - -This script will check: -- ✅ RTK installed and in PATH -- ✅ Correct version (Token Killer, not Type Kit) -- ✅ Available features (pnpm, vitest, next, etc.) -- ✅ Claude Code integration (CLAUDE.md files) -- ✅ Auto-rewrite hook status - -The script provides specific fix commands for any issues found. diff --git a/docs/architecture/decisions/proxy-architecture.md b/docs/architecture/decisions/proxy-architecture.md deleted file mode 100644 index 7c2ccd9ba..000000000 --- a/docs/architecture/decisions/proxy-architecture.md +++ /dev/null @@ -1,68 +0,0 @@ ---- -title: Proxy Architecture -description: ADR — why RTK uses a CLI proxy pattern instead of shell aliases or wrappers -sidebar: - order: 2 ---- - -# ADR: Proxy Architecture - -## Decision - -RTK is a CLI proxy: a single binary that intercepts commands, executes them as subprocesses, filters the output, and exits. Users invoke `rtk git status` instead of `git status`. - -Hooks extend this by making the interception transparent — the AI agent's command is rewritten before execution, so neither the agent nor the user types `rtk`. - -## Alternatives considered - -### Shell aliases - -```bash -alias git='rtk git' -alias cargo='rtk cargo' -``` - -**Rejected because:** -- Requires shell configuration per-user, per-machine, per-shell -- Doesn't work for non-interactive contexts (scripts, CI, AI agents) -- Can't be installed programmatically without modifying shell dotfiles -- Breaks if the user has other aliases or functions with the same name - -### Shell function wrappers - -Similar to aliases but more fragile. Same problems. - -### `LD_PRELOAD` / dynamic linking interception - -**Rejected because:** -- Platform-specific (Linux only with glibc) -- Security restrictions (macOS SIP, container environments) -- Complex to implement, maintain, and debug - -### Hook-only approach (no explicit `rtk` prefix) - -Make RTK entirely invisible — install hooks and never expose `rtk ` as a user-facing interface. - -**Rejected because:** -- Users need a way to invoke RTK explicitly for debugging (`rtk git status -vvv`) -- `rtk gain` and `rtk discover` need a namespace -- Transparent hooks are additive, not a replacement for the explicit interface - -## Why the proxy pattern works - -**Single binary, no configuration:** `rtk git status` works identically on macOS, Linux, and Windows. No dotfiles. No shell-specific setup. - -**Explicit and debuggable:** `-v`/`-vv`/`-vvv` flags expose what RTK is doing at each phase. `RTK_DISABLED=1` bypasses it for one command. - -**Exit code preservation:** RTK propagates the underlying tool's exit code. CI pipelines that check `$?` work correctly. - -**Fail-safe:** If RTK's filter fails, it falls back to raw output. The user always gets a result. - -**Hook interception as an enhancement:** The hook layer adds transparency on top of the proxy pattern — it rewrites `git status` to `rtk git status` before the agent sees it. But the proxy interface remains available for direct use, debugging, and tools that can't be hooked. - -## Consequences - -- Every supported command needs a module in `src/cmds/` -- Unsupported commands pass through transparently (no breakage) -- The binary grows as new commands are added (~5MB currently, well within the `<5MB` soft target after stripping) -- Adding a new command = adding a module + registering in `main.rs` + adding rewrite pattern in `discover/registry.rs` diff --git a/docs/architecture/decisions/why-no-async.md b/docs/architecture/decisions/why-no-async.md deleted file mode 100644 index 299c51a9f..000000000 --- a/docs/architecture/decisions/why-no-async.md +++ /dev/null @@ -1,52 +0,0 @@ ---- -title: Why No Async -description: ADR — why RTK is single-threaded and does not use tokio or async runtimes -sidebar: - order: 1 ---- - -# ADR: Why No Async - -## Decision - -RTK is single-threaded. No `tokio`, `async-std`, or `futures`. All I/O is blocking. - -## Context - -RTK is a CLI proxy that runs for milliseconds and exits. The typical invocation: - -1. Parse CLI arguments (~0.1ms) -2. Spawn one subprocess and capture its output (~2-5ms) -3. Filter the output (~0.1ms) -4. Print and exit - -There is no concurrent I/O, no network server, no parallel request handling. - -## Consequences - -**Why async would hurt:** - -- `tokio` adds 5-10ms to startup time from runtime initialization. RTK's target is `<10ms` total. Async would consume half the budget before the first useful line of code. -- The entire value proposition of RTK is zero-overhead transparency. If developers perceive any delay, they disable it. -- One subprocess. One output stream. No concurrency needed. - -**Why blocking I/O is correct here:** - -- `std::process::Command::output()` captures stdout + stderr in one blocking call. This is exactly what RTK needs. -- No event loop required. No `.await` noise in filter code. -- Binary stays under 5MB. No runtime dependencies. - -## Tradeoffs - -**What we give up:** -- Hypothetical future parallelism (e.g., running multiple filters in parallel). Not needed today. -- Async ecosystem crates (reqwest, sqlx). RTK uses `rusqlite` (sync) and `ureq` (sync) instead. - -**What we gain:** -- `<10ms` startup, always. -- Simple, readable filter code with no `.await` punctuation. -- No runtime initialization path that can fail. - -## Rule - -If you add a dependency that pulls in `tokio` or any async runtime, the PR will be rejected. Check before adding: `cargo tree | grep tokio`. diff --git a/docs/architecture/diagrams/command-flow.md b/docs/architecture/diagrams/command-flow.md deleted file mode 100644 index 6d8f06267..000000000 --- a/docs/architecture/diagrams/command-flow.md +++ /dev/null @@ -1,65 +0,0 @@ ---- -title: Command Flow -description: End-to-end diagram of how a command flows through RTK from agent to LLM -sidebar: - order: 1 ---- - -# Command Flow - -End-to-end flow from the AI agent issuing a command to the filtered output reaching the LLM. - -## With hook (transparent rewrite) - -```mermaid -flowchart TD - A["AI Agent\n(Claude Code, Cursor, etc.)"] -->|"runs: cargo test"| B - - subgraph HOOK ["Hook Interception (PreToolUse)"] - B["Hook reads JSON input\nextract command string"] --> C - C["rtk rewrite 'cargo test'"] --> D - D{"Registry match?"} - D -->|"yes"| E["returns 'rtk cargo test'"] - D -->|"no match"| F["returns original unchanged"] - end - - E --> G - F --> G - - subgraph RTK ["RTK Binary"] - G["Phase 1: Parse\nClap → Commands::Cargo"] --> H - H["Phase 2: Route\ncargo::run(args)"] --> I - I["Phase 3: Execute\nstd::process::Command::new('cargo')\n.args(['test'])"] --> J - J["Phase 4: Filter\nfailures only\n200 lines → 5 lines"] --> K - K["Phase 5: Print\nprintln!(filtered)"] --> L - L["Phase 6: Track\nSQLite INSERT\n(input=5000tok, output=50tok)"] - end - - K -->|"filtered output"| M["LLM Context\n~90% fewer tokens"] -``` - -## Without hook (direct usage) - -```mermaid -flowchart LR - A["Developer\ntype: rtk git status"] --> B["RTK Binary"] - B --> C["git status (subprocess)"] - C -->|"20 lines raw"| B - B -->|"5 lines filtered"| D["Terminal\n(or LLM context)"] -``` - -## Filter lookup (TOML path) - -```mermaid -flowchart LR - CMD["rtk my-tool args"] --> P1 - P1{"1. .rtk/filters.toml\n(project-local)"} - P1 -->|"match"| WIN["apply filter → print"] - P1 -->|"no match"| P2 - P2{"2. ~/.config/rtk/filters.toml\n(user-global)"} - P2 -->|"match"| WIN - P2 -->|"no match"| P3 - P3{"3. BUILTIN_TOML\n(binary)"} - P3 -->|"match"| WIN - P3 -->|"no match"| P4[["exec raw\n(passthrough)"]] -``` diff --git a/docs/architecture/diagrams/filter-pipeline.md b/docs/architecture/diagrams/filter-pipeline.md deleted file mode 100644 index dda20e9d4..000000000 --- a/docs/architecture/diagrams/filter-pipeline.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: Filter Pipeline -description: How a TOML filter goes from file to execution — build pipeline and runtime stages -sidebar: - order: 2 ---- - -# Filter Pipeline - -## Build pipeline - -```mermaid -flowchart TD - A[["src/filters/my-tool.toml\n(new file)"]] --> B - - subgraph BUILD ["cargo build"] - B["build.rs\n1. ls src/filters/*.toml\n2. sort alphabetically\n3. concat → BUILTIN_TOML"] --> C - C{"TOML valid?\nDuplicate names?"} -->|"❌ panic"| D[["Build fails\nerror points to bad file"]] - C -->|"✅ ok"| E[["OUT_DIR/builtin_filters.toml\n(generated)"]] - E --> F["rustc embeds via include_str!"] - F --> G[["rtk binary\nBUILTIN_TOML embedded"]] - end - - subgraph TESTS ["cargo test"] - H["test_builtin_filter_count\nassert_eq!(filters.len(), N)"] -->|"❌ wrong count"| I[["FAIL"]] - J["test_builtin_all_filters_present\nassert!(names.contains('my-tool'))"] -->|"❌ name missing"| K[["FAIL"]] - L["test_builtin_all_filters_have_inline_tests\nassert!(tested.contains(name))"] -->|"❌ no tests"| M[["FAIL"]] - end - - subgraph VERIFY ["rtk verify"] - N["runs [[tests.my-tool]]\ninput → filter → compare expected"] - N -->|"❌ mismatch"| O[["FAIL\nshows actual vs expected"]] - N -->|"✅ pass"| P[["All tests passed"]] - end - - G --> H & J & L & N -``` - -## Runtime stages - -```mermaid -flowchart TD - CMD["rtk my-tool args"] --> LOOKUP - - subgraph LOOKUP ["Filter Lookup"] - L1{".rtk/filters.toml"} -->|"match"| APPLY - L1 -->|"no match"| L2 - L2{"~/.config/rtk/filters.toml"} -->|"match"| APPLY - L2 -->|"no match"| L3 - L3{"BUILTIN_TOML"} -->|"match"| APPLY - L3 -->|"no match"| RAW[["exec raw (passthrough)"]] - end - - APPLY --> EXEC["exec command\ncapture stdout"] - EXEC --> PIPE - - subgraph PIPE ["8-stage filter pipeline"] - S1["1. strip_ansi"] --> S2 - S2["2. replace"] --> S3 - S3{"3. match_output\nshort-circuit?"} - S3 -->|"✅ match"| MSG[["emit on_match\nstop"]] - S3 -->|"no match"| S4 - S4["4. strip/keep_lines"] --> S5 - S5["5. truncate_lines_at"] --> S6 - S6["6. tail_lines"] --> S7 - S7["7. max_lines"] --> S8 - S8{"8. output empty?"} - S8 -->|"yes"| EMPTY[["emit on_empty"]] - S8 -->|"no"| OUT[["print filtered output\n+ exit code"]] - end -``` diff --git a/docs/architecture/index.md b/docs/architecture/index.md deleted file mode 100644 index b64e5a332..000000000 --- a/docs/architecture/index.md +++ /dev/null @@ -1,15 +0,0 @@ ---- -title: Architecture -description: System design, diagrams, and architectural decision records for RTK -sidebar: - order: 1 ---- - -# Architecture - -Conceptual documentation covering how RTK is designed and why key decisions were made. - -## What's in this section - -- **Diagrams** — Visual representations of the command flow and filter pipeline -- **Decisions** — Architectural decision records (ADRs) for key design choices diff --git a/docs/filter-workflow.md b/docs/filter-workflow.md deleted file mode 100644 index 0b0d32c1b..000000000 --- a/docs/filter-workflow.md +++ /dev/null @@ -1,102 +0,0 @@ -# How a TOML filter goes from file to execution - -This document explains what happens between "I created `src/filters/my-tool.toml`" and "RTK filters the output of `my-tool`". - -## Build pipeline - -```mermaid -flowchart TD - A[["📄 src/filters/my-tool.toml\n(new file)"]] --> B - - subgraph BUILD ["🔨 cargo build"] - B["build.rs\n① ls src/filters/*.toml\n② sort alphabetically\n③ concat → schema_version = 1 + all files"] --> C - C{"TOML valid?\nDuplicate names?"} -->|"❌ panic! (build fails)"| D[["🛑 Error message\npoints to bad file"]] - C -->|"✅ ok"| E[["OUT_DIR/builtin_filters.toml\n(generated file)"]] - E --> F["rustc\ninclude_str!(concat!(env!(OUT_DIR),\n'/builtin_filters.toml'))"] - F --> G[["🦀 rtk binary\nBUILTIN_TOML embedded"]] - end - - subgraph TESTS ["🧪 cargo test"] - H["test_builtin_filter_count\nassert_eq!(filters.len(), N)"] -->|"❌ count wrong"| I[["FAIL\n'Expected N, got N+1'\nUpdate the count'"]] - J["test_builtin_all_expected_\nfilters_present\nassert!(names.contains('my-tool'))"] -->|"❌ name missing"| K[["FAIL\n'my-tool is missing—\nwas its .toml deleted?'"]] - L["test_builtin_all_filters_\nhave_inline_tests\nassert!(tested.contains(name))"] -->|"❌ no tests"| M[["FAIL\n'Add tests.my-tool\nentries'"]] - end - - subgraph VERIFY ["✅ rtk verify"] - N["runs [[tests.my-tool]]\ninput → filter → compare expected"] - N -->|"❌ mismatch"| O[["FAIL\nshows actual vs expected"]] - N -->|"✅ pass"| P[["60/60 tests passed"]] - end - - G --> H - G --> J - G --> L - G --> N - - subgraph RUNTIME ["⚡ rtk my-tool --verbose"] - Q["Claude Code hook\nmy-tool ... → rtk my-tool ..."] --> R - R["TomlFilterRegistry::load()\n① .rtk/filters.toml (project)\n② ~/.config/rtk/filters.toml (user)\n③ BUILTIN_TOML (binary)\n④ passthrough"] --> S - S{"match_command\n'^my-tool\\b'\nmatches?"} -->|"No match"| T[["exec raw\n(passthrough)"]] - S -->|"✅ match"| U["exec command\ncapture stdout"] - U --> V - - subgraph PIPELINE ["8-stage filter pipeline"] - V["strip_ansi"] --> W["replace"] - W --> X{"match_output\nshort-circuit?"} - X -->|"✅ pattern matched"| Y[["emit message\nstop pipeline"]] - X -->|"no match"| Z["strip/keep_lines"] - Z --> AA["truncate_lines_at"] - AA --> AB["tail_lines"] - AB --> AC["max_lines"] - AC --> AD{"output\nempty?"} - AD -->|"yes"| AE[["emit on_empty"]] - AD -->|"no"| AF[["print filtered\noutput + exit code"]] - end - end - - G --> Q - - style BUILD fill:#1e3a5f,color:#fff - style TESTS fill:#1a3a1a,color:#fff - style VERIFY fill:#2d1b69,color:#fff - style RUNTIME fill:#3a1a1a,color:#fff - style PIPELINE fill:#4a2a00,color:#fff - style D fill:#8b0000,color:#fff - style I fill:#8b0000,color:#fff - style K fill:#8b0000,color:#fff - style M fill:#8b0000,color:#fff - style O fill:#8b0000,color:#fff -``` - -## Step-by-step summary - -| Step | Who | What happens | Fails if | -|------|-----|--------------|----------| -| 1 | Contributor | Creates `src/filters/my-tool.toml` | — | -| 2 | `build.rs` | Concatenates all `.toml` files alphabetically | TOML syntax error, duplicate filter name | -| 3 | `rustc` | Embeds result in binary via `BUILTIN_TOML` const | — | -| 4 | `cargo test` | 3 guards check count, names, inline test presence | Count not updated, name not in list, no `[[tests.*]]` | -| 5 | `rtk verify` | Runs each `[[tests.my-tool]]` entry | Filter logic doesn't match expected output | -| 6 | Runtime | Hook rewrites command, registry looks up filter, pipeline runs | No match → passthrough (not an error) | - -## Filter lookup priority at runtime - -```mermaid -flowchart LR - CMD["rtk my-tool args"] --> P1 - P1{"1. .rtk/filters.toml\n(project-local)"} - P1 -->|"✅ match"| WIN["apply filter"] - P1 -->|"no match"| P2 - P2{"2. ~/.config/rtk/filters.toml\n(user-global)\n(macOS alt: ~/Library/Application Support/rtk/filters.toml)"} - P2 -->|"✅ match"| WIN - P2 -->|"no match"| P3 - P3{"3. BUILTIN_TOML\n(binary)"} - P3 -->|"✅ match"| WIN - P3 -->|"no match"| P4[["exec raw\n(passthrough)"]] -``` - -First match wins. A project filter with the same name as a built-in shadows the built-in and triggers a warning: - -``` -[rtk] warning: filter 'make' is shadowing a built-in filter -``` diff --git a/docs/guide/analytics/discover.md b/docs/guide/analytics/discover.md deleted file mode 100644 index bcbd248ec..000000000 --- a/docs/guide/analytics/discover.md +++ /dev/null @@ -1,49 +0,0 @@ ---- -title: Discover Missed Savings -description: Find commands that ran without RTK and could have saved tokens -sidebar: - order: 2 ---- - -# Discover Missed Savings - -`rtk discover` analyzes Claude Code's command history to find commands that ran without RTK and could have been optimized. - -## Usage - -```bash -rtk discover # current project, last 30 days -rtk discover --all --since 7 # all projects, last 7 days -rtk discover -p /path/to/project # filter by project path -rtk discover --limit 20 # max commands per section -rtk discover --format json # JSON export -``` - -## Options - -| Option | Short | Description | -|--------|-------|-------------| -| `--project` | `-p` | Filter by project path | -| `--limit` | `-l` | Max commands per section (default: 15) | -| `--all` | `-a` | Scan all projects | -| `--since` | `-s` | Last N days (default: 30) | -| `--format` | `-f` | Output format: `text`, `json` | - -## Example output - -``` -RTK Missed Opportunities (last 30 days) - -Commands that could have used RTK: - git log --oneline -20 ×12 (est. 80% savings each) - cargo test ×8 (est. 90% savings each) - pnpm list ×5 (est. 70% savings each) - -Estimated savings missed: ~340K tokens -``` - -## How it works - -RTK reads Claude Code's command history database (the same one that backs `rtk gain --history`). It matches raw commands against the RTK rewrite registry and flags instances where RTK was not used but a filter exists. - -Use this after setting up RTK to see how much you were leaving on the table before. diff --git a/docs/guide/analytics/economics.md b/docs/guide/analytics/economics.md deleted file mode 100644 index 3287d6915..000000000 --- a/docs/guide/analytics/economics.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Claude Code Economics -description: Compare your Claude Code spending against RTK token savings -sidebar: - order: 3 ---- - -# Claude Code Economics - -`rtk cc-economics` compares your Claude Code API spending (via ccusage) against the token savings RTK has generated. It answers: "what is RTK actually saving me in dollars?" - -## Requirements - -Requires [ccusage](https://github.com/ryoppippi/ccusage) to be installed and have data. ccusage tracks Claude Code API costs from your usage history. - -## Usage - -```bash -rtk cc-economics # summary -rtk cc-economics --daily # day-by-day breakdown -rtk cc-economics --weekly # week-by-week -rtk cc-economics --monthly # month-by-month -rtk cc-economics --all # all breakdowns at once -rtk cc-economics --format json -``` - -## Example output - -``` -Claude Code Economics -════════════════════════════════════════ -Total API cost $12.40 (30 days) -RTK tokens saved 1.2M (30 days) -Estimated savings $3.20 (26% of bill) - -At current savings rate: - Monthly reduction: ~$3.20/mo - Annual reduction: ~$38/yr -``` - -## How savings are estimated - -RTK estimates dollar savings by applying Claude's input token pricing to the tokens it prevented from reaching the LLM: - -``` -Saved tokens × (input price per token) = estimated dollar savings -``` - -This is an estimate — actual savings depend on which model was used for each request. - -## See also - -- [Token Savings Analytics](./gain.md) — the `rtk gain` command for raw token counts -- [Discover Missed Savings](./discover.md) — find commands that ran without RTK diff --git a/docs/guide/commands/cargo.md b/docs/guide/commands/cargo.md deleted file mode 100644 index dc2df8f77..000000000 --- a/docs/guide/commands/cargo.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Cargo (Rust) -description: RTK filters for cargo — build, test, check, clippy, and nextest -sidebar: - order: 2 ---- - -# Cargo (Rust) - -RTK filters the most verbose cargo outputs and passes through unrecognized subcommands unchanged. - -## cargo test — 90% savings - -```bash -rtk cargo test [args...] -``` - -Shows failures only. On success, shows a compact summary. - -**Before (200+ lines on failure):** -``` -running 15 tests -test utils::test_parse ... ok -test utils::test_format ... ok -test utils::test_edge_case ... FAILED - -failures: - ----- utils::test_edge_case stdout ---- -thread 'utils::test_edge_case' panicked at 'assertion failed: ...' -...150 lines of backtrace... -``` - -**After (~5 lines):** -``` -FAILED: 2/15 tests - test_edge_case: assertion failed at utils.rs:42 - test_overflow: panic at utils.rs:18 -[full output: ~/.local/share/rtk/tee/cargo_test_1234.log] -``` - -The tee file path lets you (or your AI assistant) read the full output if needed without re-running the command. - -## cargo nextest — failures only - -```bash -rtk cargo nextest [run|list|--lib] [args...] -``` - -Same behavior as `cargo test` — filters to failures only. - -## cargo build — 80% savings - -```bash -rtk cargo build [args...] -``` - -Removes all "Compiling..." lines, keeps errors and the final result. - -**Before:** -``` - Compiling proc-macro2 v1.0.79 - Compiling unicode-ident v1.0.12 - Compiling quote v1.0.35 - ...200 crate lines... - Compiling rtk v0.28.0 - Finished release [optimized] target(s) in 12.34s -``` - -**After:** -``` -Finished release [optimized] in 12.34s -``` - -## cargo check — 80% savings - -```bash -rtk cargo check [args...] -``` - -Removes "Checking..." lines, keeps errors. - -## cargo clippy — 80% savings - -```bash -rtk cargo clippy [args...] -``` - -Groups warnings by lint rule. - -**Before (50 lines for 3 warnings):** -``` -warning: unused variable `x` - --> src/main.rs:42:9 - | -42 | let x = 5; - | ^ help: if this is intentional, prefix it with an underscore: `_x` - = note: `#[warn(unused_variables)]` on by default -... -``` - -**After:** -``` -src/main.rs — 2 warnings - unused_variables (x2): src/main.rs:42, src/main.rs:67 -``` - -## cargo install - -```bash -rtk cargo install [args...] -``` - -Removes dependency compilation noise, keeps the install result and any errors. - -## Generic test wrapper - -```bash -rtk test -``` - -Runs any test command and shows failures only. Works with any test runner: - -```bash -rtk test cargo test -rtk test npm test -rtk test bun test -rtk test pytest -``` - -## Error-only wrapper - -```bash -rtk err -``` - -Runs any command and shows errors and warnings only: - -```bash -rtk err cargo build -rtk err npm run build -``` diff --git a/docs/guide/commands/containers.md b/docs/guide/commands/containers.md deleted file mode 100644 index c5d6a52c1..000000000 --- a/docs/guide/commands/containers.md +++ /dev/null @@ -1,90 +0,0 @@ ---- -title: Containers -description: RTK filters for Docker and Kubernetes -sidebar: - order: 9 ---- - -# Containers - -RTK compresses Docker and Kubernetes command output into compact, token-efficient summaries. - -## Docker - -### docker ps — 80% savings - -```bash -rtk docker ps [args...] -``` - -**Before:** -``` -CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES -abc123def456 nginx:1.25 "/docker-entrypoint.…" 2 days ago Up 2 days (healthy) 0.0.0.0:80->80/tcp web -789012345678 postgres:16 "docker-entrypoint.s…" 2 days ago Up 2 days (healthy) 0.0.0.0:5432->5432/tcp db -``` - -**After:** -``` -web nginx:1.25 Up 2d (healthy) -db postgres:16 Up 2d (healthy) -``` - -### docker images — 80% savings - -```bash -rtk docker images [args...] -``` - -Compact list: name, tag, size, age. - -### docker logs — 70% savings - -```bash -rtk docker logs [args...] -``` - -Deduplicates repeated log lines: `[ERROR] Connection refused (×42)`. - -### Docker Compose - -```bash -rtk docker compose ps # compact service list — 80% -rtk docker compose logs [service] # deduplicated logs — 70% -rtk docker compose build [service] # build summary — 60% -``` - -Unrecognized `docker compose` subcommands pass through. - -## Kubernetes - -### kubectl pods — 75%+ savings - -```bash -rtk kubectl pods [-n namespace] [-A] -``` - -Compact pod list with status. - -### kubectl services - -```bash -rtk kubectl services [-n namespace] [-A] -``` - -### kubectl logs — 70% savings - -```bash -rtk kubectl logs [-c container] -``` - -Deduplicates repeated log lines. - -### Passthrough - -All other `kubectl` subcommands pass through to kubectl: - -```bash -rtk kubectl apply -f deployment.yaml # passes through -rtk kubectl describe pod # passes through -``` diff --git a/docs/guide/commands/data.md b/docs/guide/commands/data.md deleted file mode 100644 index 239e1aa2e..000000000 --- a/docs/guide/commands/data.md +++ /dev/null @@ -1,103 +0,0 @@ ---- -title: Data & Network -description: RTK filters for JSON, environment variables, logs, curl, and wget -sidebar: - order: 11 ---- - -# Data & Network - -RTK compresses structured data, network output, and log streams. - -## json — 60% savings - -```bash -rtk json [--depth N] # default: depth 5 -rtk json - # from stdin -``` - -Shows JSON structure (keys and types) without values. Useful for exploring large API responses or config files. - -**Before — `cat package.json` (50 lines):** -```json -{ - "name": "my-app", - "version": "1.0.0", - "dependencies": { - "react": "^18.2.0", - "next": "^14.0.0", - ...15 dependencies... - } -} -``` - -**After — `rtk json package.json` (10 lines):** -``` -{ - name: string - version: string - dependencies: { 15 keys } - devDependencies: { 8 keys } - scripts: { 6 keys } -} -``` - -## env — sensitive values masked - -```bash -rtk env # all variables (sensitive values masked) -rtk env -f AWS # filter by name -rtk env --show-all # include sensitive values -``` - -Sensitive variables (tokens, secrets, passwords) are masked by default: `AWS_SECRET_ACCESS_KEY=***`. - -## log — 60-80% savings - -```bash -rtk log # from a file -rtk log # from stdin (pipe) -``` - -Deduplicates repeated log lines: `[ERROR] Connection refused (×42)`. Savings depend on how repetitive the log is. - -## curl — HTTP with JSON detection - -```bash -rtk curl [args...] -``` - -Auto-detects JSON responses and shows schema instead of full content. Falls back to raw output for non-JSON. - -## wget - -```bash -rtk wget [args...] -rtk wget -O - # output to stdout -``` - -Removes progress bars and download noise. - -## aws - -```bash -rtk aws [args...] -``` - -Forces JSON output mode and compresses the result. Supports all AWS services (sts, s3, ec2, ecs, rds, cloudformation, etc.). - -## psql - -```bash -rtk psql [args...] -``` - -Removes table borders and compresses query output. - -## summary - -```bash -rtk summary -``` - -Runs any command and generates a heuristic summary of the output. Useful for commands that don't have a dedicated RTK filter. diff --git a/docs/guide/commands/dotnet.md b/docs/guide/commands/dotnet.md deleted file mode 100644 index 08d93d05e..000000000 --- a/docs/guide/commands/dotnet.md +++ /dev/null @@ -1,71 +0,0 @@ ---- -title: .NET -description: RTK filters for dotnet build, test, and MSBuild logs -sidebar: - order: 8 ---- - -# .NET - -RTK covers .NET build, test, and diagnostic outputs. - -## dotnet build — 70-80% savings - -```bash -rtk dotnet build [args...] -``` - -Removes per-project compilation lines, keeps errors and build summary. - -**Before:** -``` -Build started... -Microsoft (R) Build Engine version 17.x - Restore complete (1.2s) - MyLib -> bin/Debug/net8.0/MyLib.dll - MyApp -> bin/Debug/net8.0/MyApp.dll - -Build succeeded. - 0 Warning(s) - 0 Error(s) -Time Elapsed 00:00:04.23 -``` - -**After:** -``` -Build succeeded. 0 warnings, 0 errors (4.23s) -``` - -## dotnet test — 85% savings - -```bash -rtk dotnet test [args...] -``` - -Shows failures only. On success, compact summary. - -## MSBuild binary logs - -```bash -rtk dotnet binlog [path/to/file.binlog] -``` - -Parses `.binlog` binary log files and displays a compact error/warning summary. - -## dotnet format - -```bash -rtk dotnet format [args...] -``` - -Shows only files that were reformatted or have formatting issues. - -## Passthrough - -Other `dotnet` subcommands pass through unchanged: - -```bash -rtk dotnet run # passes through -rtk dotnet publish # passes through -rtk dotnet ef migrate # passes through -``` diff --git a/docs/guide/commands/files.md b/docs/guide/commands/files.md deleted file mode 100644 index 53d652398..000000000 --- a/docs/guide/commands/files.md +++ /dev/null @@ -1,185 +0,0 @@ ---- -title: File System Commands -description: RTK filters for ls, read, grep, find, diff, and wc -sidebar: - order: 3 ---- - -# File System Commands - -RTK replaces common file and search commands with compact, token-optimized equivalents. - -## ls — 80% savings - -```bash -rtk ls [args...] -``` - -Replaces `ls` and `tree` with a compact directory tree. All native `ls` flags are supported (`-l`, `-a`, `-h`, `-R`, etc.). - -**Before (45 lines):** -``` -drwxr-xr-x 15 user staff 480 ... --rw-r--r-- 1 user staff 1234 ... -...40 more lines... -``` - -**After (12 lines):** -``` -my-project/ -+-- src/ (8 files) -| +-- main.rs -+-- Cargo.toml -+-- README.md -``` - -## read — up to 74% savings - -```bash -rtk read [options] -rtk read - # read from stdin -``` - -Replaces `cat`, `head`, `tail` with intelligent content filtering. - -| Option | Short | Default | Description | -|--------|-------|---------|-------------| -| `--level` | `-l` | `minimal` | Filtering level: `none`, `minimal`, `aggressive` | -| `--max-lines` | `-m` | unlimited | Maximum number of lines | -| `--line-numbers` | `-n` | off | Show line numbers | - -**Filtering levels:** - -| Level | Description | Savings | -|-------|-------------|---------| -| `none` | Raw output, no filtering | 0% | -| `minimal` | Removes excessive blank lines and comments | ~30% | -| `aggressive` | Signatures only — removes function bodies | ~74% | - -**Before — `cat main.rs` (~200 lines):** -```rust -fn main() -> Result<()> { - let config = Config::load()?; - let data = process_data(&input); - for item in data { - println!("{}", item); - } - Ok(()) -} -... -``` - -**After — `rtk read main.rs -l aggressive` (~50 lines):** -```rust -fn main() -> Result<()> { ... } -fn process_data(input: &str) -> Vec { ... } -struct Config { ... } -impl Config { fn load() -> Result { ... } } -``` - -Supported languages for filtering: Rust, Python, JavaScript, TypeScript, Go, C, C++, Java, Ruby, Shell. - -## grep — 80% savings - -```bash -rtk grep [path] [options] -``` - -Replaces `grep` and `rg` with results grouped by file and truncated. - -| Option | Short | Default | Description | -|--------|-------|---------|-------------| -| `--max-len` | `-l` | 80 | Maximum line length | -| `--max` | `-m` | 50 | Maximum number of results | -| `--context-only` | `-c` | off | Show only match context | -| `--file-type` | `-t` | all | Filter by type (ts, py, rust, etc.) | - -Additional arguments are passed to `rg` (ripgrep). - -**Before (20 lines):** -``` -src/git.rs:45:pub fn run(...) -src/git.rs:120:fn run_status(...) -src/ls.rs:12:pub fn run(...) -src/ls.rs:25:fn run_tree(...) -``` - -**After (10 lines):** -``` -src/git.rs - 45: pub fn run(...) - 120: fn run_status(...) -src/ls.rs - 12: pub fn run(...) - 25: fn run_tree(...) -``` - -## find — 80% savings - -```bash -rtk find [args...] -``` - -Replaces `find` and `fd` with results grouped by directory. Both RTK syntax and native `find` flags (`-name`, `-type`, etc.) are supported. - -**Before (30 lines):** -``` -./src/main.rs -./src/git.rs -./src/config.rs -./src/tracking.rs -./src/filter.rs -./src/utils.rs -...24 more lines... -``` - -**After (8 lines):** -``` -src/ (12 .rs) - main.rs, git.rs, config.rs - tracking.rs, filter.rs, utils.rs - ...6 more -tests/ (3 .rs) - test_git.rs, test_ls.rs, test_filter.rs -``` - -## tree - -```bash -rtk tree [args...] -``` - -Proxy to native `tree` with filtered output. All native flags supported (`-L`, `-d`, `-a`, etc.). - -**Savings:** ~80% - -## diff — 60% savings - -```bash -rtk diff -rtk diff # stdin as second file -``` - -Ultra-compact diff showing only changed lines. - -## wc - -```bash -rtk wc [args...] -``` - -Replaces `wc` with compact output (removes paths and padding). All native flags supported (`-l`, `-w`, `-c`, etc.). - -## smart — 95% savings - -```bash -rtk smart -``` - -Generates a 2-line technical summary of a source file using heuristics. - -```bash -$ rtk smart src/tracking.rs -SQLite-based token tracking system for command executions. -Records input/output tokens, savings %, execution times with 90-day retention. -``` diff --git a/docs/guide/commands/git.md b/docs/guide/commands/git.md deleted file mode 100644 index dc1b37a1d..000000000 --- a/docs/guide/commands/git.md +++ /dev/null @@ -1,221 +0,0 @@ ---- -title: Git Commands -description: RTK filters for git and GitHub CLI — compact status, log, diff, and more -sidebar: - order: 1 ---- - -# Git Commands - -RTK supports all git subcommands. Unrecognized subcommands pass through to git unchanged. - -## git status — 80% savings - -```bash -rtk git status [args...] -``` - -Replaces the verbose multi-section output with a compact one-line summary. - -**Before (20 lines):** -``` -On branch main -Your branch is up to date with 'origin/main'. - -Changes not staged for commit: - (use "git add ..." to update what will be committed) - modified: src/main.rs - modified: src/git.rs - -Untracked files: - (use "git add ..." to include in what will be committed) - new_file.txt -... -``` - -**After (5 lines):** -``` -main | 3M 1? 1A -M src/main.rs -M src/git.rs -? new_file.txt -A staged_file.rs -``` - -## git log — 80% savings - -```bash -rtk git log [args...] # supports --oneline, --graph, --all, -n, etc. -``` - -Shows hash + subject only, one line per commit. - -**Before (50+ lines for 5 commits):** -``` -commit abc123def... (HEAD -> main) -Author: User -Date: Mon Jan 15 10:30:00 2024 - - Fix token counting bug - -commit def456... -... -``` - -**After (5 lines):** -``` -abc123 Fix token counting bug -def456 Add vitest support -789abc Refactor filter engine -012def Update README -345ghi Initial commit -``` - -## git diff — 75% savings - -```bash -rtk git diff [args...] # supports --stat, --cached, --staged, etc. -``` - -Shows changed files with line counts and condensed hunks. - -**Before (~100 lines):** -``` -diff --git a/src/main.rs b/src/main.rs -index abc123..def456 100644 ---- a/src/main.rs -+++ b/src/main.rs -@@ -10,6 +10,8 @@ - fn main() { -+ let config = Config::load()?; -+ config.validate()?; -...30 lines of headers and context... -``` - -**After (~25 lines):** -``` -src/main.rs (+5/-2) - + let config = Config::load()?; - + config.validate()?; - - // old code - - let x = 42; -src/git.rs (+1/-1) - ~ format!("ok {}", branch) -``` - -## git show — 80% savings - -```bash -rtk git show [args...] -``` - -Shows commit summary + stat + compact diff. - -## git add — 92% savings - -```bash -rtk git add [args...] # supports -A, -p, --all, etc. -``` - -Output: `ok` (single word). - -## git commit — 92% savings - -```bash -rtk git commit -m "message" [args...] # supports -a, --amend, --allow-empty, etc. -``` - -Output: `ok abc1234` (confirmation + short hash). - -## git push — 92% savings - -```bash -rtk git push [args...] # supports -u, remote, branch, etc. -``` - -**Before (15 lines):** -``` -Enumerating objects: 5, done. -Counting objects: 100% (5/5), done. -Delta compression using up to 8 threads -... -``` - -**After (1 line):** -``` -ok main -``` - -## git pull — 92% savings - -```bash -rtk git pull [args...] -``` - -Output: `ok 3 files +10 -2` - -## git branch - -```bash -rtk git branch [args...] # supports -d, -D, -m, etc. -``` - -Shows current branch, local branches, and remote branches in compact form. - -## git fetch - -```bash -rtk git fetch [args...] -``` - -Output: `ok fetched (N new refs)` - -## git stash - -```bash -rtk git stash [list|show|pop|apply|drop|push] [args...] -``` - -## git worktree - -```bash -rtk git worktree [add|remove|prune|list] [args...] -``` - -## Passthrough - -Any git subcommand without a specific RTK filter runs unchanged: - -```bash -rtk git rebase main # runs git rebase main -rtk git cherry-pick abc # runs git cherry-pick abc -rtk git tag v1.0.0 # runs git tag v1.0.0 -``` - -## GitHub CLI - -```bash -rtk gh pr list -rtk gh pr view # 87% savings -rtk gh pr checks # 79% savings -rtk gh issue list -rtk gh run list # 82% savings -rtk gh api -``` - -**Before (30 lines for pr list):** -``` -Showing 10 of 15 pull requests in org/repo - -#42 feat: add vitest support - user opened about 2 days ago - labels: enhancement -... -``` - -**After (10 lines):** -``` -#42 feat: add vitest (open, 2d) -#41 fix: git diff crash (open, 3d) -#40 chore: update deps (merged, 5d) -``` diff --git a/docs/guide/commands/github-cli.md b/docs/guide/commands/github-cli.md deleted file mode 100644 index 5367942f8..000000000 --- a/docs/guide/commands/github-cli.md +++ /dev/null @@ -1,94 +0,0 @@ ---- -title: GitHub CLI -description: RTK filters for gh — pull requests, issues, checks, and workflow runs -sidebar: - order: 10 ---- - -# GitHub CLI - -RTK filters `gh` output by removing ASCII art, verbose metadata, and decorative formatting, keeping the information that matters. - -## pr list — 80% savings - -```bash -rtk gh pr list [args...] -``` - -**Before (~30 lines):** -``` -Showing 3 of 3 pull requests in org/repo - -#42 feat: add vitest support - user1 opened about 2 days ago - Labels: enhancement - Review: 0 approving, 0 requesting changes - -#41 fix: git diff crash - user2 opened about 3 days ago - ... -``` - -**After (~6 lines):** -``` -#42 feat: add vitest (open, 2d) -#41 fix: git diff crash (open, 3d) -#40 chore: update deps (merged, 5d) -``` - -## pr view — 87% savings - -```bash -rtk gh pr view [args...] -``` - -Compact PR summary: title, status, author, description excerpt, and CI checks in one block. - -## pr checks — 79% savings - -```bash -rtk gh pr checks [args...] -``` - -Shows check name + status only, strips URLs and timestamps. - -## issue list — 80% savings - -```bash -rtk gh issue list [args...] -``` - -Same compact format as pr list. - -## run list — 82% savings - -```bash -rtk gh run list [args...] -``` - -Workflow run name + status + duration, one line each. - -## api - -```bash -rtk gh api [args...] -``` - -~26% savings — strips HTTP headers and formats JSON output. - -## Stacked PRs (Graphite) - -```bash -rtk gt log # compact stack log -rtk gt submit # compact submit output -rtk gt sync # compact sync -rtk gt restack # compact restack -rtk gt create # compact create -rtk gt branch # compact branch info -``` - -Unrecognized `gt` subcommands pass through to Graphite or git. - -## Passthrough - -Other `gh` subcommands pass through to the GitHub CLI unchanged. diff --git a/docs/guide/commands/go.md b/docs/guide/commands/go.md deleted file mode 100644 index e0b9d8ce5..000000000 --- a/docs/guide/commands/go.md +++ /dev/null @@ -1,54 +0,0 @@ ---- -title: Go -description: RTK filters for go test and golangci-lint -sidebar: - order: 6 ---- - -# Go - -RTK covers Go's two most verbose outputs: test runs and the linter. - -## go test — 90% savings - -```bash -rtk go test [args...] -``` - -Parses Go's NDJSON streaming output for precise failure filtering. Shows failures only on failure, compact summary on success. - -Uses `go test -json` internally for reliable event-by-event parsing. - -## golangci-lint — 85% savings - -```bash -rtk golangci-lint run [args...] -``` - -Compressed JSON output grouped by linter and file. - -**Before (verbose lint output):** -``` -src/main.go:42:5: Error return value of `fmt.Fprintf` is not checked (errcheck) -src/main.go:67:1: exported function `ProcessData` should have comment (godot) -src/handler.go:15:9: G104: Errors unhandled. (gosec) -... -``` - -**After:** -``` -src/main.go (2 issues) - errcheck: Error return not checked (x1) - godot: Missing comment on exported func (x1) -src/handler.go (1 issue) - gosec/G104: Errors unhandled (x1) -``` - -## go build - -Unrecognized `go` subcommands pass through directly: - -```bash -rtk go build ./... # passes through to go build -rtk go vet ./... # passes through to go vet -``` diff --git a/docs/guide/commands/javascript.md b/docs/guide/commands/javascript.md deleted file mode 100644 index 6b27cdc43..000000000 --- a/docs/guide/commands/javascript.md +++ /dev/null @@ -1,129 +0,0 @@ ---- -title: JavaScript / TypeScript -description: RTK filters for pnpm, npm, vitest, tsc, ESLint, Next.js, Playwright, and Prisma -sidebar: - order: 4 ---- - -# JavaScript / TypeScript - -RTK covers the full JS/TS toolchain: package managers, test runners, type checking, linting, build tools, and ORM. - -## vitest run — 99.5% savings - -```bash -rtk vitest run [args...] -``` - -Shows failures only. One of the highest savings rates in RTK — vitest verbose output can be massive. - -## Playwright — 94% savings - -```bash -rtk playwright [args...] -``` - -Shows failures and summaries only, strips progress output. - -## tsc — 83% savings - -```bash -rtk tsc [args...] -``` - -Groups TypeScript errors by file and error code. - -**Before (50 lines for 4 errors):** -``` -src/api.ts(12,5): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. -src/api.ts(15,10): error TS2345: Argument of type 'string' is not assignable to parameter of type 'number'. -src/api.ts(20,3): error TS7006: Parameter 'x' implicitly has an 'any' type. -src/utils.ts(5,1): error TS2304: Cannot find name 'foo'. -``` - -**After (15 lines):** -``` -src/api.ts (3 errors) - TS2345: Argument type mismatch (x2) - TS7006: Parameter implicitly has 'any' -src/utils.ts (1 error) - TS2304: Cannot find name 'foo' -``` - -## ESLint / Biome — 84% savings - -```bash -rtk lint [args...] -rtk lint biome [args...] -``` - -Groups violations by rule and file. Auto-detects the linter. - -## prettier - -```bash -rtk prettier --check . # 70% savings -rtk prettier --write src/ -``` - -Shows only files that need formatting. - -## Next.js build — 87% savings - -```bash -rtk next [args...] -rtk pnpm build # delegates to Next.js filter -``` - -Compact output with route metrics. - -## pnpm - -| Command | Description | Savings | -|---------|-------------|---------| -| `rtk pnpm list [-d N]` | Compact dependency tree | ~70% | -| `rtk pnpm outdated` | Outdated packages: `pkg: old -> new` | ~80% | -| `rtk pnpm install [pkgs...]` | Filters progress bars | ~60% | -| `rtk pnpm build` | Delegates to Next.js filter | ~87% | -| `rtk pnpm typecheck` | Delegates to tsc filter | ~83% | - -Unrecognized subcommands pass through to pnpm directly. - -## npm - -```bash -rtk npm [args...] # e.g. rtk npm run build -``` - -Filters npm boilerplate (progress bars, headers, audit notices). - -## npx — smart routing - -```bash -rtk npx [args...] -``` - -Routes automatically to specialized filters: - -- `rtk npx tsc` → tsc filter -- `rtk npx eslint` → lint filter -- `rtk npx prisma` → prisma filter -- Other → passthrough filter - -## Prisma - -| Command | Description | -|---------|-------------| -| `rtk prisma generate` | Client generation (removes ASCII art) | -| `rtk prisma migrate dev [--name N]` | Create and apply a migration | -| `rtk prisma migrate status` | Migration status | -| `rtk prisma migrate deploy` | Deploy to production | -| `rtk prisma db-push` | Push schema | - -## Universal format detector - -```bash -rtk format [args...] -``` - -Auto-detects the project formatter (prettier, black, ruff format, rustfmt) and applies a unified compact filter. diff --git a/docs/guide/commands/python.md b/docs/guide/commands/python.md deleted file mode 100644 index 4444dcc26..000000000 --- a/docs/guide/commands/python.md +++ /dev/null @@ -1,55 +0,0 @@ ---- -title: Python -description: RTK filters for pytest, ruff, mypy, and pip -sidebar: - order: 5 ---- - -# Python - -RTK covers the core Python toolchain: testing, linting/formatting, type checking, and package management. - -## pytest — 90% savings - -```bash -rtk pytest [args...] -``` - -Shows failures only. On success, shows a compact summary. - -## ruff — 80% savings - -```bash -rtk ruff check [args...] -rtk ruff format --check [args...] -``` - -Compressed JSON output grouped by file and rule. - -## mypy — type errors grouped by file - -```bash -rtk mypy [args...] -``` - -Groups type errors by file and error code, similar to the tsc filter. - -## pip / uv - -```bash -rtk pip list # package list -rtk pip outdated # outdated packages -rtk pip install # installation -``` - -Auto-detects `uv` if available and uses it instead of pip. - -## deps — project overview - -```bash -rtk deps [path] # default: current directory -``` - -Compact summary of project dependencies. Auto-detects `Cargo.toml`, `package.json`, `pyproject.toml`, `go.mod`, `Gemfile`, and others. - -**Savings:** ~70% diff --git a/docs/guide/commands/ruby.md b/docs/guide/commands/ruby.md deleted file mode 100644 index 5e1a4ed28..000000000 --- a/docs/guide/commands/ruby.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -title: Ruby -description: RTK filters for rake, rspec, and rubocop -sidebar: - order: 7 ---- - -# Ruby - -RTK covers Ruby's core development tools: build tasks, test output, and linting. - -## rspec — 60-90% savings - -```bash -rtk rspec [args...] -``` - -Shows failures only. On success, compact summary. - -## rubocop — 80%+ savings - -```bash -rtk rubocop [args...] -``` - -Groups violations by cop and file. - -## rake — 60-80% savings - -```bash -rtk rake [args...] -rtk rake test -rtk rake spec -``` - -Filters task execution noise, keeps errors and final result. - -## Passthrough - -Unrecognized rake tasks pass through to rake directly: - -```bash -rtk rake db:migrate # passes through unchanged -rtk rake assets:precompile -``` diff --git a/docs/guide/commands/utilities.md b/docs/guide/commands/utilities.md deleted file mode 100644 index 02ff3dc31..000000000 --- a/docs/guide/commands/utilities.md +++ /dev/null @@ -1,66 +0,0 @@ ---- -title: Utilities -description: RTK utility commands — proxy passthrough, analytics, global flags -sidebar: - order: 12 ---- - -# Utilities - -Utility commands that apply across all ecosystems. - -## proxy — passthrough with tracking - -```bash -rtk proxy -``` - -Runs any command without filtering but records it in the token savings database. Useful when you need raw output but still want usage tracked. - -```bash -rtk proxy git log --oneline -20 # full git log, no filtering -rtk proxy npm install express # raw npm output -rtk proxy curl https://api.example.com/data -``` - -All proxy commands appear in `rtk gain --history` with 0% savings (input = output). - -## Global flags - -These flags apply to every RTK command: - -| Flag | Description | -|------|-------------| -| `-v` | Debug messages | -| `-vv` | Show command being executed | -| `-vvv` | Show raw output before filtering | -| `-u` / `--ultra-compact` | Maximum compression (ASCII icons, inline format) | - -**Verbosity example:** -```bash -rtk git status -vvv -# shows the raw git status output before RTK filters it -``` - -**Ultra-compact example:** -```bash -rtk git push -u -# output: ✓ main (vs "ok main" in normal mode) -``` - -## RTK_DISABLED — per-command override - -```bash -RTK_DISABLED=1 git status # runs raw git status, no rewrite -``` - -## Passthrough behavior - -Any command RTK doesn't recognize is executed unchanged and the output passes through: - -```bash -rtk make install # runs make install verbatim -rtk terraform plan # runs terraform plan verbatim (unless a TOML filter matches) -``` - -The exit code from the underlying command is always preserved. diff --git a/docs/guide/filters/creating-filters.md b/docs/guide/filters/creating-filters.md deleted file mode 100644 index 2203d3a8d..000000000 --- a/docs/guide/filters/creating-filters.md +++ /dev/null @@ -1,105 +0,0 @@ ---- -title: Creating Filters -description: Write TOML filters to compress output from any command RTK doesn't cover yet -sidebar: - order: 2 ---- - -# Creating Filters - -TOML filters let you add RTK support for any command without writing Rust. They work well for tools with predictable, line-by-line text output where regex filtering achieves 60%+ savings. - -## When to use a TOML filter - -TOML works well for: -- Install/update logs (brew, composer, poetry) — strip `Using ...` / `Already installed` lines -- System monitoring (df, ps, systemctl) — keep essential rows, drop headers -- Simple linters (shellcheck, yamllint, hadolint) — strip context, keep findings -- Infra tools (terraform plan, helm, rsync) — strip progress, keep summary - -For commands with structured JSON output, state machine parsing, or complex routing, a Rust module is needed instead. - -## Create a project-local filter - -Create `.rtk/filters.toml` in your project root: - -```toml -[filters.my-tool] -description = "Strip progress noise from my-tool output" -match_command = "^my-tool\\b" -strip_ansi = true -strip_lines_matching = [ - "^Loading", - "^Scanning", - "^\\s*$", -] -max_lines = 50 -on_empty = "my-tool: nothing to report" - -[[tests.my-tool]] -name = "strips progress lines" -input = "Loading plugins...\nScan complete: 3 issues\nWarning: foo at line 42" -expected = "Scan complete: 3 issues\nWarning: foo at line 42" -``` - -Verify: - -```bash -rtk verify -``` - -## Filter fields reference - -| Field | Type | Description | -|-------|------|-------------| -| `description` | string | Human-readable description | -| `match_command` | regex | Matched against full command string | -| `strip_ansi` | bool | Strip ANSI escape codes first | -| `strip_lines_matching` | regex[] | Drop lines matching any of these | -| `keep_lines_matching` | regex[] | Keep only lines matching at least one | -| `replace` | array | Regex substitutions (`{ pattern, replacement }`) | -| `match_output` | array | Short-circuit rules (`{ pattern, message }`) | -| `truncate_lines_at` | int | Truncate lines longer than N chars | -| `max_lines` | int | Keep only the first N lines | -| `tail_lines` | int | Keep only the last N lines | -| `on_empty` | string | Message when output is empty after filtering | - -## Naming convention - -Use the command name as the filter key: `terraform-plan`, `docker-inspect`, `mix-compile`. For commands with subcommands, prefer `cmd-subcommand` over grouping multiple filters together. - -## Filter lookup order - -Project filters override user-global filters, which override built-ins: - -``` -1. .rtk/filters.toml (project-local — highest priority) -2. ~/.config/rtk/filters.toml (user-global) -3. BUILTIN_TOML (embedded in binary — lowest priority) -``` - -A project filter with the same key as a built-in shadows the built-in with a warning: - -``` -[rtk] warning: filter 'make' is shadowing a built-in filter -``` - -## Writing inline tests - -Always add at least one `[[tests.my-tool]]` entry. Tests run via `rtk verify` and also during `cargo test` (for built-in filters). - -```toml -[[tests.my-tool]] -name = "normal output" -input = "Progress: 10%\nDone. 3 findings." -expected = "Done. 3 findings." - -[[tests.my-tool]] -name = "empty output" -input = "Progress: 10%\nProgress: 100%" -expected = "my-tool: nothing to report" -``` - -## Contributing a filter upstream - -To add a filter to RTK's built-in set, see the [Contributing guide](../../reference/contributing/guide.md) for the full checklist (register in `discover/rules.rs`, update filter count in tests, write fixture). diff --git a/docs/guide/filters/using-filters.md b/docs/guide/filters/using-filters.md deleted file mode 100644 index a23bd80d9..000000000 --- a/docs/guide/filters/using-filters.md +++ /dev/null @@ -1,121 +0,0 @@ ---- -title: Using Filters -description: How RTK filters work, the lookup priority, and how to create project-local filters -sidebar: - order: 1 ---- - -# Using Filters - -RTK filters are TOML files that define how a command's output should be compressed. They are the core mechanism behind every `rtk ` savings number. - -## Filter lookup priority - -When you run `rtk my-tool args`, RTK checks three locations in order: - -``` -1. .rtk/filters.toml (project-local) -2. ~/.config/rtk/filters.toml (user-global) - macOS alt: ~/Library/Application Support/rtk/filters.toml -3. BUILTIN_TOML (embedded in binary) -``` - -First match wins. A project filter with the same name as a built-in shadows the built-in and triggers a warning: - -``` -[rtk] warning: filter 'make' is shadowing a built-in filter -``` - -## The 8-stage filter pipeline - -Every matched filter runs output through this pipeline: - -``` -strip_ansi - -> replace - -> match_output (short-circuit: if pattern matches, emit message and stop) - -> strip/keep_lines - -> truncate_lines_at - -> tail_lines - -> max_lines - -> on_empty (if output is empty after all stages, emit this message) -``` - -Stages you don't configure are skipped. - -## Creating a project-local filter - -Create `.rtk/filters.toml` in your project root: - -```toml -[[filters]] -name = "my-tool" -match_command = "^my-tool\\b" - -strip_lines = [ - "^Loading", - "^Scanning", -] - -max_lines = 50 -on_empty = "my-tool: nothing to do" - -[[tests.my-tool]] -input = "Loading plugins...\nScan complete: 3 issues\nWarning: foo at line 42" -expected = "Scan complete: 3 issues\nWarning: foo at line 42" -``` - -Verify it works: - -```bash -rtk verify -``` - -## Filter fields reference - -| Field | Type | Description | -|-------|------|-------------| -| `name` | string | Unique identifier (used in logs and `rtk verify`) | -| `match_command` | regex | Pattern matched against the full command string | -| `strip_lines` | regex[] | Remove lines matching any of these patterns | -| `keep_lines` | regex[] | Keep only lines matching any of these patterns | -| `replace` | `[[from, to]]` | String replacements applied before line filtering | -| `match_output` | regex | If matched, emit `on_match` and stop pipeline | -| `on_match` | string | Message emitted when `match_output` matches | -| `truncate_lines_at` | number | Truncate lines longer than N characters | -| `tail_lines` | number | Keep only the last N lines | -| `max_lines` | number | Keep only the first N lines after all other stages | -| `on_empty` | string | Message emitted when pipeline produces empty output | - -## How built-in filters are compiled - -Built-in filters live in `src/filters/*.toml` in the RTK source. At build time, `build.rs` concatenates all TOML files alphabetically and embeds the result in the binary as a constant. No external files are needed at runtime. - -This means: -- Built-in filters have zero filesystem overhead -- Project filters override built-ins by name -- New built-in filters require a new RTK release - -## Verifying filters - -```bash -rtk verify -``` - -Runs every `[[tests.*]]` entry in all filter files and reports pass/fail. Use this to validate your custom filters before committing. - -## Flow diagram - -```mermaid -flowchart LR - CMD["rtk my-tool args"] --> P1 - P1{"1. .rtk/filters.toml\n(project-local)"} - P1 -->|"match"| WIN["apply filter"] - P1 -->|"no match"| P2 - P2{"2. ~/.config/rtk/filters.toml\n(user-global)"} - P2 -->|"match"| WIN - P2 -->|"no match"| P3 - P3{"3. BUILTIN_TOML\n(binary)"} - P3 -->|"match"| WIN - P3 -->|"no match"| P4[["exec raw\n(passthrough)"]] -``` diff --git a/docs/guide/what-rtk-covers.md b/docs/guide/what-rtk-covers.md new file mode 100644 index 000000000..3e3f2a46a --- /dev/null +++ b/docs/guide/what-rtk-covers.md @@ -0,0 +1,133 @@ +--- +title: What RTK Optimizes +description: Commands and ecosystems automatically optimized by RTK with typical token savings +sidebar: + order: 2 +--- + +# 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. + +60+ commands across 9 ecosystems. 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 | +|---------|---------|--------------| +| `vitest run` | 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 | + +## 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 | + +## 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`. diff --git a/docs/reference/contributing/coding-standards.md b/docs/reference/contributing/coding-standards.md deleted file mode 100644 index 0670d563c..000000000 --- a/docs/reference/contributing/coding-standards.md +++ /dev/null @@ -1,128 +0,0 @@ ---- -title: Coding Standards -description: Rust patterns, error handling rules, and anti-patterns for RTK development -sidebar: - order: 3 ---- - -# Coding Standards - -RTK-specific Rust constraints. These override general Rust conventions. - -## Non-negotiable rules - -1. **No async** — Zero `tokio`, `async-std`, `futures`. Single-threaded by design. Async adds 5-10ms startup. -2. **No `.unwrap()` in production** — Use `.context("description")?`. Tests: use `.expect("reason")`. -3. **Lazy regex** — `Regex::new()` inside a function recompiles on every call. Always `lazy_static!`. -4. **Fallback pattern** — If filter fails, execute raw command unchanged. Never block the user. -5. **Exit code propagation** — `std::process::exit(code)` if underlying command fails. - -## Error handling - -```rust -use anyhow::{Context, Result}; - -// ✅ Correct -fn read_config(path: &Path) -> Result { - let content = fs::read_to_string(path) - .with_context(|| format!("Failed to read config: {}", path.display()))?; - toml::from_str(&content).context("Failed to parse config TOML") -} - -// ❌ Wrong — no context -fn read_config(path: &Path) -> Result { - let content = fs::read_to_string(path)?; - Ok(toml::from_str(&content)?) -} -``` - -## Fallback pattern (mandatory for all filters) - -```rust -pub fn run(args: MyArgs) -> Result<()> { - let output = execute_command("mycmd", &args.to_cmd_args()) - .context("Failed to execute mycmd")?; - - let filtered = filter_output(&output.stdout) - .unwrap_or_else(|e| { - eprintln!("rtk: filter warning: {}", e); - output.stdout.clone() // passthrough on failure - }); - - tracking::record("mycmd", &output.stdout, &filtered)?; - print!("{}", filtered); - - if !output.status.success() { - std::process::exit(output.status.code().unwrap_or(1)); - } - Ok(()) -} -``` - -## Regex — always lazy_static - -```rust -use lazy_static::lazy_static; -use regex::Regex; - -lazy_static! { - static ref ERROR_RE: Regex = Regex::new(r"^error\[").unwrap(); - static ref HASH_RE: Regex = Regex::new(r"^[0-9a-f]{7,40}").unwrap(); -} - -// ✅ Compiled once at first use -fn is_error_line(line: &str) -> bool { - ERROR_RE.is_match(line) -} - -// ❌ Recompiles on every call -fn is_error_line(line: &str) -> bool { - let re = Regex::new(r"^error\[").unwrap(); - re.is_match(line) -} -``` - -Note: `lazy_static!` with `.unwrap()` for initialization is the established RTK pattern — acceptable because a bad regex literal is a programming error caught at first use. - -## Module structure - -Every `*_cmd.rs` follows this pattern: - -```rust -// 1. Imports -use anyhow::{Context, Result}; -use lazy_static::lazy_static; -use regex::Regex; - -// 2. Args struct -pub struct MyArgs { ... } - -// 3. Lazy regexes -lazy_static! { static ref MY_RE: Regex = ...; } - -// 4. Public entry point -pub fn run(args: MyArgs) -> Result<()> { ... } - -// 5. Private filter functions -fn filter_output(input: &str) -> Result { ... } - -// 6. Tests (always present) -#[cfg(test)] -mod tests { - use super::*; - fn count_tokens(s: &str) -> usize { s.split_whitespace().count() } - // snapshot tests, savings tests... -} -``` - -## Anti-patterns - -| Pattern | Problem | Fix | -|---------|---------|-----| -| `Regex::new()` in function | Recompiles every call | `lazy_static!` | -| `.unwrap()` in production | Panic breaks user workflow | `.context()?` | -| `tokio::main` or `async fn` | +5-10ms startup | Blocking I/O only | -| `Err(_) => {}` | User gets no output | Log warning + fallback | -| `println!` in filter path | Debug artifact in output | Use `eprintln!` | -| Early return without exit code | CI thinks command succeeded | `std::process::exit(code)` | -| `.clone()` of large strings in hot path | Extra allocation | Borrow with `&str` | diff --git a/docs/reference/contributing/guide.md b/docs/reference/contributing/guide.md deleted file mode 100644 index e37a6a48c..000000000 --- a/docs/reference/contributing/guide.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Contributing Guide -description: How to contribute to RTK — design philosophy, PR process, testing, and documentation -sidebar: - order: 1 ---- - -# Contributing Guide - -## Design philosophy - -Four principles guide every RTK design decision. - -### Correctness over savings - -When a user explicitly requests detailed output via flags (e.g., `--nocapture`, `--verbose`, `-la`), respect that intent. Filters should be flag-aware: default output gets compressed, explicit verbose flags pass through more content. - -> `rtk cargo test` shows failures only (90% savings). `rtk cargo test -- --nocapture` preserves all output. - -### Transparency - -RTK's output must be a valid, useful subset of the original tool's output. The filtered version should be indistinguishable from "a shorter version of the real command." Don't invent new formats or add RTK-specific headers in default output. - -### Never block - -If a filter fails, fall back to raw output. RTK must never prevent a command from executing. Every filter needs a fallback path. Every hook must handle malformed input gracefully and exit 0. - -### Zero overhead - -`<10ms` startup. No async runtime. No config file I/O on the critical path. Use `lazy_static!` for all regex. No network calls in the hot path. Benchmark before/after with `hyperfine`. - -## What belongs in RTK? - -**In scope:** Commands that produce text output (typically 100+ tokens) compressible 60%+ without losing essential information for the LLM. - -- Test runners (vitest, pytest, cargo test, go test) -- Linters and type checkers (eslint, ruff, tsc, mypy) -- Build tools (cargo build, dotnet build, make, next build) -- VCS operations (git status/log/diff, gh pr/issue) -- Package managers (pnpm, pip, cargo install) -- File operations (ls, tree, grep, find) -- Infrastructure tools with text output (docker, kubectl, terraform) - -**Out of scope:** Interactive TUIs, binary output, trivial commands, non-text output. - -## TOML filter vs Rust module - -| Use **TOML filter** when | Use **Rust module** when | -|--------------------------|--------------------------| -| Plain text with predictable line structure | Structured output (JSON, NDJSON) | -| Regex line filtering achieves 60%+ savings | State machine parsing needed | -| No CLI flag injection needed | Must inject flags like `--format json` | -| No cross-command routing | Routes to other commands | - -## Branch naming - -| Prefix | Semver | When to use | -|--------|--------|-------------| -| `fix/` | Patch | Bug fixes, filter corrections | -| `feat/` | Minor | New filters, new command support | -| `chore/` | Major | Breaking changes, API changes | - -Examples: `fix/git-log-drops-merge-commits`, `feat/kubectl-pod-filter` - -## PR process - -1. Branch from `develop` -2. Make changes + add tests + update docs -3. Run pre-commit gate: `cargo fmt --all --check && cargo clippy --all-targets && cargo test` -4. Open PR targeting `develop` -5. Sign CLA (CLA Assistant will prompt on first PR) -6. Address review feedback -7. Maintainer merges to `develop` → eventual release to `master` - -## Testing requirements - -Every PR must include tests. Follow TDD (Red-Green-Refactor): write failing test first. - -| Type | Location | Runner | -|------|----------|--------| -| Unit tests | `#[cfg(test)] mod tests` in each module | `cargo test` | -| Snapshot tests | `assert_snapshot!()` via `insta` | `cargo test` + `cargo insta review` | -| Smoke tests | `scripts/test-all.sh` | `bash scripts/test-all.sh` | -| Integration tests | `#[ignore]` tests | `cargo test --ignored` | - -**PR testing checklist:** -- [ ] Unit tests added/updated -- [ ] Snapshot tests reviewed (`cargo insta review`) -- [ ] Token savings ≥60% verified -- [ ] `cargo fmt --all --check && cargo clippy --all-targets && cargo test` passes - -## Documentation requirements - -Every filter addition requires: -- Update `docs/guide/commands/.md` with the new command -- Update `CHANGELOG.md` under `[Unreleased]` - -For the full step-by-step checklist for adding a new command filter, see [src/cmds/README.md](https://github.com/rtk-ai/rtk/blob/master/src/cmds/README.md#adding-a-new-command-filter). - -## Contributor License Agreement - -All contributions require signing the CLA. CLA Assistant will post a comment on your first PR with a link to sign. You only need to sign once. diff --git a/docs/reference/contributing/security.md b/docs/reference/contributing/security.md deleted file mode 100644 index 2b7299e22..000000000 --- a/docs/reference/contributing/security.md +++ /dev/null @@ -1,83 +0,0 @@ ---- -title: Security Policy -description: How to report vulnerabilities, the PR security review process, and dangerous patterns to avoid -sidebar: - order: 2 ---- - -# Security Policy - -## Reporting a vulnerability - -Report security issues privately — do not open public GitHub issues for vulnerabilities. - -- **Email**: security@rtk-ai.dev (or create a private GitHub security advisory) -- **Response time**: Acknowledgment within 48 hours -- **Disclosure**: 90-day embargo, responsible disclosure - -## Automated security checks - -Every PR triggers `security-check.yml`: - -1. **Dependency audit** (`cargo audit`) — detects known CVEs -2. **Critical files alert** — flags modifications to high-risk files -3. **Dangerous pattern scan** — regex detection of shell execution, env manipulation, network ops, unsafe code, `.unwrap()` in production -4. **Clippy security lints** - -## High-risk files - -These files require enhanced review (2 maintainers for Tier 1): - -**Tier 1 — Shell execution & system interaction:** -- `src/runner.rs` — shell command execution engine -- `src/tracking.rs` — SQLite database operations -- `src/discover/registry.rs` — command rewrite logic -- `hooks/rtk-rewrite.sh` — intercepts all Claude Code commands - -**Tier 2 — Input validation:** -- `src/pnpm_cmd.rs` — package name validation -- `src/container.rs` — Docker/container operations - -**Tier 3 — Supply chain & CI/CD:** -- `Cargo.toml` — dependency manifest -- `.github/workflows/*.yml` — CI/CD pipelines - -## Dangerous patterns - -| Pattern | Risk | -|---------|------| -| `Command::new("sh")` | Shell injection | -| `.env("LD_PRELOAD")` | Library hijacking | -| `reqwest::`, `std::net::` | Data exfiltration | -| `unsafe {` | Memory safety bypass | -| `.unwrap()` in `src/` | DoS via panic | -| `SystemTime::now() > ...` | Logic bombs | - -**Avoid — shell injection:** -```rust -// ❌ Never do this -Command::new("sh").arg("-c").arg(format!("echo {}", user_input)).output(); - -// ✅ Direct binary execution -Command::new("echo").arg(user_input).output(); -``` - -## Dependency criteria for new crates - -- Downloads: >10,000 on crates.io -- Maintainer: verified GitHub profile + track record -- License: MIT or Apache-2.0 -- Activity: commits within 6 months -- No typosquatting (verify against similar crate names) - -## Disclosure timeline - -| Day | Action | -|-----|--------| -| 0 | Acknowledgment to reporter | -| 7 | Severity assessment | -| 14 | Patch development | -| 30 | Patch released + CVE filed (if applicable) | -| 90 | Public disclosure | - -Critical vulnerabilities (RCE, data exfiltration) may be fast-tracked. diff --git a/docs/reference/contributing/testing.md b/docs/reference/contributing/testing.md deleted file mode 100644 index 54054f334..000000000 --- a/docs/reference/contributing/testing.md +++ /dev/null @@ -1,142 +0,0 @@ ---- -title: Testing Strategy -description: Snapshot tests, token accuracy tests, cross-platform tests, and performance benchmarks -sidebar: - order: 4 ---- - -# Testing Strategy - -## Snapshot tests (critical) - -Use the `insta` crate for output validation. This is the primary testing strategy for RTK filters. - -```rust -use insta::assert_snapshot; - -#[test] -fn test_git_log_output() { - let input = include_str!("../tests/fixtures/git_log_raw.txt"); - let output = filter_git_log(input); - assert_snapshot!(output); -} -``` - -**Workflow:** -1. Write test with `assert_snapshot!(output)` -2. `cargo test` (creates new snapshot on first run) -3. `cargo insta review` (interactive review — press `a` to accept) -4. Snapshot saved in `src/cmds//snapshots/` - -## Token accuracy tests (critical) - -All filters must verify 60-90% savings claims with real fixtures. - -```rust -fn count_tokens(text: &str) -> usize { - text.split_whitespace().count() -} - -#[test] -fn test_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); -} -``` - -**Savings targets:** - -| Filter | Minimum | Mechanism | -|--------|---------|-----------| -| `git log` | 80% | Condense commits to hash + message | -| `cargo test` | 90% | Show failures only | -| `gh pr view` | 87% | Remove ASCII art + verbose metadata | -| `pnpm list` | 70% | Compact dependency tree | -| `docker ps` | 60% | Essential fields only | - -## Creating fixtures - -Use real command output, not synthetic data: - -```bash -git log -20 > tests/fixtures/git_log_raw.txt -cargo test 2>&1 > tests/fixtures/cargo_test_raw.txt -gh pr view 123 > tests/fixtures/gh_pr_view_raw.txt -``` - -## Cross-platform tests - -RTK must work on macOS (zsh), Linux (bash), and Windows (PowerShell). Test shell escaping with `cfg` guards: - -```rust -#[test] -fn test_shell_escaping() { - let escaped = escape_for_shell("test"); - #[cfg(target_os = "windows")] - assert_eq!(escaped, "\"test\""); - #[cfg(not(target_os = "windows"))] - assert_eq!(escaped, "test"); -} -``` - -## Performance tests - -RTK targets `<10ms` startup and `<5MB` memory. - -```bash -# Benchmark before/after changes -hyperfine 'rtk git log -10' --warmup 3 - -# Memory usage (macOS) -/usr/bin/time -l rtk git status -# "maximum resident set size" should be <5MB -``` - -## Integration tests - -Run against an installed binary with `#[ignore]`: - -```rust -#[test] -#[ignore] -fn test_real_git_log() { - let output = std::process::Command::new("rtk") - .args(&["git", "log", "-10"]) - .output() - .expect("Failed to run rtk"); - assert!(output.status.success()); - let stdout = String::from_utf8_lossy(&output.stdout); - assert!(stdout.len() < 5000, "Output too large, filter not working"); -} -``` - -Run with: `cargo test --ignored` - -## Test organization - -``` -src/cmds// - .rs # filter + embedded unit tests - snapshots/ # insta snapshots (auto-generated) -tests/ - common/mod.rs # count_tokens + shared helpers - fixtures/ # real command output (txt files) - integration_test.rs # #[ignore] end-to-end tests -``` - -## Pre-commit gate - -All three must pass before any commit: - -```bash -cargo fmt --all --check && cargo clippy --all-targets && cargo test -``` - -## Anti-patterns - -- **Don't** test with hardcoded synthetic strings — use real fixture files -- **Don't** skip cross-platform tests — use `cfg` guards -- **Don't** ignore savings drops below 60% — investigate and fix -- **Don't** commit without running `cargo insta review` diff --git a/docs/reference/index.md b/docs/reference/index.md deleted file mode 100644 index 4f8567b5d..000000000 --- a/docs/reference/index.md +++ /dev/null @@ -1,16 +0,0 @@ ---- -title: Reference -description: Technical reference for RTK contributors, maintainers, and integrators -sidebar: - order: 1 ---- - -# Reference - -Technical documentation for people working on RTK internals or integrating it into their toolchain. - -## What's in this section - -- **Contributing** — How to contribute, coding standards, testing strategy, and security policy -- **Internals** — Command routing, filter pipeline, tracking system, and hook engine -- **TOML DSL** — Specification for custom filter files diff --git a/docs/reference/internals/command-routing.md b/docs/reference/internals/command-routing.md deleted file mode 100644 index 3656880f8..000000000 --- a/docs/reference/internals/command-routing.md +++ /dev/null @@ -1,130 +0,0 @@ ---- -title: Command Routing -description: How RTK parses, routes, and dispatches commands through the 6-phase execution lifecycle -sidebar: - order: 1 ---- - -# Command Routing - -## 6-phase execution lifecycle - -Every RTK command goes through six phases: - -### Phase 1: Parse - -Clap parses the CLI arguments into a typed `Commands` enum: - -``` -$ rtk git log --oneline -5 -v - -Commands::Git { args: ["log", "--oneline", "-5"], verbose: 1 } -``` - -### Phase 2: Route - -`main.rs` matches the enum variant and dispatches to the module: - -```rust -match cli.command { - Commands::Git { args, .. } => git::run(&args, verbose)?, - Commands::Cargo { args, .. } => cargo::run(&args, verbose)?, - Commands::Ls { args } => ls_cmd::run(&args)?, - // ... -} -``` - -### Phase 3: Execute - -The module spawns the underlying process: - -```rust -std::process::Command::new("git") - .args(["log", "--oneline", "-5"]) - .output()? -// stdout: "abc123 Fix bug\ndef456 Add feature\n..." -// exit_code: 0 -``` - -### Phase 4: Filter - -The module applies its filtering strategy to the captured output: - -```rust -git::format_git_output(stdout, "log", verbose) -// Strategy: Stats Extraction -// Filtered: "5 commits, +142/-89" (96% reduction) -``` - -### Phase 5: Print - -```rust -println!("{}", colored_output); -// eprintln! for debug output when verbose > 0 -``` - -### Phase 6: Track - -```rust -tracking::track( - original_cmd: "git log --oneline -5", - rtk_cmd: "rtk git log --oneline -5", - input: &raw_output, // 500 chars - output: &filtered, // 20 chars -) -// SQLite INSERT: input_tokens=125, output_tokens=5, savings_pct=96.0 -``` - -## Exit code preservation - -RTK always propagates the exit code from the underlying tool: - -```rust -if !output.status.success() { - let stderr = String::from_utf8_lossy(&output.stderr); - eprintln!("{}", stderr); - std::process::exit(output.status.code().unwrap_or(1)); -} -``` - -RTK exit codes: `0` = success, `1` = RTK internal error, `N` = preserved from underlying tool. - -## Verbosity levels - -| Flag | Behavior | -|------|----------| -| (none) | Compact output only | -| `-v` | + Debug messages (`eprintln!`) | -| `-vv` | + Command being executed | -| `-vvv` | + Raw output before filtering | - -## Ultra-compact mode - -`-u` / `--ultra-compact`: maximum compression — ASCII icons instead of words, single-line summaries. - -```bash -rtk git push -u # ✓ main (vs "ok main" normally) -``` - -## Module organization - -``` -src/ - main.rs ← Commands enum + routing - cmds/ - git/ ← git, gh, gt, diff - rust/ ← cargo, runner (err/test) - js/ ← pnpm, vitest, tsc, next, playwright, prisma, lint - python/ ← ruff, pytest, mypy, pip - go/ ← go, golangci-lint - dotnet/ ← dotnet, binlog - cloud/ ← aws, docker/kubectl, curl, wget, psql - system/ ← ls, read, grep, find, json, env, log, deps, summary - ruby/ ← rake, rspec, rubocop - core/ ← utils, tracking, tee, config, toml_filter, filter - hooks/ ← init, rewrite, hook_cmd, verify, integrity - analytics/ ← gain, cc_economics, ccusage - discover/ ← rtk discover, registry -``` - -Total: 64 modules (42 command + 22 infrastructure). diff --git a/docs/reference/internals/filter-pipeline.md b/docs/reference/internals/filter-pipeline.md deleted file mode 100644 index 0e52432ba..000000000 --- a/docs/reference/internals/filter-pipeline.md +++ /dev/null @@ -1,81 +0,0 @@ ---- -title: Filter Pipeline -description: The 12 filtering strategies RTK uses to compress command output -sidebar: - order: 2 ---- - -# Filter Pipeline - -## Filtering strategies - -RTK uses 12 distinct filtering strategies depending on the command type: - -| # | Strategy | Reduction | Used by | -|---|----------|-----------|---------| -| 1 | **Stats Extraction** | 90-99% | git status, git log, git diff, pnpm list | -| 2 | **Error Only** | 60-80% | runner (err mode), test failures | -| 3 | **Grouping by Pattern** | 80-90% | lint, tsc, grep (group by file/rule/code) | -| 4 | **Deduplication** | 70-85% | log_cmd (count occurrences) | -| 5 | **Structure Only** | 80-95% | json_cmd (keys + types, strip values) | -| 6 | **Code Filtering** | 0-90% | read, smart (language-aware, 3 levels) | -| 7 | **Failure Focus** | 94-99% | vitest, playwright, runner (test mode) | -| 8 | **Tree Compression** | 50-70% | ls (directory tree with counts) | -| 9 | **Progress Filtering** | 85-95% | wget, pnpm install (strip ANSI bars) | -| 10 | **JSON/Text Dual Mode** | 80%+ | ruff (JSON when available, text fallback) | -| 11 | **State Machine Parsing** | 90%+ | pytest (text state machine: name → result) | -| 12 | **NDJSON Streaming** | 90%+ | go test (line-by-line JSON event parsing) | - -## TOML filter pipeline (8 stages) - -TOML filters run output through this pipeline in order: - -``` -1. strip_ansi — remove terminal color codes -2. replace — regex substitutions -3. match_output — short-circuit: if pattern matches, emit message and stop -4. strip/keep_lines — keep or remove lines by regex -5. truncate_lines_at — truncate lines longer than N chars -6. tail_lines — keep only the last N lines -7. max_lines — keep only the first N lines -8. on_empty — if output is empty, emit this message -``` - -Stages not configured in the filter definition are skipped. First match wins in TOML filter lookup (project → user-global → built-in). - -## Code filtering levels (src/core/filter.rs) - -The `read` and `smart` commands use language-aware filtering with three levels: - -- **`none`**: Keep everything (0%) -- **`minimal`**: Strip comments and excessive blank lines (20-40%) -- **`aggressive`**: Keep signatures only, remove function bodies (60-90%) - -Supported languages: Rust, Python, JavaScript, TypeScript, Go, C, C++, Java, Ruby, Shell. - -## Savings by ecosystem - -``` -GIT (cmds/git/) 85-99% status, diff, log, gh, gt -JS/TS (cmds/js/) 70-99% lint, tsc, next, vitest, playwright, pnpm, prisma -PYTHON (cmds/python/) 70-90% ruff, pytest, mypy, pip -GO (cmds/go/) 75-90% go test, golangci-lint -RUBY (cmds/ruby/) 60-90% rake, rspec, rubocop -DOTNET (cmds/dotnet/) 70-85% dotnet build/test, binlog -CLOUD (cmds/cloud/) 60-80% aws, docker, kubectl, curl, wget, psql -SYSTEM (cmds/system/) 50-90% ls, read, grep, find, json, log, env -RUST (cmds/rust/) 60-99% cargo test/build/clippy, err -``` - -## Shared infrastructure (src/core/) - -| Module | Responsibility | -|--------|----------------| -| `utils.rs` | `strip_ansi`, `truncate`, `execute_command` | -| `filter.rs` | Language-aware code filtering engine | -| `toml_filter.rs` | TOML DSL filter engine (runtime) | -| `tracking.rs` | SQLite token metrics recording | -| `tee.rs` | Raw output recovery on failure | -| `config.rs` | `~/.config/rtk/config.toml` loading | -| `display_helpers.rs` | Terminal formatting helpers | -| `telemetry.rs` | Anonymous daily ping | diff --git a/docs/reference/internals/hook-engine.md b/docs/reference/internals/hook-engine.md deleted file mode 100644 index 0e958d902..000000000 --- a/docs/reference/internals/hook-engine.md +++ /dev/null @@ -1,102 +0,0 @@ ---- -title: Hook Engine -description: How RTK hooks intercept and rewrite agent commands before execution -sidebar: - order: 4 ---- - -# Hook Engine - -## Architecture - -RTK hooks are thin delegates: they parse agent-specific JSON, call `rtk rewrite` as a subprocess, and format the agent-specific response. All rewrite logic lives in the Rust binary (`src/discover/registry.rs`). - -``` -Agent runs "cargo test" - → Hook intercepts (PreToolUse / plugin event) - → Reads JSON input, extracts command string - → Calls `rtk rewrite "cargo test"` - → Registry matches pattern, returns "rtk cargo test" - → Hook formats agent-specific response - → Agent executes "rtk cargo test" - → Filtered output reaches LLM -``` - -## Integration tiers - -| Tier | Mechanism | Examples | -|------|-----------|---------| -| **Full hook** | Shell script or Rust binary, intercepts via agent hook API | Claude Code, Cursor, Copilot, Gemini | -| **Plugin** | TypeScript/JS in agent's plugin system | OpenCode | -| **Rules file** | Prompt-level instructions | Cline, Windsurf, Codex | - -## Rewrite registry - -`src/discover/registry.rs` holds 70+ rewrite patterns organized by category: - -| Category | Savings | -|----------|---------| -| Test runners (vitest, pytest, cargo test, go test, playwright) | 90-99% | -| Build tools (cargo build, npm, pnpm, dotnet, make) | 70-90% | -| VCS (git status/log/diff/show) | 70-80% | -| Language servers (tsc, mypy) | 80-83% | -| Linters (eslint, ruff, golangci-lint, biome) | 80-85% | -| Package managers (pip, cargo install, pnpm list) | 75-80% | -| File operations (ls, find, grep) | 60-75% | -| Infrastructure (docker, kubectl, aws) | 75-85% | - -## Compound command handling - -| Operator | Behavior | -|----------|----------| -| `&&`, `\|\|`, `;` | Both sides rewritten independently | -| `\|` (pipe) | Left side only (right side consumes output format) | -| `find`/`fd` in pipes | Never rewritten (incompatible with xargs/wc/grep) | - -Example: `cargo fmt --all && cargo test` → `rtk cargo fmt --all && rtk cargo test` - -## Exit code contract - -**Hooks must never block command execution.** All error paths must exit 0. A hook that exits non-zero prevents the user's command from running. - -Failure modes handled gracefully: -- RTK binary not found → warning to stderr, exit 0 -- Invalid JSON input → pass through unchanged -- RTK version too old (< 0.23.0) → warning + exit 0 -- `rtk rewrite` crashes → hook exits 0 - -## rtk init - -`src/hooks/init.rs` installs hook files for each supported agent: - -```bash -rtk init --global # Claude Code -rtk init --global --cursor # Cursor -rtk init --global --copilot # VS Code Copilot -rtk init --global --gemini # Gemini CLI -rtk init --global --opencode # OpenCode -rtk init --cline # Cline (project-local) -rtk init --windsurf # Windsurf (project-local) -rtk init --codex # Codex CLI -``` - -## rtk verify - -`src/hooks/verify_cmd.rs` runs inline TOML filter tests and checks hook integrity via SHA-256: - -```bash -rtk verify # all filters pass / fail with diff -rtk init --show # hook status per agent -``` - -## Override controls - -```bash -RTK_DISABLED=1 git status # skip rewrite for one command -``` - -```toml -# ~/.config/rtk/config.toml -[hooks] -exclude_commands = ["git rebase", "docker exec"] -``` diff --git a/docs/reference/internals/tracking-system.md b/docs/reference/internals/tracking-system.md deleted file mode 100644 index 076b3a660..000000000 --- a/docs/reference/internals/tracking-system.md +++ /dev/null @@ -1,120 +0,0 @@ ---- -title: Tracking System -description: How RTK records token savings in SQLite and exposes aggregation APIs -sidebar: - order: 3 ---- - -# Tracking System - -## Overview - -Every RTK command execution is recorded in a local SQLite database. This data powers `rtk gain` and `rtk cc-economics`. - -**Storage locations:** -- Linux: `~/.local/share/rtk/tracking.db` -- macOS: `~/Library/Application Support/rtk/tracking.db` -- Windows: `%APPDATA%\rtk\tracking.db` - -**Retention:** Records older than 90 days are automatically deleted on each write. - -## 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 INSERT - ↓ -Aggregation APIs (get_summary, get_all_days, etc.) - ↓ -rtk gain output -``` - -## Core API - -```rust -pub struct Tracker { - conn: Connection, // SQLite connection -} - -impl Tracker { - pub fn new() -> Result; - - pub fn record( - &self, - original_cmd: &str, // e.g., "ls -la" - rtk_cmd: &str, // e.g., "rtk ls" - input_tokens: usize, // estimate_tokens(raw_output) - output_tokens: usize, // estimate_tokens(filtered_output) - exec_time_ms: u64, - ) -> Result<()>; - - pub fn get_summary(&self) -> Result; - pub fn get_all_days(&self) -> Result>; - pub fn get_weekly(&self) -> Result>; - pub fn get_monthly(&self) -> Result>; -} -``` - -## Token estimation - -``` -estimate_tokens(text) = text.len() / 4 -``` - -~4 characters per token average. Accuracy: ±10% vs actual LLM tokenization. - -## Database schema - -```sql -CREATE TABLE commands ( - id INTEGER PRIMARY KEY, - timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, - original_cmd TEXT NOT NULL, - rtk_cmd TEXT NOT NULL, - input_tokens INTEGER NOT NULL, - output_tokens INTEGER NOT NULL, - saved_tokens INTEGER GENERATED ALWAYS AS (input_tokens - output_tokens), - savings_pct REAL GENERATED ALWAYS AS ( - CASE WHEN input_tokens > 0 - THEN (1.0 - CAST(output_tokens AS REAL) / input_tokens) * 100 - ELSE 0 END - ), - exec_time_ms INTEGER -); -``` - -## Reporting query - -```sql -SELECT - COUNT(*) as total_commands, - SUM(saved_tokens) as total_saved, - AVG(savings_pct) as avg_savings, - SUM(exec_time_ms) as total_time_ms -FROM commands -WHERE timestamp > datetime('now', '-90 days') -``` - -## Configuration - -```toml -[tracking] -enabled = true -history_days = 90 -database_path = "/custom/path/tracking.db" # optional override -``` - -Environment variable override: `RTK_DB_PATH=/custom/path.db` - -## Thread safety - -Single-threaded execution with `Mutex>` for future-proofing. No multi-threading currently used. diff --git a/docs/reference/toml-dsl/specification.md b/docs/reference/toml-dsl/specification.md deleted file mode 100644 index 9b4c065b4..000000000 --- a/docs/reference/toml-dsl/specification.md +++ /dev/null @@ -1,124 +0,0 @@ ---- -title: TOML DSL Specification -description: Complete specification for RTK's TOML filter format -sidebar: - order: 1 ---- - -# TOML DSL Specification - -TOML filters are the declarative way to add RTK support for commands with line-by-line text output. They are embedded in the binary at build time and can also be used as project-local or user-global overrides. - -## File format - -```toml -[filters.my-tool] -description = "Short description of what this filter does" -match_command = "^my-tool\\b" # regex matched against full command string -strip_ansi = true # optional: strip ANSI codes first - -strip_lines_matching = [ # optional: drop lines matching any regex - "^\\s*$", - "^noise pattern", -] - -keep_lines_matching = [ # optional: keep only matching lines - "^error", - "^warning", -] - -max_lines = 40 # optional: keep only first N lines after filtering -tail_lines = 20 # optional: keep only last N lines -truncate_lines_at = 120 # optional: truncate lines longer than N chars -on_empty = "my-tool: nothing to do" # optional: message when output is empty - -[[tests.my-tool]] -name = "descriptive test name" -input = "raw command output here" -expected = "expected filtered output" -``` - -## Field reference - -| Field | Type | Required | Description | -|-------|------|----------|-------------| -| `description` | string | yes | Human-readable description | -| `match_command` | regex | yes | Matched against the full command string | -| `strip_ansi` | bool | no | Strip ANSI escape codes before processing | -| `strip_lines_matching` | regex[] | no | Drop lines matching any of these patterns | -| `keep_lines_matching` | regex[] | no | Keep only lines matching at least one pattern | -| `replace` | array | no | Regex substitutions: `{ pattern, replacement }` | -| `match_output` | array | no | Short-circuit rules: `{ pattern, message }` | -| `truncate_lines_at` | int | no | Truncate lines longer than N characters | -| `max_lines` | int | no | Keep only the first N lines (after other stages) | -| `tail_lines` | int | no | Keep only the last N lines | -| `on_empty` | string | no | Message emitted when pipeline produces empty output | - -## Pipeline execution order - -When a filter matches, output passes through these stages in order: - -1. `strip_ansi` — remove terminal color codes -2. `replace` — regex substitutions -3. `match_output` — short-circuit: if pattern matches, emit message and stop -4. `strip_lines_matching` / `keep_lines_matching` -5. `truncate_lines_at` -6. `tail_lines` -7. `max_lines` -8. `on_empty` — if output is now empty, emit this message - -Stages not defined in the filter are skipped. - -## Inline tests - -Every filter must have at least one `[[tests.]]` entry. Tests run during `cargo test` and `rtk verify`. - -```toml -[[tests.my-tool]] -name = "strips progress lines" -input = "Loading...\nDone: 3 issues" -expected = "Done: 3 issues" - -[[tests.my-tool]] -name = "empty output fallback" -input = "Loading...\nProgress: 100%" -expected = "my-tool: nothing to do" -``` - -## Filter lookup priority - -``` -1. .rtk/filters.toml (project-local — highest priority) -2. ~/.config/rtk/filters.toml (user-global) -3. BUILTIN_TOML (embedded in binary — lowest priority) -``` - -First match wins. A project filter with the same name as a built-in triggers a warning: - -``` -[rtk] warning: filter 'make' is shadowing a built-in filter -``` - -## Built-in filter compilation - -Built-in filters live in `src/filters/*.toml`. At build time, `build.rs`: - -1. Lists all `*.toml` files alphabetically -2. Concatenates them into a single TOML blob -3. Validates syntax and checks for duplicate names (build fails on error) -4. Embeds the result in the binary via `include_str!(concat!(env!(OUT_DIR), "/builtin_filters.toml"))` - -New built-in filters require a new RTK release. Project and user filters take effect immediately without rebuilding. - -## Naming convention - -Use the command name as the filter key: `terraform-plan`, `docker-inspect`, `mix-compile`. For subcommands, prefer `cmd-subcommand` over grouping: `docker-ps.toml`, not `docker.toml` with multiple filters. - -## Regex syntax - -Patterns use Rust's `regex` crate (RE2-compatible). Backslashes must be doubled in TOML strings: - -```toml -match_command = "^my-tool\\b" # matches "my-tool" as a word -strip_lines_matching = ["^\\s*$"] # matches blank lines -``` diff --git a/docs/tracking.md b/docs/tracking.md deleted file mode 100644 index 82c12883d..000000000 --- a/docs/tracking.md +++ /dev/null @@ -1,583 +0,0 @@ -# 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; - - /// 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; - - /// Get daily statistics (all days) - pub fn get_all_days(&self) -> Result>; - - /// Get weekly statistics (grouped by week) - pub fn get_by_week(&self) -> Result>; - - /// Get monthly statistics (grouped by month) - pub fn get_by_month(&self) -> Result>; - - /// Get recent command history (limit = max records) - pub fn get_recent(&self, limit: usize) -> Result>; -} -``` - -#### `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 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 enabled by default**: RTK sends a daily anonymous usage ping (version, OS, command counts, token savings). Device identity is a salted SHA-256 hash. Opt out with `RTK_TELEMETRY_DISABLED=1` or `[telemetry] enabled = false` in `~/.config/rtk/config.toml` -- **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 From 6e55f8984c6efb1b23b187f704f159f7aaa6e5e0 Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Mon, 6 Apr 2026 11:15:57 +0200 Subject: [PATCH 10/13] =?UTF-8?q?docs:=20address=20Adrien's=20review=20?= =?UTF-8?q?=E2=80=94=20structure,=20missing=20content,=20agents?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Structure: - Move docs/README.md → .github/docs-pipeline-contract.md (interface contract is not user-facing content) - Rewrite docs/guide/index.md as a proper landing page with RTK intro, flow diagram, and navigation (replaces empty nav stub) - Move configuration.md → getting-started/configuration.md (sidebar order 4, after supported-agents per Adrien's suggestion) Missing content: - Add docs/guide/analytics/discover.md: rtk discover (missed savings analysis) and rtk session (adoption tracking across sessions) - Add uninstall instructions to installation.md (rtk init -g --uninstall, cargo uninstall, brew uninstall) - Add global flags section to what-rtk-covers.md (-u/--ultra-compact, -v/--verbose) - Add rtk smart command to what-rtk-covers.md Files section Agents: - Update supported-agents.md: 10 agents (was 9) - Add OpenClaw (TypeScript plugin, before_tool_call) - Add Mistral Vibe (planned, issue #800) - Fix GitHub Copilot: VS Code = transparent rewrite via PreToolUse, Copilot CLI = deny-with-suggestion (upstream limitation) Signed-off-by: Florian BRUNIAUX --- .github/docs-pipeline-contract.md | 57 +++++++++++++++++ docs/README.md | 64 ------------------- docs/guide/analytics/discover.md | 58 +++++++++++++++++ docs/guide/analytics/gain.md | 60 ++++++++++++++--- .../{ => getting-started}/configuration.md | 4 +- docs/guide/getting-started/installation.md | 8 +++ docs/guide/getting-started/quick-start.md | 36 +++-------- .../guide/getting-started/supported-agents.md | 26 ++++++-- docs/guide/index.md | 58 +++++++++++++---- docs/guide/what-rtk-covers.md | 20 ++++++ src/filters/README.md | 55 ++++++++++++++++ 11 files changed, 328 insertions(+), 118 deletions(-) create mode 100644 .github/docs-pipeline-contract.md delete mode 100644 docs/README.md create mode 100644 docs/guide/analytics/discover.md rename docs/guide/{ => getting-started}/configuration.md (94%) diff --git a/.github/docs-pipeline-contract.md b/.github/docs-pipeline-contract.md new file mode 100644 index 000000000..f812912cd --- /dev/null +++ b/.github/docs-pipeline-contract.md @@ -0,0 +1,57 @@ +# RTK Documentation — Interface Contract + +This directory contains user-facing documentation for the RTK website. +It feeds `rtk-ai/rtk-website` via the `prepare-docs.mjs` pipeline. + +**Scope**: `docs/guide/` is website content only. Technical and contributor documentation +lives in the codebase (distributed, co-located pattern): +- `ARCHITECTURE.md` — System design, ADRs, filtering strategies +- `CONTRIBUTING.md` — Design philosophy, PR process, TOML vs Rust +- `SECURITY.md` — Vulnerability policy +- `src/*/README.md` — Per-module implementation docs +- `hooks/README.md` — Hook system and agent integrations + +## Structure + +``` +docs/ + README.md <- This file (interface contract — do not remove) + guide/ -> User-facing documentation (website "Guide" tab) + index.md + getting-started/ + installation.md + quick-start.md + supported-agents.md + what-rtk-covers.md + analytics/ + gain.md + configuration.md + troubleshooting.md +``` + +## Frontmatter (required on every .md) + +Every markdown file under `docs/guide/` must include: + +```yaml +--- +title: string # Page title (used in sidebar + search) +description: string # One-line summary for search results and SEO +sidebar: + order: number # Position within the sidebar group (1 = first) +--- +``` + +The `prepare-docs.mjs` pipeline validates this at build time and fails fast +if frontmatter is missing or malformed. + +## Conventions + +- **Filenames**: kebab-case, `.md` only +- **Subdirectories**: become sidebar groups in Starlight +- **Internal links**: relative (`./foo.md`, `../configuration.md`) +- **Diagrams**: Mermaid in fenced code blocks +- **Code samples**: always specify the language (`rust`, `toml`, `bash`) +- **Language**: English only +- **No `rtk ` syntax**: users never type `rtk` — hooks rewrite commands transparently. + Only `rtk gain`, `rtk init`, `rtk verify`, and `rtk proxy` appear as user-typed commands. diff --git a/docs/README.md b/docs/README.md deleted file mode 100644 index 07d64eb46..000000000 --- a/docs/README.md +++ /dev/null @@ -1,64 +0,0 @@ -# RTK Documentation — Interface Contract - -This directory is the source of truth for user-facing and contributor documentation. -It feeds the RTK website via the `prepare-docs.mjs` pipeline in `rtk-ai/rtk-website`. - -## Structure - -``` -docs/ - README.md <- This file (interface contract — do not remove) - guide/ -> User-facing documentation (Tab "Guide") - reference/ -> Contributor/technical documentation (Tab "Reference") - architecture/ -> Conceptual/visual documentation (Tab "Architecture") -``` - -## Frontmatter (required on every .md) - -Every markdown file under `docs/guide/`, `docs/reference/`, and `docs/architecture/` -must include this frontmatter block at the top: - -```yaml ---- -title: string # Page title (used in sidebar + search) -description: string # One-line summary for search results and SEO -sidebar: - order: number # Position within the sidebar group (1 = first) ---- -``` - -The `prepare-docs.mjs` pipeline validates this contract at build time and **fails fast** -with a clear error message if frontmatter is missing or malformed. - -## Conventions - -- **Filenames**: kebab-case, `.md` only (e.g., `getting-started.md`, `quick-start.md`) -- **Subdirectories**: become sidebar groups in Starlight (directory name = group label) -- **Internal links**: relative within the same tab (`./foo.md`, `./getting-started/installation.md`) -- **Cross-tab links**: full path from `docs/` root (`../../reference/internals/command-routing.md`) -- **Diagrams**: Mermaid in fenced code blocks (` ```mermaid `) -- **Code samples**: always specify the language (`rust`, `toml`, `bash`, `shell`) -- **Language**: English only - -## Tabs overview - -| Tab | Path | Audience | -|-----|------|----------| -| Guide | `docs/guide/` | End users installing and using RTK | -| Reference | `docs/reference/` | Contributors, maintainers, integrators | -| Architecture | `docs/architecture/` | Readers exploring design decisions and diagrams | - -## Root files (do not move or modify structure) - -The following files live at the repository root and are **not** managed by this pipeline. -They are the canonical source for GitHub display and remain unchanged. - -- `README.md` — Project overview -- `INSTALL.md` — Installation reference (full) -- `CONTRIBUTING.md` — Contribution guide -- `SECURITY.md` — Security policy -- `ARCHITECTURE.md` — Full architecture document -- `CHANGELOG.md` — Release history - -The guide files in `docs/` are derived, English-only, structured versions intended -for the website. They reference root files as source material but do not replace them. diff --git a/docs/guide/analytics/discover.md b/docs/guide/analytics/discover.md new file mode 100644 index 000000000..77d21cc65 --- /dev/null +++ b/docs/guide/analytics/discover.md @@ -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](../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. diff --git a/docs/guide/analytics/gain.md b/docs/guide/analytics/gain.md index ef0000298..db2249d4b 100644 --- a/docs/guide/analytics/gain.md +++ b/docs/guide/analytics/gain.md @@ -92,12 +92,12 @@ Same columns as daily, aggregated by Sunday-Saturday week or calendar month. | Command | Typical savings | Mechanism | |---------|----------------|-----------| -| `rtk git status` | 77-93% | Compact stat format | -| `rtk eslint` | 84% | Group by rule | -| `rtk vitest run` | 94-99% | Show failures only | -| `rtk find` | 75% | Tree format | -| `rtk pnpm list` | 70-90% | Compact dependencies | -| `rtk grep` | 70% | Truncate + group | +| `git status` | 77-93% | Compact stat format | +| `eslint` | 84% | Group by rule | +| `vitest run` | 94-99% | Show failures only | +| `find` | 75% | Tree format | +| `pnpm list` | 70-90% | Compact dependencies | +| `grep` | 70% | Truncate + group | ## How token estimation works @@ -131,19 +131,63 @@ cp ~/.local/share/rtk/history.db ~/backups/rtk-history-$(date +%Y%m%d).db 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 +``` + ## Troubleshooting **No data showing:** ```bash ls -lh ~/.local/share/rtk/history.db sqlite3 ~/.local/share/rtk/history.db "SELECT COUNT(*) FROM commands" -rtk git status # run a tracked command to generate data +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 -rtk git status > output.txt +git status > output.txt python -c " import tiktoken enc = tiktoken.get_encoding('cl100k_base') diff --git a/docs/guide/configuration.md b/docs/guide/getting-started/configuration.md similarity index 94% rename from docs/guide/configuration.md rename to docs/guide/getting-started/configuration.md index 053eb351e..f2b1d5a2e 100644 --- a/docs/guide/configuration.md +++ b/docs/guide/getting-started/configuration.md @@ -2,7 +2,7 @@ title: Configuration description: Customize RTK behavior via config.toml, environment variables, and per-project filters sidebar: - order: 7 + order: 4 --- # Configuration @@ -112,4 +112,4 @@ enabled = false ## Per-project filters -Create `.rtk/filters.toml` in your project root to add custom filters or override built-ins. See [Using Filters](./filters/using-filters.md) for the TOML DSL reference. +Create `.rtk/filters.toml` in your project root to add custom filters or override built-ins. See [`src/filters/README.md`](https://github.com/rtk-ai/rtk/blob/master/src/filters/README.md) for the full TOML DSL reference. diff --git a/docs/guide/getting-started/installation.md b/docs/guide/getting-started/installation.md index e52c9df32..3f3eac286 100644 --- a/docs/guide/getting-started/installation.md +++ b/docs/guide/getting-started/installation.md @@ -71,3 +71,11 @@ 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) +``` diff --git a/docs/guide/getting-started/quick-start.md b/docs/guide/getting-started/quick-start.md index 8f3b6ed42..af661ebff 100644 --- a/docs/guide/getting-started/quick-start.md +++ b/docs/guide/getting-started/quick-start.md @@ -32,34 +32,22 @@ cd /your/project && rtk init This installs the hook that automatically rewrites commands. Restart your AI assistant after this step. -## Step 2: Run your first RTK commands +## Step 2: Use your tools normally -You can use RTK directly or let the hook rewrite commands transparently. +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. -```bash -# Git — compact status and log -rtk git status -rtk git log -10 - -# Rust — build and test with failures only -rtk cargo build -rtk cargo test +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`. -# JavaScript — type errors grouped by file -rtk tsc -rtk vitest run -``` +Supported ecosystems: Git, Cargo/Rust, JavaScript (vitest, tsc, eslint, pnpm, Next.js, Prisma), Python, Go, Ruby, .NET, Docker/Kubernetes, GitHub CLI, and more. See [What RTK Optimizes](../what-rtk-covers.md) for the full list. ## Step 3: Check your savings -After a few commands, see how much you saved: +After a few commands, see how much was saved: ```bash rtk gain ``` -Output: - ``` Total commands : 12 Input tokens : 45,230 @@ -67,22 +55,16 @@ Output tokens : 4,890 Saved : 40,340 (89.2%) ``` -## Step 4: Use the proxy for unsupported commands +## Step 4: Unsupported commands -Any command RTK doesn't know about runs through passthrough — the output is unchanged but usage is tracked: +Commands RTK doesn't recognize run through passthrough — output is unchanged, usage is tracked: ```bash rtk proxy make install ``` -## What the hook does - -Once installed, the hook intercepts every command your AI assistant runs and rewrites it transparently. You don't need to type `rtk` — the hook does it automatically. - -For example, when Claude Code executes `cargo test`, the hook rewrites it to `rtk cargo test` before it runs. The filtered output is what the LLM sees. - ## Next steps +- [What RTK Optimizes](../what-rtk-covers.md) — all supported commands and savings by ecosystem - [Supported agents](./supported-agents.md) — Claude Code, Cursor, Copilot, and more -- [Commands](../commands/git.md) — full reference for each ecosystem -- [Configuration](../configuration.md) — customize RTK behavior +- [Configuration](./configuration.md) — customize RTK behavior diff --git a/docs/guide/getting-started/supported-agents.md b/docs/guide/getting-started/supported-agents.md index 52e5b520c..dc45bf970 100644 --- a/docs/guide/getting-started/supported-agents.md +++ b/docs/guide/getting-started/supported-agents.md @@ -7,7 +7,7 @@ sidebar: # Supported Agents -RTK supports 9 AI coding agents across 3 integration tiers. +RTK supports 10 AI coding agents across 3 integration tiers. Mistral Vibe support is planned. ## How it works @@ -29,14 +29,16 @@ Agent runs "cargo test" | Agent | Integration tier | Can rewrite transparently? | |-------|-----------------|---------------------------| | Claude Code | Shell hook (`PreToolUse`) | Yes | -| VS Code Copilot Chat | Rust binary | Yes | -| GitHub Copilot CLI | Rust binary (deny-with-suggestion) | No (agent retries) | +| VS Code Copilot Chat | Shell hook (`PreToolUse`) | Yes | +| GitHub Copilot CLI | Shell hook (deny-with-suggestion) | No (agent retries) | | Cursor | Shell hook (`preToolUse`) | Yes | -| Gemini CLI | Rust binary | Yes | +| Gemini CLI | Rust binary (`BeforeTool`) | Yes | +| OpenCode | TypeScript plugin (`tool.execute.before`) | Yes | +| OpenClaw | TypeScript plugin (`before_tool_call`) | Yes | | Cline / Roo Code | Rules file (prompt-level) | N/A | | Windsurf | Rules file (prompt-level) | N/A | | Codex CLI | AGENTS.md instructions | N/A | -| OpenCode | TypeScript plugin | Yes | +| Mistral Vibe | Planned ([#800](https://github.com/rtk-ai/rtk/issues/800)) | Pending upstream | ## Installation by agent @@ -78,7 +80,15 @@ rtk init --global --gemini rtk init --global --opencode ``` -Restart OpenCode. The plugin uses the `tool.execute.before` event. +Creates `~/.config/opencode/plugins/rtk.ts`. Uses the `tool.execute.before` hook. + +### OpenClaw + +```bash +openclaw plugins install ./openclaw +``` + +Plugin in the `openclaw/` directory. Uses the `before_tool_call` hook, delegates to `rtk rewrite`. ### Cline / Roo Code @@ -100,6 +110,10 @@ rtk init --windsurf # creates .windsurfrules in current project rtk init --codex # creates AGENTS.md or patches existing one ``` +### 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 | diff --git a/docs/guide/index.md b/docs/guide/index.md index 4ccbf78a0..2b9a09b47 100644 --- a/docs/guide/index.md +++ b/docs/guide/index.md @@ -1,19 +1,55 @@ --- -title: RTK Guide -description: User-facing documentation for RTK, the token-saving CLI proxy for AI coding assistants +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 Guide +# RTK — Rust Token Killer -RTK (Rust Token Killer) is a CLI proxy that reduces LLM token consumption by 60-90% on common development operations. It filters and compresses command output before it reaches your AI assistant, without changing how you work. +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. -## What's in this guide +**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. -- **[Getting Started](./getting-started/installation.md)** — Install RTK, verify it works, run your first command -- **Commands** — Per-ecosystem reference: git, cargo, GitHub CLI, JavaScript, Python, and more -- **[Filters](./filters/using-filters.md)** — Create custom TOML filters for your own commands -- **[Analytics](./analytics/gain.md)** — Measure your actual token savings with `rtk gain` -- **[Configuration](./configuration.md)** — Customize RTK behavior via `~/.config/rtk/config.toml` -- **[Troubleshooting](./troubleshooting.md)** — Common issues and how to fix them +## 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 + +60+ commands across 9 ecosystems — Git, Cargo/Rust, JavaScript, Python, Go, Ruby, .NET, Docker/Kubernetes, and more. See [What RTK Optimizes](./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 7 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 [Analytics](./analytics/gain.md) for export formats and analysis workflows. + +## Further reading + +- [Configuration](./getting-started/configuration.md) — config.toml, global flags, env vars, tee recovery +- [Troubleshooting](./troubleshooting.md) — common issues and fixes +- [ARCHITECTURE.md](https://github.com/rtk-ai/rtk/blob/master/ARCHITECTURE.md) — system design for contributors diff --git a/docs/guide/what-rtk-covers.md b/docs/guide/what-rtk-covers.md index 3e3f2a46a..426e91a97 100644 --- a/docs/guide/what-rtk-covers.md +++ b/docs/guide/what-rtk-covers.md @@ -113,6 +113,8 @@ Once RTK is installed with a hook, these commands are automatically intercepted | `grep` | 70% | Truncated lines, grouped by file | | `diff` | 65% | Context reduced | | `wc` | 60% | Compact counts | +| `cat` / `head` / `tail ` | 60-80% | Smart file reading via `rtk read` | +| `rtk smart ` | 85% | 2-line heuristic code summary (signatures only) | ## Cloud and Data @@ -122,6 +124,24 @@ Once RTK is installed with a hook, these commands are automatically intercepted | `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 | +|------|-------------| +| `-u` / `--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 +git log # → already filtered by RTK +git log -u # → ultra-compact variant (if using rtk directly) + +# Debug: see what RTK is doing +RTK_DISABLED=0 git status -vvv +``` + ## 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: diff --git a/src/filters/README.md b/src/filters/README.md index fbd4c4cb0..702f5034f 100644 --- a/src/filters/README.md +++ b/src/filters/README.md @@ -64,3 +64,58 @@ expected = "expected filtered output" Use the command name as the filename: `terraform-plan.toml`, `docker-inspect.toml`, `mix-compile.toml`. For commands with subcommands, prefer `-.toml` over grouping multiple filters in one file. + +## Build and runtime pipeline + +How a `.toml` file goes from contributor → binary → filtered output. + +```mermaid +flowchart TD + A[["src/filters/my-tool.toml\n(new file)"]] --> B + + subgraph BUILD ["cargo build"] + B["build.rs\n1. ls src/filters/*.toml\n2. sort alphabetically\n3. concat → BUILTIN_TOML"] --> C + C{"TOML valid?\nDuplicate names?"} -->|"fail"| D[["Build fails\nerror points to bad file"]] + C -->|"ok"| E[["OUT_DIR/builtin_filters.toml\n(generated)"]] + E --> F["rustc embeds via include_str!"] + F --> G[["rtk binary\nBUILTIN_TOML embedded"]] + end + + subgraph TESTS ["cargo test"] + H["test_builtin_filter_count\nassert_eq!(filters.len(), N)"] -->|"wrong count"| I[["FAIL"]] + J["test_builtin_all_filters_present\nassert!(names.contains('my-tool'))"] -->|"name missing"| K[["FAIL"]] + L["test_builtin_all_filters_have_inline_tests\nassert!(tested.contains(name))"] -->|"no tests"| M[["FAIL"]] + end + + subgraph RUNTIME ["rtk my-tool args"] + R["TomlFilterRegistry::load()\n1. .rtk/filters.toml\n2. ~/.config/rtk/filters.toml\n3. BUILTIN_TOML\n4. passthrough"] --> S + S{"match_command\nmatches?"} -->|"no match"| T[["exec raw (passthrough)"]] + S -->|"match"| U["exec command\ncapture stdout"] + U --> V["8-stage pipeline\nstrip_ansi → replace → match_output\n→ strip/keep_lines → truncate\n→ tail_lines → max_lines → on_empty"] + V --> W[["print filtered output + exit code"]] + end + + G --> H & J & L & R +``` + +## Filter lookup priority + +```mermaid +flowchart LR + CMD["rtk my-tool args"] --> P1 + P1{"1. .rtk/filters.toml\n(project-local)"} + P1 -->|"match"| WIN["apply filter"] + P1 -->|"no match"| P2 + P2{"2. ~/.config/rtk/filters.toml\n(user-global)"} + P2 -->|"match"| WIN + P2 -->|"no match"| P3 + P3{"3. BUILTIN_TOML\n(binary)"} + P3 -->|"match"| WIN + P3 -->|"no match"| P4[["exec raw (passthrough)"]] +``` + +First match wins. A project filter with the same name as a built-in shadows the built-in and triggers a warning: + +``` +[rtk] warning: filter 'make' is shadowing a built-in filter +``` From 6938a9dacc00670fd61ec44a0fc2264974709e0e Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Mon, 6 Apr 2026 18:37:05 +0200 Subject: [PATCH 11/13] docs: fix broken README links and reduce redundancy, point to docs site - Fix broken links: docs/TROUBLESHOOTING.md and docs/AUDIT_GUIDE.md were removed in previous commit; update nav header and Documentation section - Condense Supported AI Tools: keep the summary table, replace 12 per-agent detail blocks with a single link to rtk-ai.app/guide/supported-agents - Condense Configuration: keep essential snippet and tee recovery example, link to rtk-ai.app/guide/configuration for full reference - Update Documentation section: lead with rtk-ai.app/guide as primary entry, keep INSTALL.md, ARCHITECTURE.md, CONTRIBUTING.md, SECURITY.md Signed-off-by: Florian BRUNIAUX --- README.md | 145 +++++------------------------------------------------- 1 file changed, 13 insertions(+), 132 deletions(-) diff --git a/README.md b/README.md index 24a93f364..128bbaa40 100644 --- a/README.md +++ b/README.md @@ -17,8 +17,8 @@

WebsiteInstall • - Troubleshooting • - Architecture • + Troubleshooting • + ArchitectureDiscord

@@ -313,153 +313,32 @@ RTK supports 10 AI coding tools. Each integration transparently rewrites shell c | Tool | Install | Method | |------|---------|--------| | **Claude Code** | `rtk init -g` | PreToolUse hook (bash) | -| **GitHub Copilot (VS Code)** | `rtk init -g --copilot` | PreToolUse hook (`rtk hook copilot`) — transparent rewrite | +| **GitHub Copilot (VS Code)** | `rtk init -g --copilot` | PreToolUse hook — transparent rewrite | | **GitHub Copilot CLI** | `rtk init -g --copilot` | PreToolUse deny-with-suggestion (CLI limitation) | | **Cursor** | `rtk init -g --agent cursor` | preToolUse hook (hooks.json) | -| **Gemini CLI** | `rtk init -g --gemini` | BeforeTool hook (`rtk hook gemini`) | +| **Gemini CLI** | `rtk init -g --gemini` | BeforeTool hook | | **Codex** | `rtk init -g --codex` | AGENTS.md + RTK.md instructions | | **Windsurf** | `rtk init --agent windsurf` | .windsurfrules (project-scoped) | | **Cline / Roo Code** | `rtk init --agent cline` | .clinerules (project-scoped) | | **OpenCode** | `rtk init -g --opencode` | Plugin TS (tool.execute.before) | | **OpenClaw** | `openclaw plugins install ./openclaw` | Plugin TS (before_tool_call) | -| **Mistral Vibe** | Planned (#800) | Blocked on upstream BeforeToolCallback | +| **Mistral Vibe** | Planned ([#800](https://github.com/rtk-ai/rtk/issues/800)) | Blocked on upstream | -### Claude Code (default) - -```bash -rtk init -g # Install hook + RTK.md -rtk init -g --auto-patch # Non-interactive (CI/CD) -rtk init --show # Verify installation -rtk init -g --uninstall # Remove -``` - -### GitHub Copilot (VS Code + CLI) - -```bash -rtk init -g --copilot # Install hook + instructions -``` - -Creates `.github/hooks/rtk-rewrite.json` (PreToolUse hook) and `.github/copilot-instructions.md` (prompt-level awareness). - -The hook (`rtk hook copilot`) auto-detects the format: -- **VS Code Copilot Chat**: transparent rewrite via `updatedInput` (same as Claude Code) -- **Copilot CLI**: deny-with-suggestion (CLI does not support `updatedInput` yet — see [copilot-cli#2013](https://github.com/github/copilot-cli/issues/2013)) - -### Cursor - -```bash -rtk init -g --agent cursor -``` - -Creates `~/.cursor/hooks/rtk-rewrite.sh` + patches `~/.cursor/hooks.json` with preToolUse matcher. Works with both Cursor editor and `cursor-agent` CLI. - -### Gemini CLI - -```bash -rtk init -g --gemini -rtk init -g --gemini --uninstall -``` - -Creates `~/.gemini/hooks/rtk-hook-gemini.sh` + patches `~/.gemini/settings.json` with BeforeTool hook. - -### Codex (OpenAI) - -```bash -rtk init -g --codex -``` - -Creates `~/.codex/RTK.md` + `~/.codex/AGENTS.md` with `@RTK.md` reference. Codex reads these as global instructions. - -### Windsurf - -```bash -rtk init --agent windsurf -``` - -Creates `.windsurfrules` in the current project. Cascade reads rules and prefixes commands with `rtk`. - -### Cline / Roo Code - -```bash -rtk init --agent cline -``` - -Creates `.clinerules` in the current project. Cline reads rules and prefixes commands with `rtk`. - -### OpenCode - -```bash -rtk init -g --opencode -``` - -Creates `~/.config/opencode/plugins/rtk.ts`. Uses `tool.execute.before` hook. - -### OpenClaw - -```bash -openclaw plugins install ./openclaw -``` - -Plugin in `openclaw/` directory. Uses `before_tool_call` hook, delegates to `rtk rewrite`. - -### Mistral Vibe (planned) - -Blocked on upstream BeforeToolCallback support ([mistral-vibe#531](https://github.com/mistralai/mistral-vibe/issues/531), [PR #533](https://github.com/mistralai/mistral-vibe/pull/533)). Tracked in [#800](https://github.com/rtk-ai/rtk/issues/800). - -### Commands Rewritten - -| Raw Command | Rewritten To | -|-------------|-------------| -| `git status/diff/log/add/commit/push/pull` | `rtk git ...` | -| `gh pr/issue/run` | `rtk gh ...` | -| `cargo test/build/clippy` | `rtk cargo ...` | -| `cat/head/tail ` | `rtk read ` | -| `rg/grep ` | `rtk grep ` | -| `ls` | `rtk ls` | -| `vitest/jest` | `rtk vitest run` | -| `tsc` | `rtk tsc` | -| `eslint/biome` | `rtk lint` | -| `prettier` | `rtk prettier` | -| `playwright` | `rtk playwright` | -| `prisma` | `rtk prisma` | -| `ruff check/format` | `rtk ruff ...` | -| `pytest` | `rtk pytest` | -| `pip list/install` | `rtk pip ...` | -| `go test/build/vet` | `rtk go ...` | -| `golangci-lint` | `rtk golangci-lint` | -| `rake test` / `rails test` | `rtk rake test` | -| `rspec` / `bundle exec rspec` | `rtk rspec` | -| `rubocop` / `bundle exec rubocop` | `rtk rubocop` | -| `bundle install/update` | `rtk bundle ...` | -| `aws sts/ec2/lambda/...` | `rtk aws ...` | -| `docker ps/images/logs` | `rtk docker ...` | -| `kubectl get/logs` | `rtk kubectl ...` | -| `curl` | `rtk curl` | -| `pnpm list/outdated` | `rtk pnpm ...` | - -Commands already using `rtk`, heredocs (`<<`), and unrecognized commands pass through unchanged. +For per-agent setup details, override controls, and graceful degradation, see the [Supported Agents guide](https://www.rtk-ai.app/guide/getting-started/supported-agents). ## Configuration -### Config File - `~/.config/rtk/config.toml` (macOS: `~/Library/Application Support/rtk/config.toml`): ```toml -[tracking] -database_path = "/path/to/custom.db" # default: ~/.local/share/rtk/history.db - [hooks] exclude_commands = ["curl", "playwright"] # skip rewrite for these [tee] enabled = true # save raw output on failure (default: true) mode = "failures" # "failures", "always", or "never" -max_files = 20 # rotation limit ``` -### Tee: Full Output Recovery - When a command fails, RTK saves the full unfiltered output so the LLM can read it without re-executing: ``` @@ -467,6 +346,8 @@ FAILED: 2/15 tests [full output: ~/.local/share/rtk/tee/1707753600_cargo_test.log] ``` +For the full config reference (all sections, env vars, per-project filters), see the [Configuration guide](https://www.rtk-ai.app/guide/getting-started/configuration). + ### Uninstall ```bash @@ -477,11 +358,11 @@ brew uninstall rtk # If installed via Homebrew ## Documentation -- **[TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md)** - Fix common issues -- **[INSTALL.md](INSTALL.md)** - Detailed installation guide -- **[ARCHITECTURE.md](docs/contributing/ARCHITECTURE.md)** - Technical architecture -- **[SECURITY.md](SECURITY.md)** - Security policy and PR review process -- **[AUDIT_GUIDE.md](docs/AUDIT_GUIDE.md)** - Token savings analytics guide +- **[rtk-ai.app/guide](https://www.rtk-ai.app/guide)** — full user guide (installation, supported agents, what gets optimized, analytics, configuration, troubleshooting) +- **[INSTALL.md](INSTALL.md)** — detailed installation reference +- **[ARCHITECTURE.md](ARCHITECTURE.md)** — system design and technical decisions +- **[CONTRIBUTING.md](CONTRIBUTING.md)** — contribution guide +- **[SECURITY.md](SECURITY.md)** — security policy ## Privacy & Telemetry From cea6e2093d5f87bd21cc0efb2c685183955bf36b Mon Sep 17 00:00:00 2001 From: Florian BRUNIAUX Date: Mon, 6 Apr 2026 18:48:43 +0200 Subject: [PATCH 12/13] docs: add Star History and StarMapper to README Signed-off-by: Florian BRUNIAUX --- README.md | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/README.md b/README.md index 128bbaa40..78116b252 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,26 @@ export RTK_TELEMETRY_DISABLED=1 enabled = false ``` +## Star History + + + + + + Star History Chart + + + +## StarMapper + + + + + + StarMapper + + + ## Contributing Contributions welcome! Please open an issue or PR on [GitHub](https://github.com/rtk-ai/rtk). From d22f22b9681412dc9e3f7e2248eb42e187c70e5a Mon Sep 17 00:00:00 2001 From: mariuszs Date: Wed, 8 Apr 2026 22:07:19 +0200 Subject: [PATCH 13/13] =?UTF-8?q?feat(mvn):=20add=20Maven=20filter=20modul?= =?UTF-8?q?e=20=E2=80=94=20test,=20build,=20dependency:tree?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit State-machine parser for mvn test (Preamble → Testing → Summary → Done), line filter for build goals, and dependency:tree filter stripping duplicates and version-managed annotations. Streaming passthrough for unrecognized goals. Replaces mvn-build.toml with Rust module achieving 90-99%+ token savings. 21 tests against 8 real-world fixtures. --- src/cmds/java/mod.rs | 1 + src/cmds/java/mvn_cmd.rs | 978 ++++++++++++++++++++ src/cmds/mod.rs | 1 + src/core/toml_filter.rs | 11 +- src/discover/rules.rs | 6 +- src/filters/mvn-build.toml | 44 - src/main.rs | 38 + tests/fixtures/mvn_dep_tree_beacon.txt | 652 +++++++++++++ tests/fixtures/mvn_dep_tree_conflicts.txt | 16 + tests/fixtures/mvn_dep_tree_simple.txt | 22 + tests/fixtures/mvn_test_fail_auth.txt | 95 ++ tests/fixtures/mvn_test_large_suite.txt | 204 ++++ tests/fixtures/mvn_test_many_failures.txt | 115 +++ tests/fixtures/mvn_test_multimodule.txt | 118 +++ tests/fixtures/mvn_test_pass_large_ansi.txt | 53 ++ tests/fixtures/mvn_test_pass_mavenmcp.txt | 35 + 16 files changed, 2336 insertions(+), 53 deletions(-) create mode 100644 src/cmds/java/mod.rs create mode 100644 src/cmds/java/mvn_cmd.rs delete mode 100644 src/filters/mvn-build.toml create mode 100644 tests/fixtures/mvn_dep_tree_beacon.txt create mode 100644 tests/fixtures/mvn_dep_tree_conflicts.txt create mode 100644 tests/fixtures/mvn_dep_tree_simple.txt create mode 100644 tests/fixtures/mvn_test_fail_auth.txt create mode 100644 tests/fixtures/mvn_test_large_suite.txt create mode 100644 tests/fixtures/mvn_test_many_failures.txt create mode 100644 tests/fixtures/mvn_test_multimodule.txt create mode 100644 tests/fixtures/mvn_test_pass_large_ansi.txt create mode 100644 tests/fixtures/mvn_test_pass_mavenmcp.txt diff --git a/src/cmds/java/mod.rs b/src/cmds/java/mod.rs new file mode 100644 index 000000000..4685e68f0 --- /dev/null +++ b/src/cmds/java/mod.rs @@ -0,0 +1 @@ +automod::dir!(pub "src/cmds/java"); diff --git a/src/cmds/java/mvn_cmd.rs b/src/cmds/java/mvn_cmd.rs new file mode 100644 index 000000000..c569c6d1c --- /dev/null +++ b/src/cmds/java/mvn_cmd.rs @@ -0,0 +1,978 @@ +//! Filters Maven (`mvn`) command output — test results, build errors. +//! +//! State machine parser for `mvn test` output with states: +//! Preamble -> Testing -> Summary -> Done. +//! Strips thousands of noise lines to compact failure reports (99%+ savings). + +use crate::core::runner; +use crate::core::tracking; +use crate::core::utils::{exit_code_from_status, resolved_command, strip_ansi}; +use anyhow::{Context, Result}; +use lazy_static::lazy_static; +use regex::Regex; +use std::ffi::OsString; +use std::path::Path; + +lazy_static! { + static ref TESTS_RUN_RE: Regex = + Regex::new(r"Tests run:\s*(\d+),\s*Failures:\s*(\d+),\s*Errors:\s*(\d+),\s*Skipped:\s*(\d+)") + .unwrap(); + static ref FAILURE_HEADER_RE: Regex = + Regex::new(r"^\[ERROR\]\s+(\S+\.\S+)\s+--\s+Time elapsed:.*<<<\s+(FAILURE|ERROR)!") + .unwrap(); + static ref TOTAL_TIME_RE: Regex = + Regex::new(r"Total time:\s+(.+)") + .unwrap(); + static ref VERSION_MANAGED_RE: Regex = + Regex::new(r"\s*\(version managed from [^)]+\)") + .unwrap(); +} + +/// Auto-detect mvnw wrapper; fall back to system `mvn`. +fn mvn_command() -> std::process::Command { + if Path::new("mvnw").exists() { + resolved_command("./mvnw") + } else { + resolved_command("mvn") + } +} + +/// Run `mvn test` with state-machine filtered output. +pub fn run_test(args: &[String], verbose: u8) -> Result { + let mut cmd = mvn_command(); + cmd.arg("test"); + + for arg in args { + cmd.arg(arg); + } + + if verbose > 0 { + eprintln!("Running: mvn test {}", args.join(" ")); + } + + runner::run_filtered( + cmd, + "mvn test", + &args.join(" "), + filter_mvn_test, + runner::RunOptions::with_tee("mvn_test"), + ) +} + +/// Run `mvn build` (defaults to `package` goal) with line-filtered output. +pub fn run_build(args: &[String], verbose: u8) -> Result { + let mut cmd = mvn_command(); + + // Default to 'package' if no goal specified + if args.is_empty() { + cmd.arg("package"); + } else { + for arg in args { + cmd.arg(arg); + } + } + + if verbose > 0 { + let display = if args.is_empty() { + "package".to_string() + } else { + args.join(" ") + }; + eprintln!("Running: mvn {}", display); + } + + runner::run_filtered( + cmd, + "mvn build", + &args.join(" "), + filter_mvn_build, + runner::RunOptions::with_tee("mvn_build"), + ) +} + +/// Run `mvn dependency:tree` with filtered output — strips duplicates and boilerplate. +pub fn run_dep_tree(args: &[String], verbose: u8) -> Result { + let mut cmd = mvn_command(); + cmd.arg("dependency:tree"); + + for arg in args { + cmd.arg(arg); + } + + if verbose > 0 { + eprintln!("Running: mvn dependency:tree {}", args.join(" ")); + } + + runner::run_filtered( + cmd, + "mvn dependency:tree", + &args.join(" "), + filter_mvn_dep_tree, + runner::RunOptions::with_tee("mvn_dep_tree"), + ) +} + +/// Goals that produce filterable build output (short-lived, captured). +const BUILD_GOALS: &[&str] = &["compile", "package", "clean", "install", "verify"]; + +/// Handles mvn subcommands not matched by dedicated Clap variants. +/// Build-like goals go through `filter_mvn_build`; everything else +/// streams directly via `status()` (safe for long-running goals). +pub fn run_other(args: &[OsString], verbose: u8) -> Result { + if args.is_empty() { + anyhow::bail!("mvn: no subcommand specified"); + } + + let subcommand = args[0].to_string_lossy(); + + if verbose > 0 { + eprintln!("Running: mvn {} ...", subcommand); + } + + // Route build-like goals through the build filter + if BUILD_GOALS.contains(&subcommand.as_ref()) { + let string_args: Vec = args.iter().map(|a| a.to_string_lossy().into_owned()).collect(); + return run_build(&string_args, verbose); + } + + // Everything else: passthrough with streaming (safe for spring-boot:run etc.) + let timer = tracking::TimedExecution::start(); + + let mut cmd = mvn_command(); + for arg in args { + cmd.arg(arg); + } + + let status = cmd + .status() + .with_context(|| format!("Failed to run mvn {}", subcommand))?; + + let args_str = tracking::args_display(args); + timer.track_passthrough( + &format!("mvn {}", args_str), + &format!("rtk mvn {} (passthrough)", args_str), + ); + + Ok(exit_code_from_status(&status, "mvn")) +} + +// --------------------------------------------------------------------------- +// State machine parser for mvn test output +// --------------------------------------------------------------------------- + +const MAX_DETAIL_LINES: usize = 3; +const MAX_FAILURES_SHOWN: usize = 10; +const MAX_LINE_LENGTH: usize = 200; + +#[derive(Debug, PartialEq)] +enum TestParseState { + Preamble, + Testing, + Summary, + Done, +} + +#[derive(Default)] +struct TestCounts { + run: u32, + failures: u32, + errors: u32, + skipped: u32, +} + +enum BuildResult { + Success, + Failure, +} + +struct FailureEntry { + name: String, + details: Vec, +} + +/// Filter `mvn test` output using a state machine parser. +/// +/// States: Preamble -> Testing -> Summary -> Done +/// - Preamble: skip everything before "T E S T S" marker +/// - Testing: collect failure details from [ERROR] headers and assertion lines +/// - Summary: parse final "Tests run:" line, BUILD SUCCESS/FAILURE, Total time +/// - Done: stop at Help boilerplate +fn filter_mvn_test(output: &str) -> String { + let clean = strip_ansi(output); + let mut state = TestParseState::Preamble; + + let mut failures: Vec = Vec::new(); + let mut current_failure: Option = None; + + let mut counts = TestCounts::default(); + let mut build_result: Option = None; + let mut total_time: Option = None; + let mut found_tests_section = false; + + for line in clean.lines() { + let trimmed = line.trim(); + let stripped = strip_maven_prefix(trimmed); + + // Global transition: T E S T S marker resets to Testing from any state + // (multi-module builds emit this marker per module) + if stripped.contains("T E S T S") { + found_tests_section = true; + state = TestParseState::Testing; + continue; + } + + match state { + TestParseState::Preamble => {} + TestParseState::Testing => { + if stripped == "Results:" { + if let Some(f) = current_failure.take() { + failures.push(f); + } + state = TestParseState::Summary; + continue; + } + + if let Some(caps) = FAILURE_HEADER_RE.captures(trimmed) { + if let Some(f) = current_failure.take() { + failures.push(f); + } + let test_name = caps.get(1).map_or("", |m| m.as_str()).to_string(); + current_failure = Some(FailureEntry { + name: test_name, + details: Vec::new(), + }); + continue; + } + + if let Some(ref mut f) = current_failure { + if f.details.len() >= MAX_DETAIL_LINES { + continue; + } + if is_framework_frame(stripped) + || is_maven_boilerplate(trimmed) + || stripped.is_empty() + || (trimmed.starts_with("[ERROR]") && stripped.contains("<<<")) + { + continue; + } + f.details.push(stripped.to_string()); + } + } + TestParseState::Summary => { + if is_maven_boilerplate(trimmed) || stripped.starts_with("Failures:") { + continue; + } + + if let Some(caps) = TESTS_RUN_RE.captures(stripped) { + counts.run = caps.get(1).map_or(0, |m| m.as_str().parse().unwrap_or(0)); + counts.failures = + caps.get(2).map_or(0, |m| m.as_str().parse().unwrap_or(0)); + counts.errors = + caps.get(3).map_or(0, |m| m.as_str().parse().unwrap_or(0)); + counts.skipped = + caps.get(4).map_or(0, |m| m.as_str().parse().unwrap_or(0)); + } + + if stripped.contains("BUILD SUCCESS") { + build_result = Some(BuildResult::Success); + } else if stripped.contains("BUILD FAILURE") { + build_result = Some(BuildResult::Failure); + } + + if let Some(caps) = TOTAL_TIME_RE.captures(stripped) { + total_time = Some(caps.get(1).map_or("", |m| m.as_str()).trim().to_string()); + state = TestParseState::Done; + } + } + TestParseState::Done => break, + } + } + + if !found_tests_section { + return "mvn test: no tests run".to_string(); + } + + let time_str = total_time.as_deref().unwrap_or("?"); + let has_failures = counts.failures > 0 || counts.errors > 0; + + if !has_failures { + let passed = counts.run.saturating_sub(counts.skipped); + let mut summary = format!("mvn test: {} passed", passed); + if counts.skipped > 0 { + summary.push_str(&format!(", {} skipped", counts.skipped)); + } + summary.push_str(&format!(" ({})", time_str)); + return summary; + } + + let failed_count = counts.failures + counts.errors; + let mut result = format!("mvn test: {} run, {} failed", counts.run, failed_count); + if counts.skipped > 0 { + result.push_str(&format!(", {} skipped", counts.skipped)); + } + result.push_str(&format!(" ({})\n", time_str)); + + if let Some(ref br) = build_result { + let label = match br { + BuildResult::Success => "SUCCESS", + BuildResult::Failure => "FAILURE", + }; + result.push_str(&format!("BUILD {}\n", label)); + } + + if !failures.is_empty() { + result.push_str("\nFailures:\n"); + } + for (i, failure) in failures.iter().take(MAX_FAILURES_SHOWN).enumerate() { + result.push_str(&format!("{}. {}\n", i + 1, failure.name)); + for detail in &failure.details { + if detail.len() > MAX_LINE_LENGTH { + result.push_str(&format!(" {}...\n", &detail[..MAX_LINE_LENGTH])); + } else { + result.push_str(&format!(" {}\n", detail)); + } + } + } + if failures.len() > MAX_FAILURES_SHOWN { + result.push_str(&format!( + "\n... +{} more failures\n", + failures.len() - MAX_FAILURES_SHOWN + )); + } + + result.trim().to_string() +} + +/// Strip [INFO], [ERROR], [WARNING] prefixes from Maven output lines. +fn strip_maven_prefix(line: &str) -> &str { + let trimmed = line.trim(); + for tag in &["[INFO]", "[ERROR]", "[WARNING]"] { + if let Some(rest) = trimmed.strip_prefix(tag) { + return rest.trim_start(); + } + } + trimmed +} + +/// Returns true for Java framework stack frames that should be stripped. +/// Expects pre-trimmed input (callers pass `stripped` or `trimmed`). +fn is_framework_frame(line: &str) -> bool { + let check = line.strip_prefix("at ").unwrap_or(line); + + const FRAMEWORK_PREFIXES: &[&str] = &[ + "org.apache.maven.", + "org.junit.platform.", + "org.junit.jupiter.", + "org.codehaus.plexus.", + "java.base/", + "sun.reflect.", + "jdk.internal.", + ]; + + for prefix in FRAMEWORK_PREFIXES { + if check.starts_with(prefix) { + return true; + } + } + + // "... N more" truncation markers + line.starts_with("...") && line.contains("more") +} + +/// Returns true for Maven boilerplate lines that should be stripped. +/// Expects pre-trimmed input from callers. +fn is_maven_boilerplate(line: &str) -> bool { + // Empty [ERROR] or [INFO] lines + if line == "[ERROR]" || line == "[INFO]" || line == "[WARNING]" { + return true; + } + + let stripped = strip_maven_prefix(line); + + // Separator lines (dashes) + if stripped.starts_with("---") && stripped.chars().all(|c| c == '-' || c.is_whitespace()) { + return true; + } + + const BOILERPLATE_PATTERNS: &[&str] = &[ + "-> [Help", + "http://cwiki.apache.org", + "https://cwiki.apache.org", + "surefire-reports", + "Re-run Maven", + "re-run Maven", + "full stack trace", + "enable verbose output", + "See dump files", + "Failed to execute goal", + "There are test failures", + ]; + + for pattern in BOILERPLATE_PATTERNS { + if stripped.contains(pattern) { + return true; + } + } + + false +} + +// --------------------------------------------------------------------------- +// Line filter for mvn build output +// --------------------------------------------------------------------------- + +/// Filter `mvn build`/`mvn package` output — strip [INFO] noise, keep errors and summary. +fn filter_mvn_build(output: &str) -> String { + let clean = strip_ansi(output); + let mut result_lines: Vec = Vec::new(); + + for line in clean.lines() { + let trimmed = line.trim(); + if should_keep_build_line(trimmed) { + result_lines.push(trimmed.to_string()); + } + } + + if result_lines.is_empty() { + return "mvn: ok".to_string(); + } + + result_lines.join("\n") +} + +const INFO_NOISE_PATTERNS: &[&str] = &[ + "---", + "Building ", + "Downloading ", + "Downloaded ", + "Scanning ", + "Compiling ", + "Recompiling ", + "Nothing to compile", + "Using auto detected", + "Loaded ", + "Creating container", + "Container ", + "Image ", + "Testcontainers", + "Docker ", + "Ryuk ", + "Checking the system", + "Connected to docker", + "Running ", + "Tests run:", + "Results:", + "T E S T S", + "Finished at:", + "from pom.xml", +]; + +/// Returns true if a build output line should be kept. +fn should_keep_build_line(line: &str) -> bool { + let trimmed = line.trim(); + + if trimmed.is_empty() { + return false; + } + + let stripped = strip_maven_prefix(trimmed); + + // Keep error lines + if trimmed.starts_with("[ERROR]") { + return !is_maven_boilerplate(trimmed); + } + + // Keep BUILD SUCCESS/FAILURE + if stripped.contains("BUILD SUCCESS") || stripped.contains("BUILD FAILURE") { + return true; + } + + // Keep Total time + if TOTAL_TIME_RE.is_match(stripped) { + return true; + } + + // Strip [INFO] noise + if trimmed.starts_with("[INFO]") { + if stripped.is_empty() { + return false; + } + + if stripped.starts_with("---") && stripped.chars().all(|c| c == '-' || c.is_whitespace()) { + return false; + } + + for pattern in INFO_NOISE_PATTERNS { + if stripped.contains(pattern) { + return false; + } + } + + if stripped.contains("deprecated") || stripped.contains("WARNING") { + return false; + } + + return true; + } + + // Strip [WARNING] lines for build filter + if trimmed.starts_with("[WARNING]") { + return false; + } + + // Keep anything else (compilation errors without prefix, etc.) + true +} + +// --------------------------------------------------------------------------- +// Line filter for mvn dependency:tree output +// --------------------------------------------------------------------------- + +/// Filter `mvn dependency:tree` — strip Maven boilerplate, omitted duplicates, +/// and "version managed" annotations. Keep tree structure and conflicts. +fn filter_mvn_dep_tree(output: &str) -> String { + let clean = strip_ansi(output); + let mut result_lines: Vec = Vec::new(); + + for line in clean.lines() { + let trimmed = line.trim(); + + // Skip empty lines and Maven boilerplate + if trimmed.is_empty() || is_maven_boilerplate(trimmed) { + continue; + } + + let stripped = strip_maven_prefix(trimmed); + + // Skip non-tree Maven lines (Scanning, Building, separators, etc.) + if trimmed.starts_with("[WARNING]") { + continue; + } + if trimmed.starts_with("[INFO]") { + if stripped.is_empty() { + continue; + } + // Skip separator lines + if stripped.starts_with("---") + && stripped.chars().all(|c| c == '-' || c.is_whitespace()) + { + continue; + } + // Skip preamble noise + if stripped.starts_with("Scanning ") + || stripped.starts_with("Building ") + || stripped.starts_with("Loaded ") + || stripped.contains("from pom.xml") + || stripped.contains("BUILD SUCCESS") + || stripped.contains("BUILD FAILURE") + || TOTAL_TIME_RE.is_match(stripped) + || stripped.starts_with("Finished at:") + { + continue; + } + } + + // Skip lines with "omitted for duplicate" + if stripped.contains("omitted for duplicate") { + continue; + } + + // Clean "version managed" annotations from kept lines + let cleaned = VERSION_MANAGED_RE.replace_all(stripped, "").to_string(); + + result_lines.push(cleaned); + } + + if result_lines.is_empty() { + return "mvn dependency:tree: no output".to_string(); + } + + result_lines.join("\n") +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::core::utils::count_tokens; + + #[test] + fn test_filter_pass_output() { + let input = include_str!("../../../tests/fixtures/mvn_test_pass_mavenmcp.txt"); + let output = filter_mvn_test(input); + assert!( + output.contains("mvn test:"), + "should contain summary prefix" + ); + assert!(output.contains("183 passed"), "should show 183 passed"); + assert!(output.contains("4.748 s"), "should contain total time"); + assert!( + !output.contains("[INFO]"), + "should not contain raw [INFO] prefix" + ); + } + + #[test] + fn test_filter_fail_output() { + let input = include_str!("../../../tests/fixtures/mvn_test_fail_auth.txt"); + let output = filter_mvn_test(input); + assert!( + output.contains("5 run, 2 failed"), + "should show run/failed counts, got: {}", + output + ); + assert!(output.contains("23.819 s"), "should contain total time"); + assert!( + output.contains("EmailParserTest.should_extract_domain_from_email"), + "should list first failure" + ); + assert!( + output.contains("ScoreTypeTest.shouldMapToRole"), + "should list second failure" + ); + assert!( + output.contains("broken.example.com"), + "should include assertion details" + ); + assert!( + !output.contains("surefire-reports"), + "should strip boilerplate" + ); + assert!( + !output.contains("cwiki.apache.org"), + "should strip help links" + ); + } + + #[test] + fn test_pass_savings() { + let input = include_str!("../../../tests/fixtures/mvn_test_pass_mavenmcp.txt"); + let output = filter_mvn_test(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 90.0, + "mvn test pass: expected >=90% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_fail_savings() { + let input = include_str!("../../../tests/fixtures/mvn_test_fail_auth.txt"); + let output = filter_mvn_test(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 60.0, + "mvn test fail: expected >=60% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_filter_large_suite() { + let input = include_str!("../../../tests/fixtures/mvn_test_large_suite.txt"); + let output = filter_mvn_test(input); + assert!( + output.contains("3262 run, 23 failed"), + "should show run/failed counts, got: {}", + output + ); + assert!( + output.contains("+13 more failures"), + "should cap at 10 and show remaining" + ); + assert!( + output.contains("SearchReadModelTest"), + "should list assertion failures" + ); + assert!( + output.contains("PatchableFieldTest"), + "should list compilation errors" + ); + } + + #[test] + fn test_large_suite_savings() { + let input = include_str!("../../../tests/fixtures/mvn_test_large_suite.txt"); + let output = filter_mvn_test(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 60.0, + "mvn test large suite: expected >=60% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_empty_input() { + let output = filter_mvn_test(""); + assert_eq!(output, "mvn test: no tests run"); + } + + #[test] + fn test_filter_many_failures_output() { + let input = include_str!("../../../tests/fixtures/mvn_test_many_failures.txt"); + let output = filter_mvn_test(input); + assert!( + output.contains("28 run, 28 failed"), + "should show total run/failed counts, got: {}", + output + ); + assert!( + output.contains("+4 more failures"), + "should cap at 10 and show remaining count" + ); + } + + #[test] + fn test_many_failures_savings() { + let input = include_str!("../../../tests/fixtures/mvn_test_many_failures.txt"); + let output = filter_mvn_test(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 60.0, + "mvn test many failures: expected >=60% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_filter_multimodule_output() { + let input = include_str!("../../../tests/fixtures/mvn_test_multimodule.txt"); + let output = filter_mvn_test(input); + assert!( + output.contains("860 run, 4 failed"), + "should show total run/failed across modules, got: {}", + output + ); + assert!( + output.contains("GitDiffReaderTest.shouldBuildDiff"), + "should list failure from services module" + ); + assert!( + output.contains("ServiceUnavailableException"), + "should include error details" + ); + assert!( + output.contains("01:31 min"), + "should contain total time" + ); + } + + #[test] + fn test_multimodule_savings() { + let input = include_str!("../../../tests/fixtures/mvn_test_multimodule.txt"); + let output = filter_mvn_test(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 60.0, + "mvn test multimodule: expected >=60% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_filter_pass_large_ansi() { + let input = include_str!("../../../tests/fixtures/mvn_test_pass_large_ansi.txt"); + let output = filter_mvn_test(input); + assert!( + output.contains("950 passed"), + "should show 950 passed (959-9 skipped), got: {}", + output + ); + assert!( + output.contains("9 skipped"), + "should show 9 skipped" + ); + assert!( + output.contains("01:32 min"), + "should contain total time" + ); + assert!( + !output.contains("PortUnreachableException"), + "should strip app log noise" + ); + assert!( + !output.contains("[stdout]"), + "should strip [stdout] lines" + ); + assert!( + !output.contains("liquibase"), + "should strip liquibase stderr" + ); + } + + #[test] + fn test_pass_large_ansi_savings() { + let input = include_str!("../../../tests/fixtures/mvn_test_pass_large_ansi.txt"); + let output = filter_mvn_test(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 95.0, + "mvn test large ANSI pass: expected >=95% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_no_test_section() { + let input = "[INFO] Building my-project 1.0\n[INFO] BUILD SUCCESS\n"; + let output = filter_mvn_test(input); + assert_eq!(output, "mvn test: no tests run"); + } + + // --- dependency:tree tests --- + + #[test] + fn test_dep_tree_simple() { + let input = include_str!("../../../tests/fixtures/mvn_dep_tree_simple.txt"); + let output = filter_mvn_dep_tree(input); + assert!( + output.contains("com.example:my-app:jar:1.0.0"), + "should contain root artifact, got: {}", + output + ); + assert!( + output.contains("slf4j-api"), + "should contain direct dep" + ); + assert!( + output.contains("guava"), + "should contain guava" + ); + assert!( + !output.contains("[INFO]"), + "should strip [INFO] prefix" + ); + assert!( + !output.contains("BUILD SUCCESS"), + "should strip boilerplate" + ); + assert!( + !output.contains("Scanning"), + "should strip preamble" + ); + } + + #[test] + fn test_dep_tree_conflicts() { + let input = include_str!("../../../tests/fixtures/mvn_dep_tree_conflicts.txt"); + let output = filter_mvn_dep_tree(input); + assert!( + output.contains("omitted for conflict with 2.18.3"), + "should keep conflict info, got: {}", + output + ); + assert!( + !output.contains("BUILD SUCCESS"), + "should strip boilerplate" + ); + } + + #[test] + fn test_dep_tree_beacon_strips_duplicates() { + let input = include_str!("../../../tests/fixtures/mvn_dep_tree_beacon.txt"); + let output = filter_mvn_dep_tree(input); + assert!( + !output.contains("omitted for duplicate"), + "should strip all 'omitted for duplicate' lines" + ); + assert!( + output.contains("com.skillpanel:beacon"), + "should contain root artifact" + ); + assert!( + output.contains("spring-boot-starter-web"), + "should contain direct deps" + ); + } + + #[test] + fn test_dep_tree_beacon_cleans_version_managed() { + let input = include_str!("../../../tests/fixtures/mvn_dep_tree_beacon.txt"); + let output = filter_mvn_dep_tree(input); + assert!( + !output.contains("version managed from"), + "should strip 'version managed' annotations" + ); + } + + #[test] + fn test_dep_tree_beacon_savings() { + let input = include_str!("../../../tests/fixtures/mvn_dep_tree_beacon.txt"); + let output = filter_mvn_dep_tree(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 60.0, + "mvn dep tree beacon: expected >=60% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_dep_tree_simple_savings() { + let input = include_str!("../../../tests/fixtures/mvn_dep_tree_simple.txt"); + let output = filter_mvn_dep_tree(input); + + let input_tokens = count_tokens(input); + let output_tokens = count_tokens(&output); + let savings = 100.0 - (output_tokens as f64 / input_tokens as f64 * 100.0); + + assert!( + savings >= 30.0, + "mvn dep tree simple: expected >=30% savings, got {:.1}% ({} -> {} tokens)", + savings, + input_tokens, + output_tokens, + ); + } + + #[test] + fn test_dep_tree_empty() { + let output = filter_mvn_dep_tree(""); + assert_eq!(output, "mvn dependency:tree: no output"); + } +} diff --git a/src/cmds/mod.rs b/src/cmds/mod.rs index 1eca0b84c..0f25bd8de 100644 --- a/src/cmds/mod.rs +++ b/src/cmds/mod.rs @@ -4,6 +4,7 @@ pub mod cloud; pub mod dotnet; pub mod git; pub mod go; +pub mod java; pub mod js; pub mod python; pub mod ruby; diff --git a/src/core/toml_filter.rs b/src/core/toml_filter.rs index bd294f8af..5c96b0165 100644 --- a/src/core/toml_filter.rs +++ b/src/core/toml_filter.rs @@ -1574,7 +1574,6 @@ match_command = "^make\\b" "markdownlint", "mix-compile", "mix-format", - "mvn-build", "ping", "pio-run", "poetry-install", @@ -1613,8 +1612,8 @@ match_command = "^make\\b" let filters = make_filters(BUILTIN_TOML); assert_eq!( filters.len(), - 58, - "Expected exactly 58 built-in filters, got {}. \ + 57, + "Expected exactly 57 built-in filters, got {}. \ Update this count when adding/removing filters in src/filters/.", filters.len() ); @@ -1671,11 +1670,11 @@ expected = "output line 1\noutput line 2" let combined = format!("{}\n\n{}", BUILTIN_TOML, new_filter); let filters = make_filters(&combined); - // All 58 existing filters still present + 1 new = 59 + // All 57 existing filters still present + 1 new = 58 assert_eq!( filters.len(), - 59, - "Expected 59 filters after concat (58 built-in + 1 new)" + 58, + "Expected 58 filters after concat (57 built-in + 1 new)" ); // New filter is discoverable diff --git a/src/discover/rules.rs b/src/discover/rules.rs index b315edd77..def475eb2 100644 --- a/src/discover/rules.rs +++ b/src/discover/rules.rs @@ -486,11 +486,11 @@ pub const RULES: &[RtkRule] = &[ subcmd_status: &[], }, RtkRule { - pattern: r"^mvn\s+(compile|package|clean|install)\b", + pattern: r"^(\.\/?)?mvnw?\s+(test|compile|package|clean|install|dependency:tree)\b", rtk_cmd: "rtk mvn", - rewrite_prefixes: &["mvn"], + rewrite_prefixes: &["mvn", "mvnw", "./mvnw"], category: "Build", - savings_pct: 70.0, + savings_pct: 90.0, subcmd_savings: &[], subcmd_status: &[], }, diff --git a/src/filters/mvn-build.toml b/src/filters/mvn-build.toml deleted file mode 100644 index 430a7f0cd..000000000 --- a/src/filters/mvn-build.toml +++ /dev/null @@ -1,44 +0,0 @@ -[filters.mvn-build] -description = "Compact Maven build output" -match_command = "^mvn\\s+(compile|package|clean|install)\\b" -strip_ansi = true -strip_lines_matching = [ - "^\\[INFO\\] ---", - "^\\[INFO\\] Building\\s", - "^\\[INFO\\] Downloading\\s", - "^\\[INFO\\] Downloaded\\s", - "^\\[INFO\\]\\s*$", - "^\\s*$", - "^Downloading:", - "^Downloaded:", - "^Progress", -] -max_lines = 50 -on_empty = "mvn: ok" - -[[tests.mvn-build]] -name = "strips INFO noise, preserves errors and summary" -input = """ -[INFO] --- -[INFO] Building myapp 1.0-SNAPSHOT -[INFO] Downloading org.apache.maven.plugins:maven-compiler-plugin:3.11.0 -[INFO] Downloaded org.apache.maven.plugins:maven-compiler-plugin:3.11.0 -[INFO] -[ERROR] /src/main/java/Main.java:[10,5] cannot find symbol - symbol: method foo() -[INFO] BUILD FAILURE -[INFO] Total time: 2.543 s -""" -expected = "[ERROR] /src/main/java/Main.java:[10,5] cannot find symbol\n symbol: method foo()\n[INFO] BUILD FAILURE\n[INFO] Total time: 2.543 s" - -[[tests.mvn-build]] -name = "successful build keeps BUILD SUCCESS line" -input = """ -[INFO] --- -[INFO] Building myapp 1.0-SNAPSHOT -[INFO] -[INFO] BUILD SUCCESS -[INFO] Total time: 4.123 s -[INFO] Finished at: 2024-01-15T10:30:00Z -""" -expected = "[INFO] BUILD SUCCESS\n[INFO] Total time: 4.123 s\n[INFO] Finished at: 2024-01-15T10:30:00Z" diff --git a/src/main.rs b/src/main.rs index 1954ed8ea..06f2e9f3e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -11,6 +11,7 @@ use cmds::cloud::{aws_cmd, container, curl_cmd, psql_cmd, wget_cmd}; use cmds::dotnet::{binlog, dotnet_cmd, dotnet_format_report, dotnet_trx}; use cmds::git::{diff_cmd, gh_cmd, git, gt_cmd}; use cmds::go::{go_cmd, golangci_cmd}; +use cmds::java::mvn_cmd; use cmds::js::{ lint_cmd, next_cmd, npm_cmd, playwright_cmd, pnpm_cmd, prettier_cmd, prisma_cmd, tsc_cmd, vitest_cmd, @@ -642,6 +643,12 @@ enum Commands { command: GoCommands, }, + /// Maven commands with compact output + Mvn { + #[command(subcommand)] + command: MvnCommands, + }, + /// Graphite (gt) stacked PR commands with compact output Gt { #[command(subcommand)] @@ -1033,6 +1040,29 @@ enum GoCommands { Other(Vec), } +#[derive(Subcommand)] +enum MvnCommands { + /// Run tests with compact output (state machine parser, 99%+ token reduction) + Test { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + /// Build with compact output (strip noise, keep errors and summary) + Build { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + /// Dependency tree with compact output (strip duplicates and boilerplate) + #[command(name = "dependency:tree")] + DepTree { + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + /// Passthrough: runs any unsupported mvn subcommand directly + #[command(external_subcommand)] + Other(Vec), +} + /// RTK-only subcommands that should never fall back to raw execution. /// If Clap fails to parse these, show the Clap error directly. const RTK_META_COMMANDS: &[&str] = &[ @@ -1917,6 +1947,13 @@ fn run_cli() -> Result { GoCommands::Other(args) => go_cmd::run_other(&args, cli.verbose)?, }, + Commands::Mvn { command } => match command { + MvnCommands::Test { args } => mvn_cmd::run_test(&args, cli.verbose)?, + MvnCommands::Build { args } => mvn_cmd::run_build(&args, cli.verbose)?, + MvnCommands::DepTree { args } => mvn_cmd::run_dep_tree(&args, cli.verbose)?, + MvnCommands::Other(args) => mvn_cmd::run_other(&args, cli.verbose)?, + }, + Commands::Gt { command } => match command { GtCommands::Log { args } => gt_cmd::run_log(&args, cli.verbose)?, GtCommands::Submit { args } => gt_cmd::run_submit(&args, cli.verbose)?, @@ -2202,6 +2239,7 @@ fn is_operational_command(cmd: &Commands) -> bool { | Commands::Rspec { .. } | Commands::Pip { .. } | Commands::Go { .. } + | Commands::Mvn { .. } | Commands::GolangciLint { .. } | Commands::Gt { .. } ) diff --git a/tests/fixtures/mvn_dep_tree_beacon.txt b/tests/fixtures/mvn_dep_tree_beacon.txt new file mode 100644 index 000000000..6d3f6d58b --- /dev/null +++ b/tests/fixtures/mvn_dep_tree_beacon.txt @@ -0,0 +1,652 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] -----------------------< com.skillpanel:beacon >------------------------ +[INFO] Building beacon 1.0-SNAPSHOT +[INFO] from pom.xml +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- dependency:3.9.0:tree (default-cli) @ beacon --- +[INFO] com.skillpanel:beacon:jar:1.0-SNAPSHOT +[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:4.0.3:compile +[INFO] | +- org.springframework.boot:spring-boot-starter-jackson:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- org.springframework.boot:spring-boot-jackson:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (tools.jackson.core:jackson-databind:jar:3.0.4:compile - version managed from 3.0.4; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- org.springframework.boot:spring-boot-starter-tomcat-runtime:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework.boot:spring-boot-tomcat:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework.boot:spring-boot-web-server:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | +- (jakarta.annotation:jakarta.annotation-api:jar:3.0.0:compile - version managed from 3.0.0; omitted for duplicate) +[INFO] | | | +- org.apache.tomcat.embed:tomcat-embed-core:jar:11.0.18:compile (version managed from 11.0.18) +[INFO] | | | +- (org.apache.tomcat.embed:tomcat-embed-el:jar:11.0.18:compile - version managed from 11.0.18; omitted for duplicate) +[INFO] | | | \- org.apache.tomcat.embed:tomcat-embed-websocket:jar:11.0.18:compile (version managed from 11.0.18) +[INFO] | | | \- (org.apache.tomcat.embed:tomcat-embed-core:jar:11.0.18:compile - version managed from 11.0.18; omitted for duplicate) +[INFO] | | \- org.springframework.boot:spring-boot-tomcat:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-web-server:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.apache.tomcat.embed:tomcat-embed-core:jar:11.0.18:compile - version managed from 11.0.18; omitted for duplicate) +[INFO] | | \- (jakarta.annotation:jakarta.annotation-api:jar:3.0.0:runtime - version managed from 3.0.0; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-http-converter:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | +- org.springframework.boot:spring-boot:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- org.springframework:spring-web:jar:7.0.5:compile (version managed from 7.0.5; scope not updated to compile) +[INFO] | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-webmvc:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | +- org.springframework.boot:spring-boot-servlet:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | +- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | +- org.springframework:spring-webmvc:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-expression:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | \- (org.springframework.boot:spring-boot-http-converter:jar:4.0.3:runtime - version managed from 4.0.3; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-webflux:jar:4.0.3:compile +[INFO] | +- org.springframework.boot:spring-boot-starter:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- ch.qos.logback:logback-classic:jar:1.5.32:compile (version managed from 1.5.32) +[INFO] | | | | +- ch.qos.logback:logback-core:jar:1.5.32:compile (version managed from 1.5.32) +[INFO] | | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.25.3:compile (version managed from 2.25.3) +[INFO] | | | | +- org.apache.logging.log4j:log4j-api:jar:2.25.3:compile (version managed from 2.25.3) +[INFO] | | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | | | \- org.slf4j:jul-to-slf4j:jar:2.0.17:compile (version managed from 2.0.17) +[INFO] | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-autoconfigure:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- jakarta.annotation:jakarta.annotation-api:jar:3.0.0:compile (version managed from 3.0.0; scope not updated to compile) +[INFO] | | \- (org.yaml:snakeyaml:jar:2.5:compile - version managed from 2.5; omitted for duplicate) +[INFO] | +- (org.springframework.boot:spring-boot-starter-jackson:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- org.springframework.boot:spring-boot-reactor:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | \- (io.projectreactor:reactor-core:jar:3.8.3:compile - version managed from 3.8.3; omitted for duplicate) +[INFO] | | \- org.springframework.boot:spring-boot-reactor-netty:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-web-server:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- io.projectreactor.netty:reactor-netty-http:jar:1.3.3:compile (version managed from 1.3.3) +[INFO] | | | +- io.netty:netty-codec-http:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-compression:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-codec-http2:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- (io.netty:netty-codec-http:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-codec-http3:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-http:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-compression:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport-native-unix-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-resolver:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-codec-classes-quic:jar:4.2.10.Final:compile (version managed from 4.2.10.Final; scope not updated to compile) +[INFO] | | | | +- io.netty:netty-codec-native-quic:jar:linux-x86_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from runtime) +[INFO] | | | | | \- (io.netty:netty-codec-classes-quic:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-codec-native-quic:jar:linux-aarch_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from runtime) +[INFO] | | | | | \- (io.netty:netty-codec-classes-quic:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-codec-native-quic:jar:osx-x86_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from runtime) +[INFO] | | | | | \- (io.netty:netty-codec-classes-quic:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-codec-native-quic:jar:osx-aarch_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from runtime) +[INFO] | | | | | \- (io.netty:netty-codec-classes-quic:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- io.netty:netty-codec-native-quic:jar:windows-x86_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from runtime) +[INFO] | | | | \- (io.netty:netty-codec-classes-quic:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-resolver-dns:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-resolver-dns-native-macos:jar:osx-x86_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from compile) +[INFO] | | | | \- io.netty:netty-resolver-dns-classes-macos:jar:4.2.10.Final:runtime (version managed from 4.2.10.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-resolver-dns:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- (io.netty:netty-transport-native-unix-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.2.10.Final:runtime (version managed from 4.2.10.Final; scope managed from compile) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport-native-unix-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- io.netty:netty-transport-classes-epoll:jar:4.2.10.Final:runtime (version managed from 4.2.10.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- (io.netty:netty-transport-native-unix-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.projectreactor.netty:reactor-netty-core:jar:1.3.3:compile (version managed from 1.3.3) +[INFO] | | | | +- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-handler-proxy:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- io.netty:netty-codec-socks:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | | \- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-codec-http:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | \- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-resolver-dns:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-resolver-dns-native-macos:jar:osx-x86_64:4.2.10.Final:runtime - version managed from 4.2.10.Final; scope managed from compile; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.2.10.Final:runtime - version managed from 4.2.10.Final; scope managed from compile; omitted for duplicate) +[INFO] | | | | +- (io.projectreactor:reactor-core:jar:3.8.3:compile - version managed from 3.8.3; omitted for duplicate) +[INFO] | | | | \- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | | | +- (io.projectreactor:reactor-core:jar:3.8.3:compile - version managed from 3.8.3; omitted for duplicate) +[INFO] | | | \- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (org.springframework.boot:spring-boot-netty:jar:4.0.3:runtime - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-webflux:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-http-codec:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | +- org.springframework:spring-webflux:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (io.projectreactor:reactor-core:jar:3.8.3:compile - version managed from 3.8.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-web-server:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-data-jpa:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-starter-jdbc:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-jdbc:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- com.zaxxer:HikariCP:jar:7.0.2:compile (version managed from 7.0.2) +[INFO] | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-data-jpa:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- org.springframework.boot:spring-boot-data-commons:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | +- org.springframework.boot:spring-boot-persistence:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | | \- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- org.springframework.data:spring-data-commons:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | | +- org.springframework.boot:spring-boot-hibernate:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- org.springframework.boot:spring-boot-jpa:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | | +- (org.springframework.boot:spring-boot-jdbc:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | | +- (org.springframework.boot:spring-boot-transaction:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | | +- jakarta.persistence:jakarta.persistence-api:jar:3.2.0:compile (version managed from 3.2.0) +[INFO] | | | | \- (org.springframework:spring-orm:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- org.hibernate.orm:hibernate-core:jar:7.2.4.Final:compile (version managed from 7.2.4.Final) +[INFO] | | | | +- (jakarta.persistence:jakarta.persistence-api:jar:3.2.0:compile - version managed from 3.2.0; omitted for duplicate) +[INFO] | | | | +- jakarta.transaction:jakarta.transaction-api:jar:2.0.1:compile (version managed from 2.0.1) +[INFO] | | | | +- (org.jboss.logging:jboss-logging:jar:3.6.2.Final:runtime - version managed from 3.6.1.Final; omitted for duplicate) +[INFO] | | | | +- org.hibernate.models:hibernate-models:jar:1.0.1:runtime +[INFO] | | | | | \- (org.jboss.logging:jboss-logging:jar:3.6.2.Final:runtime - version managed from 3.5.0.Final; omitted for duplicate) +[INFO] | | | | +- (com.fasterxml:classmate:jar:1.7.3:runtime - version managed from 1.7.1; omitted for duplicate) +[INFO] | | | | +- (net.bytebuddy:byte-buddy:jar:1.17.8:runtime - version managed from 1.17.8; omitted for duplicate) +[INFO] | | | | +- (jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.4:runtime - version managed from 4.0.4; omitted for duplicate) +[INFO] | | | | +- org.glassfish.jaxb:jaxb-runtime:jar:4.0.6:runtime (version managed from 4.0.6) +[INFO] | | | | | \- org.glassfish.jaxb:jaxb-core:jar:4.0.6:runtime (version managed from 4.0.6) +[INFO] | | | | | +- (jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.4:runtime - version managed from 4.0.4; omitted for duplicate) +[INFO] | | | | | +- (jakarta.activation:jakarta.activation-api:jar:2.1.4:runtime - version managed from 2.1.4; omitted for duplicate) +[INFO] | | | | | +- org.eclipse.angus:angus-activation:jar:2.0.3:runtime +[INFO] | | | | | | \- (jakarta.activation:jakarta.activation-api:jar:2.1.4:runtime - version managed from 2.1.4; omitted for duplicate) +[INFO] | | | | | +- org.glassfish.jaxb:txw2:jar:4.0.6:runtime (version managed from 4.0.6) +[INFO] | | | | | \- com.sun.istack:istack-commons-runtime:jar:4.1.2:runtime +[INFO] | | | | +- jakarta.inject:jakarta.inject-api:jar:2.0.1:runtime (version managed from 2.0.1) +[INFO] | | | | \- (org.antlr:antlr4-runtime:jar:4.13.2:runtime - omitted for duplicate) +[INFO] | | | \- org.springframework:spring-orm:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-jdbc:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | +- org.springframework.data:spring-data-jpa:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework.data:spring-data-commons:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-orm:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- org.antlr:antlr4-runtime:jar:4.13.2:compile +[INFO] | | | +- (jakarta.annotation:jakarta.annotation-api:jar:3.0.0:compile - version managed from 2.0.0; omitted for duplicate) +[INFO] | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | | \- org.springframework:spring-aspects:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | | \- org.aspectj:aspectjweaver:jar:1.9.25.1:compile (version managed from 1.9.25) +[INFO] | \- org.springframework.boot:spring-boot-jdbc:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-sql:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | \- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-transaction:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-persistence:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | \- org.springframework:spring-jdbc:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | \- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-data-redis:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-data-redis:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- (org.springframework.boot:spring-boot-data-commons:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- (org.springframework.boot:spring-boot-transaction:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- io.lettuce:lettuce-core:jar:6.8.2.RELEASE:compile (version managed from 6.8.2.RELEASE) +[INFO] | | +- redis.clients.authentication:redis-authx-core:jar:0.1.1-beta2:compile +[INFO] | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 1.7.36; omitted for duplicate) +[INFO] | | +- io.netty:netty-common:jar:4.2.10.Final:compile (version managed from 4.1.125.Final) +[INFO] | | +- io.netty:netty-handler:jar:4.2.10.Final:compile (version managed from 4.1.125.Final) +[INFO] | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-resolver:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | \- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-buffer:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | \- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-transport-native-unix-common:jar:4.2.10.Final:compile (version managed from 4.2.10.Final; scope not updated to compile) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | \- io.netty:netty-codec-base:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | \- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- io.netty:netty-transport:jar:4.2.10.Final:compile (version managed from 4.1.125.Final) +[INFO] | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | \- (io.netty:netty-resolver:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- (io.projectreactor:reactor-core:jar:3.8.3:compile - version managed from 3.6.6; omitted for duplicate) +[INFO] | | \- io.netty:netty-resolver-dns:jar:4.2.10.Final:compile (version managed from 4.1.125.Final; scope not updated to compile) +[INFO] | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- (io.netty:netty-resolver:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | +- io.netty:netty-codec-dns:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | \- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | \- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | +- org.springframework.data:spring-data-redis:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- org.springframework.data:spring-data-keyvalue:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework.data:spring-data-commons:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-tx:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- org.springframework:spring-oxm:jar:7.0.5:compile (version managed from 7.0.3) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- (jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.4:runtime - version managed from 3.0.1; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- org.springframework:spring-context-support:jar:7.0.5:compile (version managed from 7.0.3) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 2.0.17; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-netty:jar:4.0.3:runtime (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:runtime - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- (io.netty:netty-common:jar:4.2.10.Final:runtime - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-validation:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-validation:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.apache.tomcat.embed:tomcat-embed-el:jar:11.0.18:compile (version managed from 11.0.18) +[INFO] | \- org.hibernate.validator:hibernate-validator:jar:9.0.1.Final:compile (version managed from 9.0.1.Final) +[INFO] | +- jakarta.validation:jakarta.validation-api:jar:3.1.1:compile (version managed from 3.1.1) +[INFO] | +- org.jboss.logging:jboss-logging:jar:3.6.2.Final:compile (version managed from 3.6.1.Final; scope not updated to compile) +[INFO] | \- com.fasterxml:classmate:jar:1.7.3:compile (version managed from 1.7.0; scope not updated to compile) +[INFO] +- org.springframework.boot:spring-boot-starter-actuator:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-starter-micrometer-metrics:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- org.springframework.boot:spring-boot-micrometer-metrics:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- org.springframework.boot:spring-boot-micrometer-observation:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | | \- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | | \- (io.micrometer:micrometer-core:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-actuator-autoconfigure:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-autoconfigure:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- org.springframework.boot:spring-boot-actuator:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | \- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-health:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | \- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- io.micrometer:micrometer-observation:jar:1.16.3:compile (version managed from 1.16.3) +[INFO] | | +- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | | \- io.micrometer:micrometer-commons:jar:1.16.3:compile (version managed from 1.16.3) +[INFO] | | \- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | \- io.micrometer:micrometer-jakarta9:jar:1.16.3:compile (version managed from 1.16.3) +[INFO] | +- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | +- io.micrometer:micrometer-core:jar:1.16.3:compile (version managed from 1.16.3) +[INFO] | | +- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | | +- (io.micrometer:micrometer-commons:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | | +- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | | +- org.hdrhistogram:HdrHistogram:jar:2.2.2:runtime +[INFO] | | \- org.latencyutils:LatencyUtils:jar:2.0.3:runtime +[INFO] | +- (io.micrometer:micrometer-commons:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | \- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-security:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-security:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- org.springframework.security:spring-security-config:jar:7.0.3:compile (version managed from 7.0.3) +[INFO] | | | +- (org.springframework.security:spring-security-core:jar:7.0.3:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | \- (org.springframework.security:spring-security-web:jar:7.0.3:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | \- org.springframework:spring-aop:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | +- org.springframework:spring-beans:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] +- com.devskiller:toolkit:jar:1.3-38:compile +[INFO] | +- org.slf4j:slf4j-api:jar:2.0.17:compile (version managed from 2.0.7) +[INFO] | +- (com.nimbusds:nimbus-jose-jwt:jar:10.5:compile - omitted for conflict with 9.37) +[INFO] | +- org.bouncycastle:bcprov-jdk18on:jar:1.82:compile +[INFO] | +- org.bouncycastle:bcpkix-jdk18on:jar:1.82:compile +[INFO] | | \- org.bouncycastle:bcutil-jdk18on:jar:1.82:compile +[INFO] | | \- (org.bouncycastle:bcprov-jdk18on:jar:1.82:compile - omitted for duplicate) +[INFO] | +- com.courier:courier-java:jar:3.3.0:compile +[INFO] | | +- (com.squareup.okhttp3:okhttp:jar:4.12.0:compile - omitted for conflict with 5.0.0-alpha.14) +[INFO] | | +- (com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile - version managed from 2.13.0; omitted for duplicate) +[INFO] | | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.20.2:compile (version managed from 2.12.3) +[INFO] | | | +- (com.fasterxml.jackson.core:jackson-core:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] | | | \- (com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] | | \- (com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.20.2:compile - version managed from 2.12.3; omitted for duplicate) +[INFO] | +- (com.devskiller.friendly-id:friendly-id:jar:1.1.0:compile - omitted for conflict with 2.0.0-SNAPSHOT) +[INFO] | +- org.apache.commons:commons-lang3:jar:3.19.0:compile (version managed from 3.13.0; scope not updated to compile) +[INFO] | +- jakarta.servlet:jakarta.servlet-api:jar:6.1.0:compile (version managed from 6.0.0) +[INFO] | +- com.github.ulisesbocchio:jasypt-spring-boot-starter:jar:3.0.5:compile +[INFO] | | \- com.github.ulisesbocchio:jasypt-spring-boot:jar:3.0.5:compile +[INFO] | | \- org.jasypt:jasypt:jar:1.9.3:compile +[INFO] | +- io.github.resilience4j:resilience4j-retry:jar:2.0.2:compile +[INFO] | | +- io.github.resilience4j:resilience4j-core:jar:2.0.2:compile +[INFO] | | | \- (org.slf4j:slf4j-api:jar:2.0.17:runtime - version managed from 1.7.30; omitted for duplicate) +[INFO] | | \- (org.slf4j:slf4j-api:jar:2.0.17:runtime - version managed from 1.7.30; omitted for duplicate) +[INFO] | \- io.vavr:vavr:jar:0.10.4:compile +[INFO] | \- io.vavr:vavr-match:jar:0.10.4:compile +[INFO] +- com.devskiller.friendly-id:friendly-id:jar:2.0.0-SNAPSHOT:compile (scope not updated to compile) +[INFO] | \- org.jspecify:jspecify:jar:1.0.0:compile (version managed from 1.0.0; scope not updated to compile) +[INFO] +- com.devskiller.friendly-id:friendly-id-jackson2-datatype:jar:2.0.0-SNAPSHOT:compile +[INFO] | +- (com.devskiller.friendly-id:friendly-id:jar:2.0.0-SNAPSHOT:compile - omitted for duplicate) +[INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile (version managed from 2.18.2) +[INFO] | +- com.fasterxml.jackson.core:jackson-core:jar:2.20.2:compile (version managed from 2.18.2) +[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile (version managed from 2.18.2) +[INFO] | | +- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile - version managed from 2.20; omitted for duplicate) +[INFO] | | \- (com.fasterxml.jackson.core:jackson-core:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.20.2:compile (version managed from 2.18.2) +[INFO] | +- (com.fasterxml.jackson.core:jackson-core:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] | \- (com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] +- com.devskiller.friendly-id:friendly-id-jackson-datatype:jar:2.0.0-SNAPSHOT:compile +[INFO] | +- (com.devskiller.friendly-id:friendly-id:jar:2.0.0-SNAPSHOT:compile - omitted for duplicate) +[INFO] | +- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile - version managed from 2.20; omitted for duplicate) +[INFO] | +- tools.jackson.core:jackson-core:jar:3.0.4:compile (version managed from 3.0.3) +[INFO] | \- tools.jackson.core:jackson-databind:jar:3.0.4:compile (version managed from 3.0.3) +[INFO] | +- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile - version managed from 2.20; omitted for duplicate) +[INFO] | \- (tools.jackson.core:jackson-core:jar:3.0.4:compile - version managed from 3.0.4; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-amqp:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-amqp:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework:spring-messaging:jar:7.0.5:compile (version managed from 7.0.5) +[INFO] | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | +- (org.springframework.amqp:spring-rabbit:jar:4.0.2:compile - version managed from 4.0.2; omitted for duplicate) +[INFO] | \- (org.springframework.boot:spring-boot-transaction:jar:4.0.3:runtime - version managed from 4.0.3; omitted for duplicate) +[INFO] +- nl.big-o:liqp:jar:0.9.2.3:compile +[INFO] | +- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile - version managed from 2.13.2; omitted for duplicate) +[INFO] | +- (com.fasterxml.jackson.core:jackson-core:jar:2.20.2:compile - version managed from 2.13.2; omitted for duplicate) +[INFO] | +- (com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile - version managed from 2.13.4.2; omitted for duplicate) +[INFO] | +- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.20.2:compile (version managed from 2.13.2) +[INFO] | | +- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:compile - version managed from 2.20; omitted for duplicate) +[INFO] | | +- (com.fasterxml.jackson.core:jackson-core:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] | | \- (com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile - version managed from 2.20.2; omitted for duplicate) +[INFO] | \- ua.co.k:strftime4j:jar:1.0.6:compile +[INFO] +- com.ibm.icu:icu4j:jar:78.2:compile +[INFO] +- com.mailjet:mailjet-client:jar:6.0.1:compile +[INFO] | +- com.squareup.okhttp3:okhttp:jar:5.0.0-alpha.14:compile +[INFO] | | +- com.squareup.okio:okio-jvm:jar:3.9.0:compile +[INFO] | | | \- (org.jetbrains.kotlin:kotlin-stdlib:jar:2.2.21:compile - version managed from 1.9.21; omitted for duplicate) +[INFO] | | \- org.jetbrains.kotlin:kotlin-stdlib:jar:2.2.21:compile (version managed from 1.9.23) +[INFO] | | \- org.jetbrains:annotations:jar:13.0:compile (scope not updated to compile) +[INFO] | +- org.json:json:jar:20231013:compile +[INFO] | \- com.google.code.gson:gson:jar:2.13.2:compile (version managed from 2.9.0) +[INFO] | \- com.google.errorprone:error_prone_annotations:jar:2.41.0:compile +[INFO] +- com.slack.api:slack-api-client:jar:1.47.0:compile +[INFO] | +- com.slack.api:slack-api-model:jar:1.47.0:compile +[INFO] | | \- (com.google.code.gson:gson:jar:2.13.2:compile - version managed from 2.12.1; omitted for duplicate) +[INFO] | +- (com.squareup.okhttp3:okhttp:jar:4.12.0:compile - omitted for conflict with 5.0.0-alpha.14) +[INFO] | +- (com.google.code.gson:gson:jar:2.13.2:compile - version managed from 2.12.1; omitted for duplicate) +[INFO] | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 1.7.36; omitted for duplicate) +[INFO] +- com.nimbusds:nimbus-jose-jwt:jar:9.37:compile (scope not updated to compile) +[INFO] | \- com.github.stephenc.jcip:jcip-annotations:jar:1.0-1:compile +[INFO] +- org.jsoup:jsoup:jar:1.22.1:compile +[INFO] +- commons-validator:commons-validator:jar:1.10.1:compile +[INFO] | +- commons-beanutils:commons-beanutils:jar:1.11.0:compile +[INFO] | | +- (commons-logging:commons-logging:jar:1.3.5:compile - version managed from 1.3.5; omitted for duplicate) +[INFO] | | \- (commons-collections:commons-collections:jar:3.2.2:compile - omitted for duplicate) +[INFO] | +- commons-digester:commons-digester:jar:2.1:compile +[INFO] | +- commons-logging:commons-logging:jar:1.3.5:compile (version managed from 1.3.5) +[INFO] | \- commons-collections:commons-collections:jar:3.2.2:compile +[INFO] +- org.postgresql:postgresql:jar:42.7.10:runtime +[INFO] | \- org.checkerframework:checker-qual:jar:3.52.0:runtime +[INFO] +- org.liquibase:liquibase-core:jar:5.0.1:compile +[INFO] | +- com.opencsv:opencsv:jar:5.12.0:compile +[INFO] | | +- (org.apache.commons:commons-lang3:jar:3.19.0:compile - version managed from 3.18.0; omitted for duplicate) +[INFO] | | +- (org.apache.commons:commons-text:jar:1.13.1:compile - omitted for conflict with 1.14.0) +[INFO] | | \- (org.apache.commons:commons-collections4:jar:4.5.0:compile - omitted for duplicate) +[INFO] | +- org.yaml:snakeyaml:jar:2.5:compile (version managed from 2.5) +[INFO] | +- javax.xml.bind:jaxb-api:jar:2.3.1:compile +[INFO] | +- org.apache.commons:commons-collections4:jar:4.5.0:compile +[INFO] | +- org.apache.commons:commons-text:jar:1.14.0:compile +[INFO] | | \- (org.apache.commons:commons-lang3:jar:3.19.0:compile - version managed from 3.18.0; omitted for duplicate) +[INFO] | +- (org.apache.commons:commons-lang3:jar:3.19.0:compile - version managed from 3.19.0; omitted for duplicate) +[INFO] | \- commons-io:commons-io:jar:2.20.0:compile (scope not updated to compile) +[INFO] +- org.springframework.boot:spring-boot-starter-jooq:jar:4.0.3:compile +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- (org.springframework.boot:spring-boot-starter-jdbc:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-jooq:jar:4.0.3:compile (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-jdbc:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework.boot:spring-boot-transaction:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- (org.springframework.boot:spring-boot-jdbc:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] +- org.jooq.pro:jooq:jar:3.20.8:compile +[INFO] | \- io.r2dbc:r2dbc-spi:jar:1.0.0.RELEASE:compile (version managed from 1.0.0.RELEASE) +[INFO] | \- org.reactivestreams:reactive-streams:jar:1.0.4:compile (version managed from 1.0.3) +[INFO] +- org.projectlombok:lombok:jar:1.18.42:compile +[INFO] +- net.logstash.logback:logstash-logback-encoder:jar:8.0:compile +[INFO] | \- (com.fasterxml.jackson.core:jackson-databind:jar:2.20.2:compile - version managed from 2.17.2; omitted for duplicate) +[INFO] +- com.datadoghq:dd-trace-api:jar:1.60.1:compile +[INFO] | \- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 1.7.30; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-test:jar:4.0.3:test +[INFO] | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-test:jar:4.0.3:test (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-test:jar:7.0.5:test - version managed from 7.0.5; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-test-autoconfigure:jar:4.0.3:test (version managed from 4.0.3) +[INFO] | | \- (org.springframework.boot:spring-boot-test:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- com.jayway.jsonpath:json-path:jar:2.10.0:test (version managed from 2.10.0) +[INFO] | | +- (net.minidev:json-smart:jar:2.6.0:test - version managed from 2.6.0; omitted for duplicate) +[INFO] | | \- (org.slf4j:slf4j-api:jar:2.0.17:test - version managed from 2.0.17; omitted for duplicate) +[INFO] | +- jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.4:runtime (version managed from 4.0.4; scope not updated to runtime) +[INFO] | | \- jakarta.activation:jakarta.activation-api:jar:2.1.4:runtime (version managed from 2.1.4) +[INFO] | +- net.minidev:json-smart:jar:2.6.0:test (version managed from 2.6.0) +[INFO] | | \- net.minidev:accessors-smart:jar:2.6.0:test +[INFO] | | \- org.ow2.asm:asm:jar:9.7.1:test +[INFO] | +- org.assertj:assertj-core:jar:3.27.7:test (version managed from 3.27.7) +[INFO] | | \- net.bytebuddy:byte-buddy:jar:1.17.8:runtime (version managed from 1.18.3; scope not updated to runtime) +[INFO] | +- (org.awaitility:awaitility:jar:4.3.0:test - version managed from 4.3.0; omitted for duplicate) +[INFO] | +- org.hamcrest:hamcrest:jar:3.0:test (version managed from 3.0) +[INFO] | +- org.junit.jupiter:junit-jupiter:jar:6.0.3:test (version managed from 6.0.3) +[INFO] | | +- org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test (version managed from 6.0.3) +[INFO] | | | +- org.opentest4j:opentest4j:jar:1.3.0:test +[INFO] | | | +- org.junit.platform:junit-platform-commons:jar:6.0.3:test (version managed from 6.0.3) +[INFO] | | | | +- (org.apiguardian:apiguardian-api:jar:1.1.2:test - omitted for duplicate) +[INFO] | | | | \- (org.jspecify:jspecify:jar:1.0.0:test - version managed from 1.0.0; omitted for duplicate) +[INFO] | | | +- org.apiguardian:apiguardian-api:jar:1.1.2:test +[INFO] | | | \- (org.jspecify:jspecify:jar:1.0.0:test - version managed from 1.0.0; omitted for duplicate) +[INFO] | | +- org.junit.jupiter:junit-jupiter-params:jar:6.0.3:test (version managed from 6.0.3) +[INFO] | | | +- (org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test - version managed from 6.0.3; omitted for duplicate) +[INFO] | | | +- (org.apiguardian:apiguardian-api:jar:1.1.2:test - omitted for duplicate) +[INFO] | | | \- (org.jspecify:jspecify:jar:1.0.0:test - version managed from 1.0.0; omitted for duplicate) +[INFO] | | \- org.junit.jupiter:junit-jupiter-engine:jar:6.0.3:test (version managed from 6.0.3) +[INFO] | | +- org.junit.platform:junit-platform-engine:jar:6.0.3:test (version managed from 6.0.3) +[INFO] | | | +- (org.opentest4j:opentest4j:jar:1.3.0:test - omitted for duplicate) +[INFO] | | | +- (org.junit.platform:junit-platform-commons:jar:6.0.3:test - version managed from 6.0.3; omitted for duplicate) +[INFO] | | | +- (org.apiguardian:apiguardian-api:jar:1.1.2:test - omitted for duplicate) +[INFO] | | | \- (org.jspecify:jspecify:jar:1.0.0:test - version managed from 1.0.0; omitted for duplicate) +[INFO] | | +- (org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test - version managed from 6.0.3; omitted for duplicate) +[INFO] | | +- (org.apiguardian:apiguardian-api:jar:1.1.2:test - omitted for duplicate) +[INFO] | | \- (org.jspecify:jspecify:jar:1.0.0:test - version managed from 1.0.0; omitted for duplicate) +[INFO] | +- org.mockito:mockito-core:jar:5.20.0:test (version managed from 5.20.0) +[INFO] | | +- (net.bytebuddy:byte-buddy:jar:1.17.8:test - version managed from 1.17.7; omitted for duplicate) +[INFO] | | +- net.bytebuddy:byte-buddy-agent:jar:1.17.8:test (version managed from 1.17.7) +[INFO] | | \- org.objenesis:objenesis:jar:3.3:test +[INFO] | +- org.mockito:mockito-junit-jupiter:jar:5.20.0:test (version managed from 5.20.0) +[INFO] | | +- (org.mockito:mockito-core:jar:5.20.0:test - version managed from 5.20.0; omitted for duplicate) +[INFO] | | \- (org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test - version managed from 5.13.4; omitted for duplicate) +[INFO] | +- org.skyscreamer:jsonassert:jar:1.5.3:test (version managed from 1.5.3) +[INFO] | | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test +[INFO] | +- org.springframework:spring-core:jar:7.0.5:compile (version managed from 7.0.5; scope not updated to compile) +[INFO] | | +- (commons-logging:commons-logging:jar:1.3.5:compile - version managed from 1.3.5; omitted for duplicate) +[INFO] | | \- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | +- org.springframework:spring-test:jar:7.0.5:test (version managed from 7.0.5) +[INFO] | | \- (org.springframework:spring-core:jar:7.0.5:test - version managed from 7.0.5; omitted for duplicate) +[INFO] | \- org.xmlunit:xmlunit-core:jar:2.10.4:test (version managed from 2.10.4) +[INFO] | \- (jakarta.xml.bind:jakarta.xml.bind-api:jar:4.0.4:test - version managed from 2.3.3; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-starter-webmvc-test:jar:4.0.3:test +[INFO] | +- org.springframework.boot:spring-boot-starter-jackson-test:jar:4.0.3:test (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-starter-jackson:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework.boot:spring-boot-starter-test:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- (org.springframework.boot:spring-boot-starter-test:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-starter-webmvc:jar:4.0.3:test (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-starter:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-starter-jackson:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-starter-tomcat:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-http-converter:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework.boot:spring-boot-webmvc:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- org.springframework.boot:spring-boot-webmvc-test:jar:4.0.3:test (version managed from 4.0.3) +[INFO] | | +- (org.springframework.boot:spring-boot-test-autoconfigure:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-webmvc:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework.boot:spring-boot-http-converter:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | | \- (org.springframework.boot:spring-boot-web-server:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- org.springframework.boot:spring-boot-resttestclient:jar:4.0.3:test (version managed from 4.0.3) +[INFO] | +- (org.springframework.boot:spring-boot-test:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | +- (org.springframework.boot:spring-boot-http-converter:jar:4.0.3:test - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- (org.springframework:spring-web:jar:7.0.5:test - version managed from 7.0.5; omitted for duplicate) +[INFO] +- org.springframework.security:spring-security-test:jar:7.0.3:test +[INFO] | +- org.springframework.security:spring-security-core:jar:7.0.3:compile (version managed from 7.0.3; scope not updated to compile) +[INFO] | | +- org.springframework.security:spring-security-crypto:jar:7.0.3:compile (version managed from 7.0.3) +[INFO] | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- org.springframework:spring-context:jar:7.0.5:compile (version managed from 7.0.4) +[INFO] | | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | +- (org.springframework:spring-expression:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- org.springframework:spring-expression:jar:7.0.5:compile (version managed from 7.0.4) +[INFO] | | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.3; omitted for duplicate) +[INFO] | +- org.springframework.security:spring-security-web:jar:7.0.3:compile (version managed from 7.0.3; scope not updated to compile) +[INFO] | | +- (org.springframework.security:spring-security-core:jar:7.0.3:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-aop:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-expression:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | | \- (org.springframework:spring-web:jar:7.0.5:compile - version managed from 7.0.4; omitted for duplicate) +[INFO] | +- (org.springframework:spring-core:jar:7.0.5:test - version managed from 7.0.4; omitted for duplicate) +[INFO] | \- (org.springframework:spring-test:jar:7.0.5:test - version managed from 7.0.4; omitted for duplicate) +[INFO] +- org.springframework.boot:spring-boot-testcontainers:jar:4.0.3:test +[INFO] | +- org.springframework.boot:spring-boot-autoconfigure:jar:4.0.3:compile (version managed from 4.0.3; scope not updated to compile) +[INFO] | | \- (org.springframework.boot:spring-boot:jar:4.0.3:compile - version managed from 4.0.3; omitted for duplicate) +[INFO] | \- org.testcontainers:testcontainers:jar:2.0.3:test (version managed from 2.0.3) +[INFO] | +- (org.slf4j:slf4j-api:jar:2.0.17:test - version managed from 1.7.36; omitted for duplicate) +[INFO] | +- org.apache.commons:commons-compress:jar:1.28.0:test +[INFO] | | +- commons-codec:commons-codec:jar:1.19.0:test (version managed from 1.19.0) +[INFO] | | +- (commons-io:commons-io:jar:2.20.0:test - omitted for duplicate) +[INFO] | | \- (org.apache.commons:commons-lang3:jar:3.19.0:test - version managed from 3.18.0; omitted for duplicate) +[INFO] | +- org.rnorth.duct-tape:duct-tape:jar:1.0.8:test +[INFO] | | \- (org.jetbrains:annotations:jar:17.0.0:test - omitted for conflict with 13.0) +[INFO] | +- com.github.docker-java:docker-java-api:jar:3.7.0:test +[INFO] | | +- (com.fasterxml.jackson.core:jackson-annotations:jar:2.20:test - version managed from 2.20; omitted for duplicate) +[INFO] | | \- (org.slf4j:slf4j-api:jar:2.0.17:test - version managed from 1.7.30; omitted for duplicate) +[INFO] | \- com.github.docker-java:docker-java-transport-zerodep:jar:3.7.0:test +[INFO] | +- com.github.docker-java:docker-java-transport:jar:3.7.0:test +[INFO] | +- (org.slf4j:slf4j-api:jar:2.0.17:test - version managed from 1.7.36; omitted for duplicate) +[INFO] | \- net.java.dev.jna:jna:jar:5.18.1:test +[INFO] +- org.springframework.amqp:spring-rabbit-test:jar:4.0.2:test +[INFO] | +- org.springframework.amqp:spring-rabbit:jar:4.0.2:compile (version managed from 4.0.2; scope not updated to compile) +[INFO] | | +- org.springframework.amqp:spring-amqp:jar:4.0.2:compile (version managed from 4.0.2) +[INFO] | | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- com.rabbitmq:amqp-client:jar:5.27.1:compile (version managed from 5.27.1; scope not updated to compile) +[INFO] | | | +- (org.slf4j:slf4j-api:jar:2.0.17:compile - version managed from 1.7.36; omitted for duplicate) +[INFO] | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.7.Final; omitted for duplicate) +[INFO] | | | +- io.netty:netty-codec:jar:4.2.10.Final:compile (version managed from 4.2.7.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-codec-compression:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | \- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- io.netty:netty-codec-protobuf:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | | \- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- io.netty:netty-codec-marshalling:jar:4.2.10.Final:compile (version managed from 4.2.10.Final) +[INFO] | | | | +- (io.netty:netty-common:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-buffer:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | +- (io.netty:netty-transport:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | | \- (io.netty:netty-codec-base:jar:4.2.10.Final:compile - version managed from 4.2.10.Final; omitted for duplicate) +[INFO] | | | \- (io.netty:netty-handler:jar:4.2.10.Final:compile - version managed from 4.2.7.Final; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-context:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-messaging:jar:7.0.5:compile - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- org.springframework:spring-tx:jar:7.0.5:compile (version managed from 7.0.3) +[INFO] | | | +- (org.springframework:spring-beans:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | | \- (org.springframework:spring-core:jar:7.0.5:compile - version managed from 7.0.5; omitted for duplicate) +[INFO] | | \- (io.micrometer:micrometer-observation:jar:1.16.3:compile - version managed from 1.16.2; omitted for duplicate) +[INFO] | +- org.springframework.amqp:spring-rabbit-junit:jar:4.0.2:test (version managed from 4.0.2) +[INFO] | | +- (org.springframework:spring-core:jar:7.0.5:test - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-test:jar:7.0.5:test - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- (com.rabbitmq:amqp-client:jar:5.27.1:test - version managed from 5.27.1; omitted for duplicate) +[INFO] | | +- (org.springframework:spring-web:jar:7.0.5:test - version managed from 7.0.3; omitted for duplicate) +[INFO] | | +- (org.junit.jupiter:junit-jupiter-api:jar:6.0.3:test - version managed from 6.0.2; omitted for duplicate) +[INFO] | | \- (org.assertj:assertj-core:jar:3.27.7:test - version managed from 3.27.6; omitted for duplicate) +[INFO] | +- org.hamcrest:hamcrest-library:jar:3.0:test (version managed from 3.0) +[INFO] | | \- (org.hamcrest:hamcrest-core:jar:3.0:test - version managed from 3.0; omitted for duplicate) +[INFO] | +- org.hamcrest:hamcrest-core:jar:3.0:test (version managed from 3.0) +[INFO] | | \- (org.hamcrest:hamcrest:jar:3.0:test - version managed from 3.0; omitted for duplicate) +[INFO] | \- (org.mockito:mockito-core:jar:5.20.0:test - version managed from 5.20.0; omitted for duplicate) +[INFO] +- io.projectreactor:reactor-test:jar:3.8.3:test +[INFO] | +- io.projectreactor:reactor-core:jar:3.8.3:compile (version managed from 3.8.3; scope not updated to compile) +[INFO] | | +- (org.reactivestreams:reactive-streams:jar:1.0.4:compile - version managed from 1.0.4; omitted for duplicate) +[INFO] | | \- (org.jspecify:jspecify:jar:1.0.0:compile - version managed from 1.0.0; omitted for duplicate) +[INFO] | \- (org.jspecify:jspecify:jar:1.0.0:test - version managed from 1.0.0; omitted for duplicate) +[INFO] +- org.awaitility:awaitility:jar:4.3.0:test (scope not updated to test) +[INFO] | \- (org.hamcrest:hamcrest:jar:3.0:test - version managed from 2.1; omitted for duplicate) +[INFO] +- org.testcontainers:testcontainers-junit-jupiter:jar:2.0.3:test +[INFO] | \- (org.testcontainers:testcontainers:jar:2.0.3:test - version managed from 2.0.3; omitted for duplicate) +[INFO] +- org.testcontainers:testcontainers-postgresql:jar:2.0.3:test +[INFO] | \- org.testcontainers:testcontainers-jdbc:jar:2.0.3:test (version managed from 2.0.3) +[INFO] | \- org.testcontainers:testcontainers-database-commons:jar:2.0.3:test (version managed from 2.0.3) +[INFO] | \- (org.testcontainers:testcontainers:jar:2.0.3:test - version managed from 2.0.3; omitted for duplicate) +[INFO] \- org.testcontainers:testcontainers-rabbitmq:jar:2.0.3:test +[INFO] \- (org.testcontainers:testcontainers:jar:2.0.3:test - version managed from 2.0.3; omitted for duplicate) +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 0.561 s +[INFO] Finished at: 2026-03-09T20:37:27+01:00 +[INFO] ------------------------------------------------------------------------ diff --git a/tests/fixtures/mvn_dep_tree_conflicts.txt b/tests/fixtures/mvn_dep_tree_conflicts.txt new file mode 100644 index 000000000..72e8bd9ac --- /dev/null +++ b/tests/fixtures/mvn_dep_tree_conflicts.txt @@ -0,0 +1,16 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] --- dependency:3.7.0:tree (default-cli) @ my-app --- +[INFO] com.example:my-app:jar:1.0.0 +[INFO] +- com.example:lib-a:jar:1.0.0:compile +[INFO] | +- com.fasterxml.jackson.core:jackson-databind:jar:2.18.3:compile +[INFO] | | +- com.fasterxml.jackson.core:jackson-core:jar:2.18.3:compile +[INFO] | | \- com.fasterxml.jackson.core:jackson-annotations:jar:2.18.3:compile +[INFO] | \- org.slf4j:slf4j-api:jar:2.0.9:compile +[INFO] +- com.example:lib-b:jar:2.0.0:compile +[INFO] | +- (com.fasterxml.jackson.core:jackson-databind:jar:2.17.0:compile - omitted for conflict with 2.18.3) +[INFO] | \- (org.slf4j:slf4j-api:jar:2.0.7:compile - omitted for conflict with 2.0.9) +[INFO] \- com.example:lib-c:jar:3.0.0:compile +[INFO] \- (com.fasterxml.jackson.core:jackson-databind:jar:2.19.0:compile - omitted for conflict with 2.18.3) +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS diff --git a/tests/fixtures/mvn_dep_tree_simple.txt b/tests/fixtures/mvn_dep_tree_simple.txt new file mode 100644 index 000000000..92a436d85 --- /dev/null +++ b/tests/fixtures/mvn_dep_tree_simple.txt @@ -0,0 +1,22 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] ------------------< com.example:my-app >------------------- +[INFO] Building my-app 1.0.0 +[INFO] from pom.xml +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- dependency:3.7.0:tree (default-cli) @ my-app --- +[INFO] com.example:my-app:jar:1.0.0 +[INFO] +- org.slf4j:slf4j-api:jar:2.0.17:compile +[INFO] +- com.google.guava:guava:jar:33.0.0-jre:compile +[INFO] | +- com.google.guava:failureaccess:jar:1.0.2:compile +[INFO] | \- com.google.guava:listenablefuture:jar:9999.0-empty-to-avoid-conflict-with-guava:compile +[INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.19.2:compile +[INFO] | +- com.fasterxml.jackson.core:jackson-annotations:jar:2.19.2:compile +[INFO] | \- com.fasterxml.jackson.core:jackson-core:jar:2.19.2:compile +[INFO] \- org.junit.jupiter:junit-jupiter:jar:5.11.4:test +[INFO] +- org.junit.jupiter:junit-jupiter-api:jar:5.11.4:test +[INFO] \- org.junit.jupiter:junit-jupiter-engine:jar:5.11.4:test +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ diff --git a/tests/fixtures/mvn_test_fail_auth.txt b/tests/fixtures/mvn_test_fail_auth.txt new file mode 100644 index 000000000..018dad29e --- /dev/null +++ b/tests/fixtures/mvn_test_fail_auth.txt @@ -0,0 +1,95 @@ +[INFO] Scanning for projects... +[WARNING] Could not transfer metadata from/to central: Checksum validation failed +[WARNING] +[WARNING] 1 problem was encountered while building the effective model for 'com.example:webapp:jar:1.0-SNAPSHOT' +[WARNING] +[WARNING] Total model problems reported: 1 +[WARNING] +[INFO] +[INFO] -------------------------------------------------< com.example:webapp >-------------------------------------------------- +[INFO] Building webapp 1.0-SNAPSHOT +[INFO] from pom.xml +[INFO] ---------------------------------------------------------[ jar ]---------------------------------------------------------- +[WARNING] 176 problems were encountered while building the effective model for 'com.example:dep:jar:3.2.0' during n/a +[WARNING] 130 problems were encountered while building the effective model for 'org.redisson:redisson-spring-boot-starter:jar:4.0.0' +[WARNING] 76 problems were encountered while building the effective model for 'com.playtika.testcontainers:embedded-redis:jar:3.1.11' +[INFO] +[INFO] --- clean:3.5.0:clean (default-clean) @ webapp --- +[INFO] Deleting /home/user/project/target +[INFO] +[INFO] --- jacoco:0.8.14:prepare-agent (prepare-agent) @ webapp --- +[INFO] argLine set to -javaagent:/home/user/.m2/repository/org/jacoco/org.jacoco.agent/0.8.14/org.jacoco.agent-0.8.14-runtime.jar +[INFO] +[INFO] --- testcontainers-jooq-codegen:0.0.4:generate (generate-jooq-sources) @ webapp --- +[WARNING] Could not transfer metadata from/to flyway-repo: Connection refused +[INFO] Image pull policy will be performed by: DefaultPullPolicy() +[WARNING] [stderr] Apr 08, 2026 5:45:45 PM liquibase.changelog +[WARNING] [stderr] INFO: Reading resource: db/changelog/2025/db.changelog-main.yaml +[WARNING] [stderr] INFO: Reading resource: db/changelog/2025/db.changelog-001.yaml +[WARNING] [stderr] INFO: Reading resource: db/changelog/2025/db.changelog-002.yaml +[INFO] +[INFO] --- maven-compiler-plugin:3.14.0:compile (default-compile) @ webapp --- +[INFO] Compiling 42 source files with javac [debug target 25] to target/classes +[INFO] +[INFO] --- maven-compiler-plugin:3.14.0:testCompile (default-testCompile) @ webapp --- +[INFO] Compiling 18 source files with javac [debug target 25] to target/test-classes +[WARNING] /home/user/project/src/test/java/com/example/TestFactory.java:[44,27] deprecated +[WARNING] /home/user/project/src/test/java/com/example/TestFactory.java:[48,45] deprecated +[WARNING] /home/user/project/src/test/java/com/example/SpecificationsTest.java:[60,44] deprecated +[INFO] /home/user/project/src/test/java/com/example/ProcessorTest.java: uses unchecked or unsafe operations. +[INFO] +[INFO] --- surefire:3.5.5:test (default-test) @ webapp --- +[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +[WARNING] The system property java.util.logging.config.file is configured twice! +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.score.ScoreTypeTest +[INFO] Running com.example.company.EmailParserTest +[ERROR] Tests run: 2, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.050 s <<< FAILURE! -- in com.example.company.EmailParserTest +[ERROR] Tests run: 3, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.058 s <<< FAILURE! -- in com.example.score.ScoreTypeTest +[ERROR] com.example.company.EmailParserTest.should_extract_domain_from_email -- Time elapsed: 0.009 s <<< FAILURE! +org.opentest4j.AssertionFailedError: + +expected: "broken.example.com" + but was: "user.example.com" + at com.example.company.EmailParserTest.should_extract_domain_from_email(EmailParserTest.java:14) + +[ERROR] com.example.score.ScoreTypeTest.shouldMapToRole -- Time elapsed: 0.008 s <<< FAILURE! +org.opentest4j.AssertionFailedError: + +expected: "app:BROKEN" + but was: "app:all" + at com.example.score.ScoreTypeTest.shouldMapToRole(ScoreTypeTest.java:24) + +[INFO] +[INFO] Results: +[INFO] +[ERROR] Failures: +[ERROR] EmailParserTest.should_extract_domain_from_email:14 +expected: "broken.example.com" + but was: "user.example.com" +[ERROR] ScoreTypeTest.shouldMapToRole:24 +expected: "app:BROKEN" + but was: "app:all" +[INFO] +[ERROR] Tests run: 5, Failures: 2, Errors: 0, Skipped: 0 +[INFO] +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] BUILD FAILURE +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] Total time: 23.819 s +[INFO] Finished at: 2026-04-08T17:45:57+02:00 +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.5:test (default-test) on project webapp: There are test failures. +[ERROR] +[ERROR] See /home/user/project/target/surefire-reports for the individual test results. +[ERROR] See dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. +[ERROR] -> [Help 1] +[ERROR] +[ERROR] To see the full stack trace of the errors, re-run Maven with the '-e' switch +[ERROR] Re-run Maven using the '-X' switch to enable verbose output +[ERROR] +[ERROR] For more information about the errors and possible solutions, please read the following articles: +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/tests/fixtures/mvn_test_large_suite.txt b/tests/fixtures/mvn_test_large_suite.txt new file mode 100644 index 000000000..5ab6abb71 --- /dev/null +++ b/tests/fixtures/mvn_test_large_suite.txt @@ -0,0 +1,204 @@ +[INFO] Scanning for projects... +[WARNING] Could not transfer metadata from/to central: Checksum validation failed +[INFO] +[INFO] -----------------------< com.example:platform >------------------------ +[INFO] Building platform 2.0-SNAPSHOT +[INFO] from pom.xml +[INFO] --------------------------------[ jar ]--------------------------------- +[WARNING] 42 problems were encountered while building the effective model +[INFO] +[INFO] --- maven-compiler-plugin:3.14.0:compile (default-compile) @ platform --- +[INFO] Compiling 120 source files with javac +[INFO] +[INFO] --- maven-compiler-plugin:3.14.0:testCompile (default-testCompile) @ platform --- +[INFO] Compiling 85 source files with javac +[WARNING] /home/user/project/src/test/java/com/example/DeprecatedTest.java:[10,5] deprecated +[WARNING] /home/user/project/src/test/java/com/example/OtherTest.java:[20,8] deprecated +[INFO] +[INFO] --- surefire:3.5.4:test (default-test) @ platform --- +[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.registry.SearchModelTest +[INFO] Tests run: 50, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 5.123 s -- in com.example.registry.SearchModelTest +[INFO] Running com.example.registry.IndexServiceTest +[INFO] Tests run: 30, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.456 s -- in com.example.registry.IndexServiceTest +[INFO] Running com.example.organization.OrgServiceTest +[INFO] Tests run: 25, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.234 s -- in com.example.organization.OrgServiceTest +[INFO] Running com.example.organization.PatchableFieldTest +[ERROR] Tests run: 6, Failures: 0, Errors: 6, Skipped: 0, Time elapsed: 0.010 s <<< FAILURE! -- in com.example.organization.PatchableFieldTest +[ERROR] com.example.organization.PatchableFieldTest.fromStrings_returns_empty -- Time elapsed: 0 s <<< ERROR! +java.lang.Error: +Unresolved compilation problem: + log cannot be resolved + + at com.example.organization.PatchableFieldTest.fromStrings_returns_empty(PatchableFieldTest.java:12) + +[ERROR] com.example.organization.PatchableFieldTest.fromStrings_converts_names -- Time elapsed: 0 s <<< ERROR! +java.lang.Error: +Unresolved compilation problem: + log cannot be resolved + + at com.example.organization.PatchableFieldTest.fromStrings_converts_names(PatchableFieldTest.java:20) + +[ERROR] com.example.organization.PatchableFieldTest.shouldUpdate_false_when_empty -- Time elapsed: 0 s <<< ERROR! +java.lang.ClassCastException: class com.example.organization.PatchableField not an enum + at com.example.organization.PatchableFieldTest.shouldUpdate_false_when_empty(PatchableFieldTest.java:36) + +[ERROR] com.example.organization.PatchableFieldTest.shouldUpdate_true_when_null -- Time elapsed: 0 s <<< ERROR! +java.lang.Error: +Unresolved compilation problem: + + at com.example.organization.PatchableField.shouldUpdate(PatchableField.java:29) + +[ERROR] com.example.organization.PatchableFieldTest.shouldUpdateAny_true -- Time elapsed: 0 s <<< ERROR! +java.lang.NullPointerException: Cannot invoke "java.lang.Enum.getDeclaringClass()" because "e1" is null + at com.example.organization.PatchableFieldTest.shouldUpdateAny_true(PatchableFieldTest.java:49) + +[ERROR] com.example.organization.PatchableFieldTest.fromStrings_ignores_unknown -- Time elapsed: 0 s <<< ERROR! +java.lang.Error: +Unresolved compilation problem: + log cannot be resolved + + at com.example.organization.PatchableFieldTest.fromStrings_ignores_unknown(PatchableFieldTest.java:55) + +[INFO] Running com.example.search.SearchReadModelTest +[ERROR] Tests run: 3, Failures: 3, Errors: 0, Skipped: 0, Time elapsed: 4.567 s <<< FAILURE! -- in com.example.search.SearchReadModelTest +[ERROR] com.example.search.SearchReadModelTest.should_get_person_ratings -- Time elapsed: 1.234 s <<< FAILURE! +java.lang.AssertionError: +Expecting actual: + [("Group1", {SELF=50, SUPERVISOR=35}, 40, null, 3), + ("Group2", {SELF=80}, 80, null, 1)] +to have size: + <1> +but had size: + <2> + + at com.example.search.SearchReadModelTest.should_get_person_ratings(SearchReadModelTest.java:85) + +[ERROR] com.example.search.SearchReadModelTest.should_get_company_ratings -- Time elapsed: 0.987 s <<< FAILURE! +org.assertj.core.error.AssertJMultipleFailuresError: +[List check single element] (1 failure) +-- failure 1 -- +expected: 42 + but was: 38 + + at com.example.search.SearchReadModelTest.should_get_company_ratings(SearchReadModelTest.java:120) + +[ERROR] com.example.search.SearchReadModelTest.should_not_include_unknown_skills -- Time elapsed: 0.456 s <<< FAILURE! +org.assertj.core.error.AssertJMultipleFailuresError: +[List check single element] (1 failure) +-- failure 1 -- +expected size: 0 but was: 1 + + at com.example.search.SearchReadModelTest.should_not_include_unknown_skills(SearchReadModelTest.java:150) + +[INFO] Running com.example.payment.PaymentServiceTest +[INFO] Tests run: 100, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 8.901 s -- in com.example.payment.PaymentServiceTest +[INFO] Running com.example.report.ReportGeneratorTest +[INFO] Tests run: 45, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 3.210 s -- in com.example.report.ReportGeneratorTest +[INFO] Running com.example.integration.ApiIntegrationTest +[ERROR] Tests run: 14, Failures: 0, Errors: 14, Skipped: 0, Time elapsed: 0.015 s <<< FAILURE! -- in com.example.integration.ApiIntegrationTest +[ERROR] com.example.integration.ApiIntegrationTest.shouldCreateUser -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldDeleteUser -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldUpdateUser -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldListUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldGetUser -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldSearchUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldPaginateUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldFilterUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldSortUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldExportUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldImportUsers -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldValidateInput -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldHandleConcurrency -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.integration.ApiIntegrationTest.shouldRetryOnFailure -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[INFO] +[INFO] Results: +[INFO] +[ERROR] Failures: +[ERROR] SearchReadModelTest.should_get_person_ratings:85 expected size: 1 but was: 2 +[ERROR] SearchReadModelTest.should_get_company_ratings:120 expected: 42 but was: 38 +[ERROR] SearchReadModelTest.should_not_include_unknown_skills:150 expected size: 0 but was: 1 +[ERROR] Errors: +[ERROR] PatchableFieldTest.fromStrings_returns_empty >> Error Unresolved compilation problem: log cannot be resolved +[ERROR] PatchableFieldTest.fromStrings_converts_names >> Error Unresolved compilation problem: log cannot be resolved +[ERROR] PatchableFieldTest.shouldUpdate_false_when_empty >> ClassCast not an enum +[ERROR] PatchableFieldTest.shouldUpdate_true_when_null >> Error Unresolved compilation problem +[ERROR] PatchableFieldTest.shouldUpdateAny_true >> NullPointer Cannot invoke "java.lang.Enum.getDeclaringClass()" +[ERROR] PatchableFieldTest.fromStrings_ignores_unknown >> Error Unresolved compilation problem: log cannot be resolved +[ERROR] ApiIntegrationTest.shouldCreateUser >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldDeleteUser >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldUpdateUser >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldListUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldGetUser >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldSearchUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldPaginateUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldFilterUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldSortUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldExportUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldImportUsers >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldValidateInput >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldHandleConcurrency >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ApiIntegrationTest.shouldRetryOnFailure >> IllegalState ApplicationContext failure threshold (1) exceeded +[INFO] +[ERROR] Tests run: 3262, Failures: 3, Errors: 20, Skipped: 4 +[INFO] +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] BUILD FAILURE +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] Total time: 03:25 min +[INFO] Finished at: 2026-04-08T21:07:54+02:00 +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.4:test (default-test) on project platform: There are test failures. +[ERROR] +[ERROR] See /home/user/project/target/surefire-reports for the individual test results. +[ERROR] -> [Help 1] +[ERROR] +[ERROR] Re-run Maven using the '-X' switch to enable full debug logging. +[ERROR] +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/tests/fixtures/mvn_test_many_failures.txt b/tests/fixtures/mvn_test_many_failures.txt new file mode 100644 index 000000000..a3466fff9 --- /dev/null +++ b/tests/fixtures/mvn_test_many_failures.txt @@ -0,0 +1,115 @@ +[INFO] Scanning for projects... +[WARNING] The requested profile "unit-tests" could not be activated because it does not exist. +[INFO] +[INFO] -----------------------< com.example:myapp >------------------------ +[INFO] Building myapp 0.0.1-SNAPSHOT +[INFO] from pom.xml +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- resources:3.3.1:resources (default-resources) @ myapp --- +[INFO] Copying 2 resources from src/main/resources to target/classes +[INFO] +[INFO] --- surefire:3.5.5:test (default-test) @ myapp --- +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.controller.ItemControllerTest +[ERROR] Tests run: 4, Failures: 0, Errors: 4, Skipped: 0, Time elapsed: 8.737 s <<< FAILURE! -- in com.example.controller.ItemControllerTest +[ERROR] com.example.controller.ItemControllerTest.shouldReturn404ForNonExistent -- Time elapsed: 0.003 s <<< ERROR! +java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@7b05dab testClass = com.example.controller.ItemControllerTest, locations = [], classes = [com.example.MyApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceDescriptors = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"]] + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.lambda$loadContext$0(DefaultCacheAwareContextLoaderDelegate.java:195) + at org.springframework.test.context.cache.DefaultContextCache.put(DefaultContextCache.java:214) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + +[ERROR] com.example.controller.ItemControllerTest.shouldCreate -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.ItemControllerTest.shouldUpdate -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.ItemControllerTest.shouldDelete -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[INFO] Running com.example.controller.OrderControllerTest +[ERROR] Tests run: 7, Failures: 0, Errors: 7, Skipped: 0, Time elapsed: 0.012 s <<< FAILURE! -- in com.example.controller.OrderControllerTest +[ERROR] com.example.controller.OrderControllerTest.shouldReturn404 -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.OrderControllerTest.shouldUpdate -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.OrderControllerTest.shouldCreate -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.OrderControllerTest.shouldDelete -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.OrderControllerTest.shouldList -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.OrderControllerTest.shouldRejectDuplicate -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.OrderControllerTest.shouldGetById -- Time elapsed: 0.001 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded: skipping repeated attempt to load context + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[INFO] Running com.example.controller.LeaseControllerTest +[ERROR] Tests run: 14, Failures: 0, Errors: 14, Skipped: 0, Time elapsed: 0.019 s <<< FAILURE! -- in com.example.controller.LeaseControllerTest +[ERROR] com.example.controller.LeaseControllerTest.shouldReturn400 -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.LeaseControllerTest.shouldList -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[ERROR] com.example.controller.LeaseControllerTest.shouldGetById -- Time elapsed: 0 s <<< ERROR! +java.lang.IllegalStateException: ApplicationContext failure threshold (1) exceeded + at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:157) + +[INFO] +[INFO] Results: +[INFO] +[ERROR] Errors: +[ERROR] ItemControllerTest.shouldReturn404ForNonExistent >> IllegalState Failed to load ApplicationContext +[ERROR] ItemControllerTest.shouldCreate >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ItemControllerTest.shouldUpdate >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] ItemControllerTest.shouldDelete >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldReturn404 >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldUpdate >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldCreate >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldDelete >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldList >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldRejectDuplicate >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] OrderControllerTest.shouldGetById >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] LeaseControllerTest.shouldReturn400 >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] LeaseControllerTest.shouldList >> IllegalState ApplicationContext failure threshold (1) exceeded +[ERROR] LeaseControllerTest.shouldGetById >> IllegalState ApplicationContext failure threshold (1) exceeded +[INFO] +[ERROR] Tests run: 28, Failures: 0, Errors: 28, Skipped: 0 +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD FAILURE +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 22.379 s +[INFO] Finished at: 2026-04-08T20:58:01+02:00 +[INFO] ------------------------------------------------------------------------ +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.5.5:test (default-test) on project myapp: There are test failures. +[ERROR] +[ERROR] See /home/user/project/target/surefire-reports for the individual test results. +[ERROR] -> [Help 1] +[ERROR] +[ERROR] Re-run Maven using the '-X' switch to enable full debug logging. +[ERROR] +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/tests/fixtures/mvn_test_multimodule.txt b/tests/fixtures/mvn_test_multimodule.txt new file mode 100644 index 000000000..ab3ea3404 --- /dev/null +++ b/tests/fixtures/mvn_test_multimodule.txt @@ -0,0 +1,118 @@ +[INFO] Scanning for projects... +[INFO] Building parent 1.0-SNAPSHOT +[INFO] --------------------------------[ pom ]--------------------------------- +[INFO] +[INFO] --- maven-surefire-plugin:3.1.2:test (default-test) @ common --- +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.common.PathUtilsTest +[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.056 s -- in com.example.common.PathUtilsTest +[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] Results: +[INFO] +[INFO] Tests run: 10, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] --- maven-surefire-plugin:3.1.2:test (default-test) @ data --- +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.data.RepositoryTest +[INFO] Tests run: 34, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.036 s -- in com.example.data.RepositoryTest +[INFO] Running com.example.data.ModelTest +[INFO] Tests run: 25, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.370 s -- in com.example.data.ModelTest +[INFO] Tests run: 194, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] Results: +[INFO] +[INFO] Tests run: 194, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] --- maven-surefire-plugin:3.1.2:test (default-test) @ ml --- +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.ml.BenchmarkTest +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.632 s -- in com.example.ml.BenchmarkTest +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] Results: +[INFO] +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] --- maven-surefire-plugin:3.1.2:test (default-test) @ services --- +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.services.FileHasherTest +[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s -- in com.example.services.FileHasherTest +[INFO] Running com.example.services.GitDiffReaderTest +[ERROR] Tests run: 4, Failures: 0, Errors: 4, Skipped: 0, Time elapsed: 3.167 s <<< FAILURE! -- in com.example.services.GitDiffReaderTest +[ERROR] com.example.services.GitDiffReaderTest.shouldBuildDiffWithFiltering -- Time elapsed: 3.109 s <<< ERROR! +org.eclipse.jgit.api.errors.ServiceUnavailableException: Signing service is not available + at org.eclipse.jgit.api.CommitCommand.sign(CommitCommand.java:328) + at org.eclipse.jgit.api.CommitCommand.call(CommitCommand.java:283) + at com.example.services.GitDiffReaderTest.shouldBuildDiffWithFiltering(GitDiffReaderTest.java:116) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + +[ERROR] com.example.services.GitDiffReaderTest.shouldReturnEmptyDiff -- Time elapsed: 0.013 s <<< ERROR! +org.eclipse.jgit.api.errors.ServiceUnavailableException: Signing service is not available + at org.eclipse.jgit.api.CommitCommand.sign(CommitCommand.java:328) + at org.eclipse.jgit.api.CommitCommand.call(CommitCommand.java:283) + at com.example.services.GitDiffReaderTest.shouldReturnEmptyDiff(GitDiffReaderTest.java:67) + at java.base/java.lang.reflect.Method.invoke(Method.java:565) + +[ERROR] com.example.services.GitDiffReaderTest.shouldBuildDiff -- Time elapsed: 0.012 s <<< ERROR! +org.eclipse.jgit.api.errors.ServiceUnavailableException: Signing service is not available + at org.eclipse.jgit.api.CommitCommand.sign(CommitCommand.java:328) + at com.example.services.GitDiffReaderTest.shouldBuildDiff(GitDiffReaderTest.java:35) + +[ERROR] com.example.services.GitDiffReaderTest.shouldBuildDiffForDevops -- Time elapsed: 0.011 s <<< ERROR! +org.eclipse.jgit.api.errors.ServiceUnavailableException: Signing service is not available + at org.eclipse.jgit.api.CommitCommand.sign(CommitCommand.java:328) + at com.example.services.GitDiffReaderTest.shouldBuildDiffForDevops(GitDiffReaderTest.java:88) + +[INFO] Running com.example.services.IoHelperTest +[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0 s -- in com.example.services.IoHelperTest +[INFO] Tests run: 651, Failures: 0, Errors: 4, Skipped: 4 +[INFO] +[INFO] Results: +[INFO] +[ERROR] Errors: +[ERROR] GitDiffReaderTest.shouldBuildDiff:35 >> ServiceUnavailable Signing service is not available +[ERROR] GitDiffReaderTest.shouldBuildDiffForDevops:88 >> ServiceUnavailable Signing service is not available +[ERROR] GitDiffReaderTest.shouldBuildDiffWithFiltering:116 >> ServiceUnavailable Signing service is not available +[ERROR] GitDiffReaderTest.shouldReturnEmptyDiff:67 >> ServiceUnavailable Signing service is not available +[INFO] +[ERROR] Tests run: 860, Failures: 0, Errors: 4, Skipped: 4 +[INFO] +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] Reactor Summary for parent 1.0-SNAPSHOT: +[INFO] +[INFO] parent ............................................................................................... SUCCESS [ 0.189 s] +[INFO] common ............................................................................................... SUCCESS [ 0.959 s] +[INFO] data ................................................................................................. SUCCESS [ 28.127 s] +[INFO] ml ................................................................................................... SUCCESS [ 2.214 s] +[INFO] services ............................................................................................. FAILURE [ 58.575 s] +[INFO] integrations ......................................................................................... SKIPPED +[INFO] webapp ............................................................................................... SKIPPED +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] BUILD FAILURE +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] Total time: 01:31 min +[INFO] Finished at: 2026-04-08T20:45:10+02:00 +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.1.2:test (default-test) on project services: There are test failures. +[ERROR] +[ERROR] See /home/user/project/services/target/surefire-reports for the individual test results. +[ERROR] -> [Help 1] +[ERROR] +[ERROR] To see the full stack trace of the errors, re-run Maven with the '-e' switch +[ERROR] Re-run Maven using the '-X' switch to enable verbose output +[ERROR] +[ERROR] For more information about the errors and possible solutions, please read the following articles: +[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException diff --git a/tests/fixtures/mvn_test_pass_large_ansi.txt b/tests/fixtures/mvn_test_pass_large_ansi.txt new file mode 100644 index 000000000..93e3ac76e --- /dev/null +++ b/tests/fixtures/mvn_test_pass_large_ansi.txt @@ -0,0 +1,53 @@ +[INFO] Loaded 22505 auto-discovered prefixes for remote repository central (prefixes-central.txt) +[INFO] Scanning for projects... +[WARNING] Could not transfer metadata /.meta/prefixes.txt from/to shibboleth-releases-5d99c0486312ae43e4bc39d107c6522d62203c00 (https://build.shibboleth.net/nexus/content/repositories/releases/): Checksum validation failed, no checksums available +[INFO] -------------------------------------------------< com.example:auth >-------------------------------------------------- +[INFO] Building auth 1.3-SNAPSHOT +[INFO] from pom.xml +[INFO] ---------------------------------------------------------[ jar ]---------------------------------------------------------- +[INFO] Loaded 22505 auto-discovered prefixes for remote repository central (prefixes-central.txt) +[INFO] Image name substitution will be performed by: DefaultImageNameSubstitutor (composite of 'ConfigurationFileImageNameSubstitutor' and 'PrefixingImageNameSubstitutor') +[INFO] Found Docker environment with local Unix socket (unix:///var/run/docker.sock) +[INFO] Docker host IP address is localhost +[INFO] Creating container for image: testcontainers/ryuk:0.14.0 +[INFO] Container testcontainers/ryuk:0.14.0 started in PT0.44146219S +[INFO] ✔︎ Docker server version should be at least 1.6.0 +[INFO] Container postgres:17 started in PT5.716666928S +[WARNING] [stderr] Apr 08, 2026 9:25:10 PM liquibase.database +[WARNING] [stderr] Apr 08, 2026 9:25:10 PM liquibase.changelog +[WARNING] [stderr] INFO: Reading resource: db/changelog/2024/db.changelog.2024-1719408042.sql +[WARNING] [stderr] Apr 08, 2026 9:25:10 PM liquibase.changelog +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.webapp.user.PermissionCompareTest +[INFO] Running com.example.webapp.user.ModifyTeamTest +[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.044 s -- in com.example.webapp.user.PermissionCompareTest +[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.065 s -- in com.example.webapp.user.PermissionGrouperTest +[INFO] Running com.example.webapp.user.PermissionProcessorTest +[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 s -- in com.example.webapp.user.PermissionProcessorTest +[INFO] Running com.example.webapp.user.ProductHelperTest +[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.003 s -- in com.example.webapp.user.ProductHelperTest +[INFO] Running com.example.webapp.user.RelayStateUtilTest +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.015 s -- in com.example.webapp.user.RelayStateUtilTest +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1719408042.sql::dev2:1719408042-1::dev2 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1719478245.yaml::1719478245-1::dev1 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1719571809.yaml::1719571809-1::dev1 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1721123826.yaml::1721123826-1::dev1 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1726125249.yaml::1726125249-1::dev1 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1726133977.yaml::1726133977-1::dev2 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1726647573.yaml::1726647573-1::dev1 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1727864000.sql::1727864000-1::dev3 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1727939482.yaml::1727939482-1::dev1 +[INFO] [stdout] Running Changeset: db/changelog/2024/db.changelog.2024-1728049660.yaml::1728049660-1::dev1 +[INFO] Tests run: 61, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 18.44 s -- in com.example.webapp.scim.ScimUserControllerIntegrationTest +[INFO] Results: +[INFO] +[INFO] Tests run: 959, Failures: 0, Errors: 0, Skipped: 9 +[INFO] +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] BUILD SUCCESS +[INFO] -------------------------------------------------------------------------------------------------------------------------- +[INFO] Total time: 01:32 min +[INFO] Finished at: 2026-04-08T21:26:30+02:00 +[INFO] -------------------------------------------------------------------------------------------------------------------------- diff --git a/tests/fixtures/mvn_test_pass_mavenmcp.txt b/tests/fixtures/mvn_test_pass_mavenmcp.txt new file mode 100644 index 000000000..beb535770 --- /dev/null +++ b/tests/fixtures/mvn_test_pass_mavenmcp.txt @@ -0,0 +1,35 @@ +[INFO] Scanning for projects... +[INFO] +[INFO] -------------------< com.example:my-app >-------------------- +[INFO] Building my-app 1.0-SNAPSHOT +[INFO] from pom.xml +[INFO] --------------------------------[ jar ]--------------------------------- +[INFO] +[INFO] --- maven-compiler-plugin:3.13.0:compile (default-compile) --- +[INFO] Nothing to compile - all classes are up to date. +[INFO] +[INFO] --- maven-surefire-plugin:3.5.4:test (default-test) --- +[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider +[INFO] +[INFO] ------------------------------------------------------- +[INFO] T E S T S +[INFO] ------------------------------------------------------- +[INFO] Running com.example.config.AppConfigTest +[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.047 s -- in com.example.config.AppConfigTest +[INFO] Running com.example.service.UserServiceTest +[INFO] Tests run: 12, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.009 s -- in com.example.service.UserServiceTest +[INFO] Running com.example.controller.ApiControllerTest +[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 1.325 s -- in com.example.controller.ApiControllerTest +[INFO] Running com.example.repository.ItemRepositoryTest +[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 2.060 s -- in com.example.repository.ItemRepositoryTest +[INFO] +[INFO] Results: +[INFO] +[INFO] Tests run: 183, Failures: 0, Errors: 0, Skipped: 0 +[INFO] +[INFO] ------------------------------------------------------------------------ +[INFO] BUILD SUCCESS +[INFO] ------------------------------------------------------------------------ +[INFO] Total time: 4.748 s +[INFO] Finished at: 2026-04-08T18:26:55+02:00 +[INFO] ------------------------------------------------------------------------