@hasna/terminal 2.2.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.js +29 -12
- package/package.json +1 -1
- package/src/ai.ts +50 -36
- package/src/cli.tsx +29 -12
- package/src/context-hints.ts +89 -0
- package/src/discover.ts +238 -0
- package/src/economy.ts +53 -0
- package/src/output-store.ts +65 -0
- package/src/providers/index.ts +4 -4
- package/src/sessions-db.ts +81 -0
- package/temp/rtk/.claude/agents/code-reviewer.md +221 -0
- package/temp/rtk/.claude/agents/debugger.md +519 -0
- package/temp/rtk/.claude/agents/rtk-testing-specialist.md +461 -0
- package/temp/rtk/.claude/agents/rust-rtk.md +511 -0
- package/temp/rtk/.claude/agents/technical-writer.md +355 -0
- package/temp/rtk/.claude/commands/diagnose.md +352 -0
- package/temp/rtk/.claude/commands/test-routing.md +362 -0
- package/temp/rtk/.claude/hooks/bash/pre-commit-format.sh +16 -0
- package/temp/rtk/.claude/hooks/rtk-rewrite.sh +70 -0
- package/temp/rtk/.claude/hooks/rtk-suggest.sh +152 -0
- package/temp/rtk/.claude/rules/cli-testing.md +526 -0
- package/temp/rtk/.claude/skills/issue-triage/SKILL.md +348 -0
- package/temp/rtk/.claude/skills/issue-triage/templates/issue-comment.md +134 -0
- package/temp/rtk/.claude/skills/performance.md +435 -0
- package/temp/rtk/.claude/skills/pr-triage/SKILL.md +315 -0
- package/temp/rtk/.claude/skills/pr-triage/templates/review-comment.md +71 -0
- package/temp/rtk/.claude/skills/repo-recap.md +206 -0
- package/temp/rtk/.claude/skills/rtk-tdd/SKILL.md +78 -0
- package/temp/rtk/.claude/skills/rtk-tdd/references/testing-patterns.md +124 -0
- package/temp/rtk/.claude/skills/security-guardian.md +503 -0
- package/temp/rtk/.claude/skills/ship.md +404 -0
- package/temp/rtk/.github/workflows/benchmark.yml +34 -0
- package/temp/rtk/.github/workflows/dco-check.yaml +12 -0
- package/temp/rtk/.github/workflows/release-please.yml +51 -0
- package/temp/rtk/.github/workflows/release.yml +343 -0
- package/temp/rtk/.github/workflows/security-check.yml +135 -0
- package/temp/rtk/.github/workflows/validate-docs.yml +78 -0
- package/temp/rtk/.release-please-manifest.json +3 -0
- package/temp/rtk/ARCHITECTURE.md +1491 -0
- package/temp/rtk/CHANGELOG.md +640 -0
- package/temp/rtk/CLAUDE.md +605 -0
- package/temp/rtk/CONTRIBUTING.md +199 -0
- package/temp/rtk/Cargo.lock +1668 -0
- package/temp/rtk/Cargo.toml +64 -0
- package/temp/rtk/Formula/rtk.rb +43 -0
- package/temp/rtk/INSTALL.md +390 -0
- package/temp/rtk/LICENSE +21 -0
- package/temp/rtk/README.md +386 -0
- package/temp/rtk/README_es.md +159 -0
- package/temp/rtk/README_fr.md +197 -0
- package/temp/rtk/README_ja.md +159 -0
- package/temp/rtk/README_ko.md +159 -0
- package/temp/rtk/README_zh.md +167 -0
- package/temp/rtk/ROADMAP.md +15 -0
- package/temp/rtk/SECURITY.md +217 -0
- package/temp/rtk/TEST_EXEC_TIME.md +102 -0
- package/temp/rtk/build.rs +57 -0
- package/temp/rtk/docs/AUDIT_GUIDE.md +432 -0
- package/temp/rtk/docs/FEATURES.md +1410 -0
- package/temp/rtk/docs/TROUBLESHOOTING.md +309 -0
- package/temp/rtk/docs/filter-workflow.md +102 -0
- package/temp/rtk/docs/images/gain-dashboard.jpg +0 -0
- package/temp/rtk/docs/tracking.md +583 -0
- package/temp/rtk/hooks/opencode-rtk.ts +39 -0
- package/temp/rtk/hooks/rtk-awareness.md +29 -0
- package/temp/rtk/hooks/rtk-rewrite.sh +61 -0
- package/temp/rtk/hooks/test-rtk-rewrite.sh +442 -0
- package/temp/rtk/install.sh +124 -0
- package/temp/rtk/release-please-config.json +10 -0
- package/temp/rtk/scripts/benchmark.sh +592 -0
- package/temp/rtk/scripts/check-installation.sh +162 -0
- package/temp/rtk/scripts/install-local.sh +37 -0
- package/temp/rtk/scripts/rtk-economics.sh +137 -0
- package/temp/rtk/scripts/test-all.sh +561 -0
- package/temp/rtk/scripts/test-aristote.sh +227 -0
- package/temp/rtk/scripts/test-tracking.sh +79 -0
- package/temp/rtk/scripts/update-readme-metrics.sh +32 -0
- package/temp/rtk/scripts/validate-docs.sh +73 -0
- package/temp/rtk/src/aws_cmd.rs +880 -0
- package/temp/rtk/src/binlog.rs +1645 -0
- package/temp/rtk/src/cargo_cmd.rs +1727 -0
- package/temp/rtk/src/cc_economics.rs +1157 -0
- package/temp/rtk/src/ccusage.rs +340 -0
- package/temp/rtk/src/config.rs +187 -0
- package/temp/rtk/src/container.rs +855 -0
- package/temp/rtk/src/curl_cmd.rs +134 -0
- package/temp/rtk/src/deps.rs +268 -0
- package/temp/rtk/src/diff_cmd.rs +367 -0
- package/temp/rtk/src/discover/mod.rs +274 -0
- package/temp/rtk/src/discover/provider.rs +388 -0
- package/temp/rtk/src/discover/registry.rs +2022 -0
- package/temp/rtk/src/discover/report.rs +202 -0
- package/temp/rtk/src/discover/rules.rs +667 -0
- package/temp/rtk/src/display_helpers.rs +402 -0
- package/temp/rtk/src/dotnet_cmd.rs +1771 -0
- package/temp/rtk/src/dotnet_format_report.rs +133 -0
- package/temp/rtk/src/dotnet_trx.rs +593 -0
- package/temp/rtk/src/env_cmd.rs +204 -0
- package/temp/rtk/src/filter.rs +462 -0
- package/temp/rtk/src/filters/README.md +52 -0
- package/temp/rtk/src/filters/ansible-playbook.toml +34 -0
- package/temp/rtk/src/filters/basedpyright.toml +47 -0
- package/temp/rtk/src/filters/biome.toml +45 -0
- package/temp/rtk/src/filters/brew-install.toml +37 -0
- package/temp/rtk/src/filters/composer-install.toml +40 -0
- package/temp/rtk/src/filters/df.toml +16 -0
- package/temp/rtk/src/filters/dotnet-build.toml +64 -0
- package/temp/rtk/src/filters/du.toml +16 -0
- package/temp/rtk/src/filters/fail2ban-client.toml +15 -0
- package/temp/rtk/src/filters/gcc.toml +49 -0
- package/temp/rtk/src/filters/gcloud.toml +22 -0
- package/temp/rtk/src/filters/hadolint.toml +24 -0
- package/temp/rtk/src/filters/helm.toml +29 -0
- package/temp/rtk/src/filters/iptables.toml +27 -0
- package/temp/rtk/src/filters/jj.toml +28 -0
- package/temp/rtk/src/filters/jq.toml +24 -0
- package/temp/rtk/src/filters/make.toml +41 -0
- package/temp/rtk/src/filters/markdownlint.toml +24 -0
- package/temp/rtk/src/filters/mix-compile.toml +27 -0
- package/temp/rtk/src/filters/mix-format.toml +15 -0
- package/temp/rtk/src/filters/mvn-build.toml +44 -0
- package/temp/rtk/src/filters/oxlint.toml +43 -0
- package/temp/rtk/src/filters/ping.toml +63 -0
- package/temp/rtk/src/filters/pio-run.toml +40 -0
- package/temp/rtk/src/filters/poetry-install.toml +50 -0
- package/temp/rtk/src/filters/pre-commit.toml +35 -0
- package/temp/rtk/src/filters/ps.toml +16 -0
- package/temp/rtk/src/filters/quarto-render.toml +41 -0
- package/temp/rtk/src/filters/rsync.toml +48 -0
- package/temp/rtk/src/filters/shellcheck.toml +27 -0
- package/temp/rtk/src/filters/shopify-theme.toml +29 -0
- package/temp/rtk/src/filters/skopeo.toml +45 -0
- package/temp/rtk/src/filters/sops.toml +16 -0
- package/temp/rtk/src/filters/ssh.toml +44 -0
- package/temp/rtk/src/filters/stat.toml +34 -0
- package/temp/rtk/src/filters/swift-build.toml +41 -0
- package/temp/rtk/src/filters/systemctl-status.toml +33 -0
- package/temp/rtk/src/filters/terraform-plan.toml +35 -0
- package/temp/rtk/src/filters/tofu-fmt.toml +16 -0
- package/temp/rtk/src/filters/tofu-init.toml +38 -0
- package/temp/rtk/src/filters/tofu-plan.toml +35 -0
- package/temp/rtk/src/filters/tofu-validate.toml +17 -0
- package/temp/rtk/src/filters/trunk-build.toml +39 -0
- package/temp/rtk/src/filters/ty.toml +50 -0
- package/temp/rtk/src/filters/uv-sync.toml +37 -0
- package/temp/rtk/src/filters/xcodebuild.toml +99 -0
- package/temp/rtk/src/filters/yamllint.toml +25 -0
- package/temp/rtk/src/find_cmd.rs +598 -0
- package/temp/rtk/src/format_cmd.rs +386 -0
- package/temp/rtk/src/gain.rs +723 -0
- package/temp/rtk/src/gh_cmd.rs +1651 -0
- package/temp/rtk/src/git.rs +2012 -0
- package/temp/rtk/src/go_cmd.rs +592 -0
- package/temp/rtk/src/golangci_cmd.rs +254 -0
- package/temp/rtk/src/grep_cmd.rs +288 -0
- package/temp/rtk/src/gt_cmd.rs +810 -0
- package/temp/rtk/src/hook_audit_cmd.rs +283 -0
- package/temp/rtk/src/hook_check.rs +171 -0
- package/temp/rtk/src/init.rs +1859 -0
- package/temp/rtk/src/integrity.rs +537 -0
- package/temp/rtk/src/json_cmd.rs +231 -0
- package/temp/rtk/src/learn/detector.rs +628 -0
- package/temp/rtk/src/learn/mod.rs +119 -0
- package/temp/rtk/src/learn/report.rs +184 -0
- package/temp/rtk/src/lint_cmd.rs +694 -0
- package/temp/rtk/src/local_llm.rs +316 -0
- package/temp/rtk/src/log_cmd.rs +248 -0
- package/temp/rtk/src/ls.rs +324 -0
- package/temp/rtk/src/main.rs +2482 -0
- package/temp/rtk/src/mypy_cmd.rs +389 -0
- package/temp/rtk/src/next_cmd.rs +241 -0
- package/temp/rtk/src/npm_cmd.rs +236 -0
- package/temp/rtk/src/parser/README.md +267 -0
- package/temp/rtk/src/parser/error.rs +46 -0
- package/temp/rtk/src/parser/formatter.rs +336 -0
- package/temp/rtk/src/parser/mod.rs +311 -0
- package/temp/rtk/src/parser/types.rs +119 -0
- package/temp/rtk/src/pip_cmd.rs +302 -0
- package/temp/rtk/src/playwright_cmd.rs +479 -0
- package/temp/rtk/src/pnpm_cmd.rs +573 -0
- package/temp/rtk/src/prettier_cmd.rs +221 -0
- package/temp/rtk/src/prisma_cmd.rs +482 -0
- package/temp/rtk/src/psql_cmd.rs +382 -0
- package/temp/rtk/src/pytest_cmd.rs +384 -0
- package/temp/rtk/src/read.rs +217 -0
- package/temp/rtk/src/rewrite_cmd.rs +50 -0
- package/temp/rtk/src/ruff_cmd.rs +402 -0
- package/temp/rtk/src/runner.rs +271 -0
- package/temp/rtk/src/summary.rs +297 -0
- package/temp/rtk/src/tee.rs +405 -0
- package/temp/rtk/src/telemetry.rs +248 -0
- package/temp/rtk/src/toml_filter.rs +1655 -0
- package/temp/rtk/src/tracking.rs +1416 -0
- package/temp/rtk/src/tree.rs +209 -0
- package/temp/rtk/src/tsc_cmd.rs +259 -0
- package/temp/rtk/src/utils.rs +432 -0
- package/temp/rtk/src/verify_cmd.rs +47 -0
- package/temp/rtk/src/vitest_cmd.rs +385 -0
- package/temp/rtk/src/wc_cmd.rs +401 -0
- package/temp/rtk/src/wget_cmd.rs +260 -0
- package/temp/rtk/tests/fixtures/dotnet/build_failed.txt +11 -0
- package/temp/rtk/tests/fixtures/dotnet/format_changes.json +31 -0
- package/temp/rtk/tests/fixtures/dotnet/format_empty.json +1 -0
- package/temp/rtk/tests/fixtures/dotnet/format_success.json +12 -0
- package/temp/rtk/tests/fixtures/dotnet/test_failed.txt +18 -0
|
@@ -0,0 +1,1491 @@
|
|
|
1
|
+
# rtk Architecture Documentation
|
|
2
|
+
|
|
3
|
+
> **rtk (Rust Token Killer)** - A high-performance CLI proxy that minimizes LLM token consumption through intelligent output filtering and compression.
|
|
4
|
+
|
|
5
|
+
This document provides a comprehensive architectural overview of rtk, including system design, data flows, module organization, and implementation patterns.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Table of Contents
|
|
10
|
+
|
|
11
|
+
1. [System Overview](#system-overview)
|
|
12
|
+
2. [Command Lifecycle](#command-lifecycle)
|
|
13
|
+
3. [Module Organization](#module-organization)
|
|
14
|
+
4. [Filtering Strategies](#filtering-strategies)
|
|
15
|
+
5. [Shared Infrastructure](#shared-infrastructure)
|
|
16
|
+
6. [Token Tracking System](#token-tracking-system)
|
|
17
|
+
7. [Global Flags Architecture](#global-flags-architecture)
|
|
18
|
+
8. [Error Handling](#error-handling)
|
|
19
|
+
9. [Configuration System](#configuration-system)
|
|
20
|
+
10. [Module Development Pattern](#module-development-pattern)
|
|
21
|
+
11. [Build Optimizations](#build-optimizations)
|
|
22
|
+
12. [Extensibility Guide](#extensibility-guide)
|
|
23
|
+
|
|
24
|
+
---
|
|
25
|
+
|
|
26
|
+
## System Overview
|
|
27
|
+
|
|
28
|
+
### Proxy Pattern Architecture
|
|
29
|
+
|
|
30
|
+
```
|
|
31
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
32
|
+
│ rtk - Token Optimization Proxy │
|
|
33
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
34
|
+
|
|
35
|
+
User Input CLI Layer Router Module Layer
|
|
36
|
+
────────── ───────── ────── ────────────
|
|
37
|
+
|
|
38
|
+
$ rtk git log ─→ Clap Parser ─→ Commands ─→ git::run()
|
|
39
|
+
-v --oneline (main.rs) enum match
|
|
40
|
+
• Parse args Execute: git log
|
|
41
|
+
• Extract flags Capture output
|
|
42
|
+
• Route command ↓
|
|
43
|
+
Filter/Compress
|
|
44
|
+
↓
|
|
45
|
+
$ 3 commits ←─ Terminal ←─ Format ←─ Compact Stats
|
|
46
|
+
+142/-89 colored optimized (90% reduction)
|
|
47
|
+
output ↓
|
|
48
|
+
tracking::track()
|
|
49
|
+
↓
|
|
50
|
+
SQLite INSERT
|
|
51
|
+
(~/.local/share/rtk/)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Key Components
|
|
55
|
+
|
|
56
|
+
| Component | Location | Responsibility |
|
|
57
|
+
|-----------|----------|----------------|
|
|
58
|
+
| **CLI Parser** | main.rs | Clap-based argument parsing, global flags |
|
|
59
|
+
| **Command Router** | main.rs | Dispatch to specialized modules |
|
|
60
|
+
| **Module Layer** | src/*_cmd.rs, src/git.rs, etc. | Command execution + filtering |
|
|
61
|
+
| **Shared Utils** | utils.rs | Package manager detection, text processing |
|
|
62
|
+
| **Filter Engine** | filter.rs | Language-aware code filtering |
|
|
63
|
+
| **Tracking** | tracking.rs | SQLite-based token metrics |
|
|
64
|
+
| **Config** | config.rs, init.rs | User preferences, LLM integration |
|
|
65
|
+
|
|
66
|
+
### Design Principles
|
|
67
|
+
|
|
68
|
+
1. **Single Responsibility**: Each module handles one command type
|
|
69
|
+
2. **Minimal Overhead**: ~5-15ms proxy overhead per command
|
|
70
|
+
3. **Exit Code Preservation**: CI/CD reliability through proper exit code propagation
|
|
71
|
+
4. **Fail-Safe**: If filtering fails, fall back to original output
|
|
72
|
+
5. **Transparent**: Users can always see raw output with `-v` flags
|
|
73
|
+
|
|
74
|
+
### Hook Architecture (v0.9.5+)
|
|
75
|
+
|
|
76
|
+
The recommended deployment mode uses a Claude Code PreToolUse hook for 100% transparent command rewriting.
|
|
77
|
+
|
|
78
|
+
```
|
|
79
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
80
|
+
│ Hook-Based Command Rewriting │
|
|
81
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
82
|
+
|
|
83
|
+
Claude Code settings.json rtk-rewrite.sh RTK binary
|
|
84
|
+
│ │ │ │
|
|
85
|
+
│ Bash: "git status" │ │ │
|
|
86
|
+
│ ─────────────────────►│ │ │
|
|
87
|
+
│ │ PreToolUse hook │ │
|
|
88
|
+
│ │ ───────────────────►│ │
|
|
89
|
+
│ │ │ detect: git │
|
|
90
|
+
│ │ │ rewrite: │
|
|
91
|
+
│ │ │ rtk git status │
|
|
92
|
+
│ │◄────────────────────│ │
|
|
93
|
+
│ │ updatedInput │ │
|
|
94
|
+
│ │ │
|
|
95
|
+
│ execute: rtk git status ────────────────────────────────────────►
|
|
96
|
+
│ │ run git
|
|
97
|
+
│ │ filter
|
|
98
|
+
│ │ track
|
|
99
|
+
│◄──────────────────────────────────────────────────────────────────
|
|
100
|
+
│ "3 modified, 1 untracked ✓" (~10 tokens vs ~200 raw)
|
|
101
|
+
│
|
|
102
|
+
│ Claude never sees the rewrite — it only sees optimized output.
|
|
103
|
+
|
|
104
|
+
Files:
|
|
105
|
+
~/.claude/hooks/rtk-rewrite.sh ← thin delegator (calls `rtk rewrite`, ~50 lines)
|
|
106
|
+
~/.claude/settings.json ← hook registry (PreToolUse registration)
|
|
107
|
+
~/.claude/RTK.md ← minimal context hint (10 lines)
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Two hook strategies:
|
|
111
|
+
|
|
112
|
+
```
|
|
113
|
+
Auto-Rewrite (default) Suggest (non-intrusive)
|
|
114
|
+
───────────────────── ────────────────────────
|
|
115
|
+
Hook intercepts command Hook emits systemMessage hint
|
|
116
|
+
Rewrites before execution Claude decides autonomously
|
|
117
|
+
100% adoption ~70-85% adoption
|
|
118
|
+
Zero context overhead Minimal context overhead
|
|
119
|
+
Best for: production Best for: learning / auditing
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Command Lifecycle
|
|
125
|
+
|
|
126
|
+
### Six-Phase Execution Flow
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
130
|
+
│ Command Execution Lifecycle │
|
|
131
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
132
|
+
|
|
133
|
+
Phase 1: PARSE
|
|
134
|
+
──────────────
|
|
135
|
+
$ rtk git log --oneline -5 -v
|
|
136
|
+
|
|
137
|
+
Clap Parser extracts:
|
|
138
|
+
• Command: Commands::Git
|
|
139
|
+
• Args: ["log", "--oneline", "-5"]
|
|
140
|
+
• Flags: verbose = 1
|
|
141
|
+
ultra_compact = false
|
|
142
|
+
|
|
143
|
+
↓
|
|
144
|
+
|
|
145
|
+
Phase 2: ROUTE
|
|
146
|
+
──────────────
|
|
147
|
+
main.rs:match Commands::Git { args, .. }
|
|
148
|
+
↓
|
|
149
|
+
git::run(args, verbose)
|
|
150
|
+
|
|
151
|
+
↓
|
|
152
|
+
|
|
153
|
+
Phase 3: EXECUTE
|
|
154
|
+
────────────────
|
|
155
|
+
std::process::Command::new("git")
|
|
156
|
+
.args(["log", "--oneline", "-5"])
|
|
157
|
+
.output()?
|
|
158
|
+
|
|
159
|
+
Output captured:
|
|
160
|
+
• stdout: "abc123 Fix bug\ndef456 Add feature\n..." (500 chars)
|
|
161
|
+
• stderr: "" (empty)
|
|
162
|
+
• exit_code: 0
|
|
163
|
+
|
|
164
|
+
↓
|
|
165
|
+
|
|
166
|
+
Phase 4: FILTER
|
|
167
|
+
───────────────
|
|
168
|
+
git::format_git_output(stdout, "log", verbose)
|
|
169
|
+
|
|
170
|
+
Strategy: Stats Extraction
|
|
171
|
+
• Count commits: 5
|
|
172
|
+
• Extract stats: +142/-89
|
|
173
|
+
• Compress: "5 commits, +142/-89"
|
|
174
|
+
|
|
175
|
+
Filtered: 20 chars (96% reduction)
|
|
176
|
+
|
|
177
|
+
↓
|
|
178
|
+
|
|
179
|
+
Phase 5: PRINT
|
|
180
|
+
──────────────
|
|
181
|
+
if verbose > 0 {
|
|
182
|
+
eprintln!("Git log summary:"); // Debug
|
|
183
|
+
}
|
|
184
|
+
println!("{}", colored_output); // User output
|
|
185
|
+
|
|
186
|
+
Terminal shows: "5 commits, +142/-89 ✓"
|
|
187
|
+
|
|
188
|
+
↓
|
|
189
|
+
|
|
190
|
+
Phase 6: TRACK
|
|
191
|
+
──────────────
|
|
192
|
+
tracking::track(
|
|
193
|
+
original_cmd: "git log --oneline -5",
|
|
194
|
+
rtk_cmd: "rtk git log --oneline -5",
|
|
195
|
+
input: &raw_output, // 500 chars
|
|
196
|
+
output: &filtered // 20 chars
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
↓
|
|
200
|
+
|
|
201
|
+
SQLite INSERT:
|
|
202
|
+
• input_tokens: 125 (500 / 4)
|
|
203
|
+
• output_tokens: 5 (20 / 4)
|
|
204
|
+
• savings_pct: 96.0
|
|
205
|
+
• timestamp: now()
|
|
206
|
+
|
|
207
|
+
Database: ~/.local/share/rtk/history.db
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Verbosity Levels
|
|
211
|
+
|
|
212
|
+
```
|
|
213
|
+
-v (Level 1): Show debug messages
|
|
214
|
+
Example: eprintln!("Git log summary:");
|
|
215
|
+
|
|
216
|
+
-vv (Level 2): Show command being executed
|
|
217
|
+
Example: eprintln!("Executing: git log --oneline -5");
|
|
218
|
+
|
|
219
|
+
-vvv (Level 3): Show raw output before filtering
|
|
220
|
+
Example: eprintln!("Raw output:\n{}", stdout);
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## Module Organization
|
|
226
|
+
|
|
227
|
+
### Complete Module Map (30 Modules)
|
|
228
|
+
|
|
229
|
+
```
|
|
230
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
231
|
+
│ Module Organization │
|
|
232
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
233
|
+
|
|
234
|
+
Category Module Commands Savings File
|
|
235
|
+
──────────────────────────────────────────────────────────────────────────
|
|
236
|
+
|
|
237
|
+
GIT git.rs status, diff, log 85-99% ✓
|
|
238
|
+
add, commit, push
|
|
239
|
+
branch, checkout
|
|
240
|
+
|
|
241
|
+
CODE SEARCH grep_cmd.rs grep 60-80% ✓
|
|
242
|
+
diff_cmd.rs diff 70-85% ✓
|
|
243
|
+
find_cmd.rs find 50-70% ✓
|
|
244
|
+
|
|
245
|
+
FILE OPS ls.rs ls 50-70% ✓
|
|
246
|
+
read.rs read 40-90% ✓
|
|
247
|
+
|
|
248
|
+
EXECUTION runner.rs err, test 60-99% ✓
|
|
249
|
+
summary.rs smart (heuristic) 50-80% ✓
|
|
250
|
+
local_llm.rs smart (LLM mode) 60-90% ✓
|
|
251
|
+
|
|
252
|
+
LOGS/DATA log_cmd.rs log 70-90% ✓
|
|
253
|
+
json_cmd.rs json 80-95% ✓
|
|
254
|
+
|
|
255
|
+
JS/TS STACK lint_cmd.rs lint 84% ✓
|
|
256
|
+
tsc_cmd.rs tsc 83% ✓
|
|
257
|
+
next_cmd.rs next 87% ✓
|
|
258
|
+
prettier_cmd.rs prettier 70% ✓
|
|
259
|
+
playwright_cmd.rs playwright 94% ✓
|
|
260
|
+
prisma_cmd.rs prisma 88% ✓
|
|
261
|
+
vitest_cmd.rs vitest 99.5% ✓
|
|
262
|
+
pnpm_cmd.rs pnpm 70-90% ✓
|
|
263
|
+
|
|
264
|
+
CONTAINERS container.rs podman, docker 60-80% ✓
|
|
265
|
+
|
|
266
|
+
VCS gh_cmd.rs gh 26-87% ✓
|
|
267
|
+
|
|
268
|
+
PYTHON ruff_cmd.rs ruff check/format 80%+ ✓
|
|
269
|
+
pytest_cmd.rs pytest 90%+ ✓
|
|
270
|
+
pip_cmd.rs pip list/outdated 70-85% ✓
|
|
271
|
+
|
|
272
|
+
GO go_cmd.rs go test/build/vet 75-90% ✓
|
|
273
|
+
golangci_cmd.rs golangci-lint 85% ✓
|
|
274
|
+
|
|
275
|
+
NETWORK wget_cmd.rs wget 85-95% ✓
|
|
276
|
+
curl_cmd.rs curl 70% ✓
|
|
277
|
+
|
|
278
|
+
INFRA aws_cmd.rs aws 80% ✓
|
|
279
|
+
psql_cmd.rs psql 75% ✓
|
|
280
|
+
|
|
281
|
+
DEPENDENCIES deps.rs deps 80-90% ✓
|
|
282
|
+
|
|
283
|
+
ENVIRONMENT env_cmd.rs env 60-80% ✓
|
|
284
|
+
|
|
285
|
+
SYSTEM init.rs init N/A ✓
|
|
286
|
+
gain.rs gain N/A ✓
|
|
287
|
+
config.rs (internal) N/A ✓
|
|
288
|
+
rewrite_cmd.rs rewrite N/A ✓
|
|
289
|
+
|
|
290
|
+
SHARED utils.rs Helpers N/A ✓
|
|
291
|
+
filter.rs Language filters N/A ✓
|
|
292
|
+
tracking.rs Token tracking N/A ✓
|
|
293
|
+
tee.rs Full output recovery N/A ✓
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
**Total: 60 modules** (38 command modules + 22 infrastructure modules)
|
|
297
|
+
|
|
298
|
+
### Module Count Breakdown
|
|
299
|
+
|
|
300
|
+
- **Command Modules**: 34 (directly exposed to users)
|
|
301
|
+
- **Infrastructure Modules**: 20 (utils, filter, tracking, tee, config, init, gain, toml_filter, verify_cmd, etc.)
|
|
302
|
+
- **Git Commands**: 7 operations (status, diff, log, add, commit, push, branch/checkout)
|
|
303
|
+
- **JS/TS Tooling**: 8 modules (modern frontend/fullstack development)
|
|
304
|
+
- **Python Tooling**: 3 modules (ruff, pytest, pip)
|
|
305
|
+
- **Go Tooling**: 2 modules (go test/build/vet, golangci-lint)
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## Filtering Strategies
|
|
310
|
+
|
|
311
|
+
### Strategy Matrix
|
|
312
|
+
|
|
313
|
+
```
|
|
314
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
315
|
+
│ Filtering Strategy Taxonomy │
|
|
316
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
317
|
+
|
|
318
|
+
Strategy Modules Technique Reduction
|
|
319
|
+
──────────────────────────────────────────────────────────────────────────
|
|
320
|
+
|
|
321
|
+
1. STATS EXTRACTION
|
|
322
|
+
┌──────────────┐
|
|
323
|
+
│ Raw: 5000 │ → Count/aggregate → "3 files, +142/-89" 90-99%
|
|
324
|
+
│ lines │ Drop details
|
|
325
|
+
└──────────────┘
|
|
326
|
+
|
|
327
|
+
Used by: git status, git log, git diff, pnpm list
|
|
328
|
+
|
|
329
|
+
2. ERROR ONLY
|
|
330
|
+
┌──────────────┐
|
|
331
|
+
│ stdout+err │ → stderr only → "Error: X failed" 60-80%
|
|
332
|
+
│ Mixed │ Drop stdout
|
|
333
|
+
└──────────────┘
|
|
334
|
+
|
|
335
|
+
Used by: runner (err mode), test failures
|
|
336
|
+
|
|
337
|
+
3. GROUPING BY PATTERN
|
|
338
|
+
┌──────────────┐
|
|
339
|
+
│ 100 errors │ → Group by rule → "no-unused-vars: 23" 80-90%
|
|
340
|
+
│ Scattered │ Count/summarize "semi: 45"
|
|
341
|
+
└──────────────┘
|
|
342
|
+
|
|
343
|
+
Used by: lint, tsc, grep (group by file/rule/error code)
|
|
344
|
+
|
|
345
|
+
4. DEDUPLICATION
|
|
346
|
+
┌──────────────┐
|
|
347
|
+
│ Repeated │ → Unique + count → "[ERROR] ... (×5)" 70-85%
|
|
348
|
+
│ Log lines │
|
|
349
|
+
└──────────────┘
|
|
350
|
+
|
|
351
|
+
Used by: log_cmd (identify patterns, count occurrences)
|
|
352
|
+
|
|
353
|
+
5. STRUCTURE ONLY
|
|
354
|
+
┌──────────────┐
|
|
355
|
+
│ JSON with │ → Keys + types → {user: {...}, ...} 80-95%
|
|
356
|
+
│ Large values │ Strip values
|
|
357
|
+
└──────────────┘
|
|
358
|
+
|
|
359
|
+
Used by: json_cmd (schema extraction)
|
|
360
|
+
|
|
361
|
+
6. CODE FILTERING
|
|
362
|
+
┌──────────────┐
|
|
363
|
+
│ Source code │ → Filter by level:
|
|
364
|
+
│ │ • none → Keep all 0%
|
|
365
|
+
│ │ • minimal → Strip comments 20-40%
|
|
366
|
+
│ │ • aggressive → Strip bodies 60-90%
|
|
367
|
+
└──────────────┘
|
|
368
|
+
|
|
369
|
+
Used by: read, smart (language-aware stripping via filter.rs)
|
|
370
|
+
|
|
371
|
+
7. FAILURE FOCUS
|
|
372
|
+
┌──────────────┐
|
|
373
|
+
│ 100 tests │ → Failures only → "2 failed:" 94-99%
|
|
374
|
+
│ Mixed │ Hide passing " • test_auth"
|
|
375
|
+
└──────────────┘
|
|
376
|
+
|
|
377
|
+
Used by: vitest, playwright, runner (test mode)
|
|
378
|
+
|
|
379
|
+
8. TREE COMPRESSION
|
|
380
|
+
┌──────────────┐
|
|
381
|
+
│ Flat list │ → Tree hierarchy → "src/" 50-70%
|
|
382
|
+
│ 50 files │ Aggregate dirs " ├─ lib/ (12)"
|
|
383
|
+
└──────────────┘
|
|
384
|
+
|
|
385
|
+
Used by: ls (directory tree with counts)
|
|
386
|
+
|
|
387
|
+
9. PROGRESS FILTERING
|
|
388
|
+
┌──────────────┐
|
|
389
|
+
│ ANSI bars │ → Strip progress → "✓ Downloaded" 85-95%
|
|
390
|
+
│ Live updates │ Final result
|
|
391
|
+
└──────────────┘
|
|
392
|
+
|
|
393
|
+
Used by: wget, pnpm install (strip ANSI escape sequences)
|
|
394
|
+
|
|
395
|
+
10. JSON/TEXT DUAL MODE
|
|
396
|
+
┌──────────────┐
|
|
397
|
+
│ Tool output │ → JSON when available → Structured data 80%+
|
|
398
|
+
│ │ Text otherwise Fallback parse
|
|
399
|
+
└──────────────┘
|
|
400
|
+
|
|
401
|
+
Used by: ruff (check → JSON, format → text), pip (list/show → JSON)
|
|
402
|
+
|
|
403
|
+
11. STATE MACHINE PARSING
|
|
404
|
+
┌──────────────┐
|
|
405
|
+
│ Test output │ → Track test state → "2 failed, 18 ok" 90%+
|
|
406
|
+
│ Mixed format │ Extract failures Failure details
|
|
407
|
+
└──────────────┘
|
|
408
|
+
|
|
409
|
+
Used by: pytest (text state machine: test_name → PASSED/FAILED)
|
|
410
|
+
|
|
411
|
+
12. NDJSON STREAMING
|
|
412
|
+
┌──────────────┐
|
|
413
|
+
│ Line-by-line │ → Parse each JSON → "2 fail (pkg1, pkg2)" 90%+
|
|
414
|
+
│ JSON events │ Aggregate results Compact summary
|
|
415
|
+
└──────────────┘
|
|
416
|
+
|
|
417
|
+
Used by: go test (NDJSON stream, interleaved package events)
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
### Code Filtering Levels (filter.rs)
|
|
421
|
+
|
|
422
|
+
```rust
|
|
423
|
+
// FilterLevel::None - Keep everything
|
|
424
|
+
fn calculate_total(items: &[Item]) -> i32 {
|
|
425
|
+
// Sum all items
|
|
426
|
+
items.iter().map(|i| i.value).sum()
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// FilterLevel::Minimal - Strip comments only (20-40% reduction)
|
|
430
|
+
fn calculate_total(items: &[Item]) -> i32 {
|
|
431
|
+
items.iter().map(|i| i.value).sum()
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
// FilterLevel::Aggressive - Strip comments + function bodies (60-90% reduction)
|
|
435
|
+
fn calculate_total(items: &[Item]) -> i32 { ... }
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
**Language Support**: Rust, Python, JavaScript, TypeScript, Go, C, C++, Java
|
|
439
|
+
|
|
440
|
+
**Detection**: File extension-based with fallback heuristics
|
|
441
|
+
|
|
442
|
+
---
|
|
443
|
+
|
|
444
|
+
## Python & Go Module Architecture
|
|
445
|
+
|
|
446
|
+
### Design Rationale
|
|
447
|
+
|
|
448
|
+
**Added**: 2026-02-12 (v0.15.1)
|
|
449
|
+
**Motivation**: Complete language ecosystem coverage beyond JS/TS
|
|
450
|
+
|
|
451
|
+
Python and Go modules follow distinct architectural patterns optimized for their ecosystems:
|
|
452
|
+
|
|
453
|
+
```
|
|
454
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
455
|
+
│ Python vs Go Module Design │
|
|
456
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
457
|
+
|
|
458
|
+
PYTHON (Standalone Commands) GO (Sub-Enum Pattern)
|
|
459
|
+
────────────────────────── ─────────────────────
|
|
460
|
+
|
|
461
|
+
Commands::Ruff { args } ────── Commands::Go {
|
|
462
|
+
Commands::Pytest { args } Test { args },
|
|
463
|
+
Commands::Pip { args } Build { args },
|
|
464
|
+
Vet { args }
|
|
465
|
+
}
|
|
466
|
+
├─ ruff_cmd.rs Commands::GolangciLint { args }
|
|
467
|
+
├─ pytest_cmd.rs │
|
|
468
|
+
└─ pip_cmd.rs ├─ go_cmd.rs (sub-enum router)
|
|
469
|
+
└─ golangci_cmd.rs
|
|
470
|
+
|
|
471
|
+
Mirrors: lint, prettier Mirrors: git, cargo
|
|
472
|
+
```
|
|
473
|
+
|
|
474
|
+
### Python Stack Architecture
|
|
475
|
+
|
|
476
|
+
#### Command Implementations
|
|
477
|
+
|
|
478
|
+
```
|
|
479
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
480
|
+
│ Python Commands (3 modules) │
|
|
481
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
482
|
+
|
|
483
|
+
Module Strategy Output Format Savings
|
|
484
|
+
─────────────────────────────────────────────────────────────────────────
|
|
485
|
+
|
|
486
|
+
ruff_cmd.rs JSON/TEXT DUAL • check → JSON 80%+
|
|
487
|
+
• format → text
|
|
488
|
+
|
|
489
|
+
ruff check: JSON API with structured violations
|
|
490
|
+
{
|
|
491
|
+
"violations": [{"rule": "F401", "file": "x.py", "line": 5}]
|
|
492
|
+
}
|
|
493
|
+
→ Group by rule, count occurrences
|
|
494
|
+
|
|
495
|
+
ruff format: Text diff output
|
|
496
|
+
"Fixed 12 files"
|
|
497
|
+
→ Extract summary, hide unchanged files
|
|
498
|
+
|
|
499
|
+
pytest_cmd.rs STATE MACHINE Text parser 90%+
|
|
500
|
+
|
|
501
|
+
State tracking: IDLE → TEST_START → PASSED/FAILED → SUMMARY
|
|
502
|
+
Extract:
|
|
503
|
+
• Test names (test_auth_login)
|
|
504
|
+
• Outcomes (PASSED ✓ / FAILED ✗)
|
|
505
|
+
• Failures only (hide passing tests)
|
|
506
|
+
|
|
507
|
+
pip_cmd.rs JSON PARSING JSON API 70-85%
|
|
508
|
+
|
|
509
|
+
pip list --format=json:
|
|
510
|
+
[{"name": "requests", "version": "2.28.1"}]
|
|
511
|
+
→ Compact table format
|
|
512
|
+
|
|
513
|
+
pip show <pkg>: JSON metadata
|
|
514
|
+
{"name": "...", "version": "...", "requires": [...]}
|
|
515
|
+
→ Extract key fields only
|
|
516
|
+
|
|
517
|
+
Auto-detect uv: If uv exists, use uv pip instead
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
#### Shared Infrastructure
|
|
521
|
+
|
|
522
|
+
**No Package Manager Detection**
|
|
523
|
+
Unlike JS/TS modules, Python commands don't auto-detect poetry/pipenv/pip because:
|
|
524
|
+
- `pip` is universally available (system Python)
|
|
525
|
+
- `uv` detection is explicit (binary presence check)
|
|
526
|
+
- Poetry/pipenv aren't execution wrappers (they manage virtualenvs differently)
|
|
527
|
+
|
|
528
|
+
**Virtual Environment Awareness**
|
|
529
|
+
Commands respect active virtualenv via `sys.executable` paths.
|
|
530
|
+
|
|
531
|
+
### Go Stack Architecture
|
|
532
|
+
|
|
533
|
+
#### Command Implementations
|
|
534
|
+
|
|
535
|
+
```
|
|
536
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
537
|
+
│ Go Commands (2 modules) │
|
|
538
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
539
|
+
|
|
540
|
+
Module Strategy Output Format Savings
|
|
541
|
+
─────────────────────────────────────────────────────────────────────────
|
|
542
|
+
|
|
543
|
+
go_cmd.rs SUB-ENUM ROUTER Mixed formats 75-90%
|
|
544
|
+
|
|
545
|
+
go test: NDJSON STREAMING
|
|
546
|
+
{"Action": "run", "Package": "pkg1", "Test": "TestAuth"}
|
|
547
|
+
{"Action": "fail", "Package": "pkg1", "Test": "TestAuth"}
|
|
548
|
+
|
|
549
|
+
→ Line-by-line JSON parse (handles interleaved package events)
|
|
550
|
+
→ Aggregate: "2 packages, 3 failures (pkg1::TestAuth, ...)"
|
|
551
|
+
|
|
552
|
+
go build: TEXT FILTERING
|
|
553
|
+
Errors only (compiler diagnostics)
|
|
554
|
+
→ Strip warnings, show errors with file:line
|
|
555
|
+
|
|
556
|
+
go vet: TEXT FILTERING
|
|
557
|
+
Issue detection output
|
|
558
|
+
→ Extract file:line:message triples
|
|
559
|
+
|
|
560
|
+
golangci_cmd.rs JSON PARSING JSON API 85%
|
|
561
|
+
|
|
562
|
+
golangci-lint run --out-format=json:
|
|
563
|
+
{
|
|
564
|
+
"Issues": [
|
|
565
|
+
{"FromLinter": "errcheck", "Pos": {...}, "Text": "..."}
|
|
566
|
+
]
|
|
567
|
+
}
|
|
568
|
+
→ Group by linter rule, count violations
|
|
569
|
+
→ Format: "errcheck: 12 issues, gosec: 5 issues"
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
#### Sub-Enum Pattern (go_cmd.rs)
|
|
573
|
+
|
|
574
|
+
```rust
|
|
575
|
+
// main.rs enum definition
|
|
576
|
+
Commands::Go {
|
|
577
|
+
#[command(subcommand)]
|
|
578
|
+
command: GoCommand,
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
// go_cmd.rs sub-enum
|
|
582
|
+
pub enum GoCommand {
|
|
583
|
+
Test { args: Vec<String> },
|
|
584
|
+
Build { args: Vec<String> },
|
|
585
|
+
Vet { args: Vec<String> },
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Router
|
|
589
|
+
pub fn run(command: &GoCommand, verbose: u8) -> Result<()> {
|
|
590
|
+
match command {
|
|
591
|
+
GoCommand::Test { args } => run_test(args, verbose),
|
|
592
|
+
GoCommand::Build { args } => run_build(args, verbose),
|
|
593
|
+
GoCommand::Vet { args } => run_vet(args, verbose),
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
**Why Sub-Enum?**
|
|
599
|
+
- `go test/build/vet` are semantically related (core Go toolchain)
|
|
600
|
+
- Mirrors existing git/cargo patterns (consistency)
|
|
601
|
+
- Natural CLI: `rtk go test` not `rtk gotest`
|
|
602
|
+
|
|
603
|
+
**Why golangci-lint Standalone?**
|
|
604
|
+
- Third-party tool (not core Go toolchain)
|
|
605
|
+
- Different output format (JSON API vs text)
|
|
606
|
+
- Distinct use case (comprehensive linting vs single-tool diagnostics)
|
|
607
|
+
|
|
608
|
+
### Format Strategy Decision Tree
|
|
609
|
+
|
|
610
|
+
```
|
|
611
|
+
Output format known?
|
|
612
|
+
├─ Tool provides JSON flag?
|
|
613
|
+
│ ├─ Structured data needed? → Use JSON API
|
|
614
|
+
│ │ Examples: ruff check, pip list, golangci-lint
|
|
615
|
+
│ │
|
|
616
|
+
│ └─ Simple output? → Use text mode
|
|
617
|
+
│ Examples: ruff format, go build errors
|
|
618
|
+
│
|
|
619
|
+
├─ Streaming events (NDJSON)?
|
|
620
|
+
│ └─ Line-by-line JSON parse
|
|
621
|
+
│ Examples: go test (interleaved packages)
|
|
622
|
+
│
|
|
623
|
+
└─ Plain text only?
|
|
624
|
+
├─ Stateful parsing needed? → State machine
|
|
625
|
+
│ Examples: pytest (test lifecycle tracking)
|
|
626
|
+
│
|
|
627
|
+
└─ Simple filtering? → Text filters
|
|
628
|
+
Examples: go vet, go build
|
|
629
|
+
```
|
|
630
|
+
|
|
631
|
+
### Testing Patterns
|
|
632
|
+
|
|
633
|
+
#### Python Module Tests
|
|
634
|
+
|
|
635
|
+
```rust
|
|
636
|
+
// pytest_cmd.rs tests
|
|
637
|
+
#[test]
|
|
638
|
+
fn test_pytest_state_machine() {
|
|
639
|
+
let output = "test_auth.py::test_login PASSED\ntest_db.py::test_query FAILED";
|
|
640
|
+
let result = parse_pytest_output(output);
|
|
641
|
+
assert!(result.contains("1 failed"));
|
|
642
|
+
assert!(result.contains("test_db.py::test_query"));
|
|
643
|
+
}
|
|
644
|
+
```
|
|
645
|
+
|
|
646
|
+
#### Go Module Tests
|
|
647
|
+
|
|
648
|
+
```rust
|
|
649
|
+
// go_cmd.rs tests
|
|
650
|
+
#[test]
|
|
651
|
+
fn test_go_test_ndjson_interleaved() {
|
|
652
|
+
let output = r#"{"Action":"run","Package":"pkg1"}
|
|
653
|
+
{"Action":"fail","Package":"pkg1","Test":"TestA"}
|
|
654
|
+
{"Action":"run","Package":"pkg2"}
|
|
655
|
+
{"Action":"pass","Package":"pkg2","Test":"TestB"}"#;
|
|
656
|
+
|
|
657
|
+
let result = parse_go_test_ndjson(output);
|
|
658
|
+
assert!(result.contains("pkg1: 1 failed"));
|
|
659
|
+
assert!(!result.contains("pkg2")); // pkg2 passed, hidden
|
|
660
|
+
}
|
|
661
|
+
```
|
|
662
|
+
|
|
663
|
+
### Performance Characteristics
|
|
664
|
+
|
|
665
|
+
```
|
|
666
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
667
|
+
│ Python/Go Module Overhead Benchmarks │
|
|
668
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
669
|
+
|
|
670
|
+
Command Raw Time rtk Time Overhead Savings
|
|
671
|
+
─────────────────────────────────────────────────────────────────────────
|
|
672
|
+
|
|
673
|
+
ruff check 850ms 862ms +12ms 83%
|
|
674
|
+
pytest 1.2s 1.21s +10ms 92%
|
|
675
|
+
pip list 450ms 458ms +8ms 78%
|
|
676
|
+
|
|
677
|
+
go test 2.1s 2.12s +20ms 88%
|
|
678
|
+
go build (errors) 950ms 961ms +11ms 80%
|
|
679
|
+
golangci-lint 4.5s 4.52s +20ms 85%
|
|
680
|
+
|
|
681
|
+
Overhead Sources:
|
|
682
|
+
• JSON parsing: 5-10ms (serde_json)
|
|
683
|
+
• State machine: 3-8ms (regex + state tracking)
|
|
684
|
+
• NDJSON streaming: 8-15ms (line-by-line JSON parse)
|
|
685
|
+
```
|
|
686
|
+
|
|
687
|
+
### Module Integration Checklist
|
|
688
|
+
|
|
689
|
+
When adding Python/Go module support:
|
|
690
|
+
|
|
691
|
+
- [x] **Output Format**: JSON API > NDJSON > State Machine > Text Filters
|
|
692
|
+
- [x] **Failure Focus**: Hide passing tests, show failures only
|
|
693
|
+
- [x] **Exit Code Preservation**: Propagate tool exit codes for CI/CD
|
|
694
|
+
- [x] **Virtual Env Awareness**: Python modules respect active virtualenv
|
|
695
|
+
- [x] **Error Grouping**: Group by rule/file for linters (ruff, golangci-lint)
|
|
696
|
+
- [x] **Streaming Support**: Handle interleaved NDJSON events (go test)
|
|
697
|
+
- [x] **Verbosity Levels**: Support -v/-vv/-vvv for debug output
|
|
698
|
+
- [x] **Token Tracking**: Integrate with tracking::track()
|
|
699
|
+
- [x] **Unit Tests**: Test parsing logic with representative outputs
|
|
700
|
+
|
|
701
|
+
---
|
|
702
|
+
|
|
703
|
+
## Shared Infrastructure
|
|
704
|
+
|
|
705
|
+
### Utilities Layer (utils.rs)
|
|
706
|
+
|
|
707
|
+
```
|
|
708
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
709
|
+
│ Shared Utilities Layer │
|
|
710
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
711
|
+
|
|
712
|
+
utils.rs provides common functionality:
|
|
713
|
+
|
|
714
|
+
┌─────────────────────────────────────────┐
|
|
715
|
+
│ truncate(s: &str, max: usize) → String │ Text truncation with "..."
|
|
716
|
+
├─────────────────────────────────────────┤
|
|
717
|
+
│ strip_ansi(text: &str) → String │ Remove ANSI color codes
|
|
718
|
+
├─────────────────────────────────────────┤
|
|
719
|
+
│ execute_command(cmd, args) │ Shell execution helper
|
|
720
|
+
│ → (stdout, stderr, exit_code) │ with error context
|
|
721
|
+
└─────────────────────────────────────────┘
|
|
722
|
+
|
|
723
|
+
Used by: All command modules (24 modules depend on utils.rs)
|
|
724
|
+
```
|
|
725
|
+
|
|
726
|
+
### Package Manager Detection Pattern
|
|
727
|
+
|
|
728
|
+
**Critical Infrastructure for JS/TS Stack (8 modules)**
|
|
729
|
+
|
|
730
|
+
```
|
|
731
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
732
|
+
│ Package Manager Detection Flow │
|
|
733
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
734
|
+
|
|
735
|
+
Detection Order:
|
|
736
|
+
┌─────────────────────────────────────┐
|
|
737
|
+
│ 1. Check: pnpm-lock.yaml exists? │
|
|
738
|
+
│ → Yes: pnpm exec -- <tool> │
|
|
739
|
+
│ │
|
|
740
|
+
│ 2. Check: yarn.lock exists? │
|
|
741
|
+
│ → Yes: yarn exec -- <tool> │
|
|
742
|
+
│ │
|
|
743
|
+
│ 3. Fallback: Use npx │
|
|
744
|
+
│ → npx --no-install -- <tool> │
|
|
745
|
+
└─────────────────────────────────────┘
|
|
746
|
+
|
|
747
|
+
Example (lint_cmd.rs:50-77):
|
|
748
|
+
|
|
749
|
+
let is_pnpm = Path::new("pnpm-lock.yaml").exists();
|
|
750
|
+
let is_yarn = Path::new("yarn.lock").exists();
|
|
751
|
+
|
|
752
|
+
let mut cmd = if is_pnpm {
|
|
753
|
+
Command::new("pnpm").arg("exec").arg("--").arg("eslint")
|
|
754
|
+
} else if is_yarn {
|
|
755
|
+
Command::new("yarn").arg("exec").arg("--").arg("eslint")
|
|
756
|
+
} else {
|
|
757
|
+
Command::new("npx").arg("--no-install").arg("--").arg("eslint")
|
|
758
|
+
};
|
|
759
|
+
|
|
760
|
+
Affects: lint, tsc, next, prettier, playwright, prisma, vitest, pnpm
|
|
761
|
+
```
|
|
762
|
+
|
|
763
|
+
**Why This Matters**:
|
|
764
|
+
- **CWD Preservation**: pnpm/yarn exec preserve working directory correctly
|
|
765
|
+
- **Monorepo Support**: Works in nested package.json structures
|
|
766
|
+
- **No Global Installs**: Uses project-local dependencies only
|
|
767
|
+
- **CI/CD Reliability**: Consistent behavior across environments
|
|
768
|
+
|
|
769
|
+
---
|
|
770
|
+
|
|
771
|
+
## Token Tracking System
|
|
772
|
+
|
|
773
|
+
### SQLite-Based Metrics
|
|
774
|
+
|
|
775
|
+
```
|
|
776
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
777
|
+
│ Token Tracking Architecture │
|
|
778
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
779
|
+
|
|
780
|
+
Flow:
|
|
781
|
+
|
|
782
|
+
1. ESTIMATION (tracking.rs:235-238)
|
|
783
|
+
────────────
|
|
784
|
+
estimate_tokens(text: &str) → usize {
|
|
785
|
+
(text.len() as f64 / 4.0).ceil() as usize
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
Heuristic: ~4 characters per token (GPT-style tokenization)
|
|
789
|
+
|
|
790
|
+
↓
|
|
791
|
+
|
|
792
|
+
2. CALCULATION
|
|
793
|
+
───────────
|
|
794
|
+
input_tokens = estimate_tokens(raw_output)
|
|
795
|
+
output_tokens = estimate_tokens(filtered_output)
|
|
796
|
+
saved_tokens = input_tokens - output_tokens
|
|
797
|
+
savings_pct = (saved / input) × 100.0
|
|
798
|
+
|
|
799
|
+
↓
|
|
800
|
+
|
|
801
|
+
3. RECORD (tracking.rs:48-59)
|
|
802
|
+
──────
|
|
803
|
+
INSERT INTO commands (
|
|
804
|
+
timestamp, -- RFC3339 format
|
|
805
|
+
original_cmd, -- "git log --oneline -5"
|
|
806
|
+
rtk_cmd, -- "rtk git log --oneline -5"
|
|
807
|
+
input_tokens, -- 125
|
|
808
|
+
output_tokens, -- 5
|
|
809
|
+
saved_tokens, -- 120
|
|
810
|
+
savings_pct, -- 96.0
|
|
811
|
+
exec_time_ms -- 15 (execution duration in milliseconds)
|
|
812
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
813
|
+
|
|
814
|
+
↓
|
|
815
|
+
|
|
816
|
+
4. STORAGE
|
|
817
|
+
───────
|
|
818
|
+
Database: ~/.local/share/rtk/history.db
|
|
819
|
+
|
|
820
|
+
Schema:
|
|
821
|
+
┌─────────────────────────────────────────┐
|
|
822
|
+
│ commands │
|
|
823
|
+
├─────────────────────────────────────────┤
|
|
824
|
+
│ id INTEGER PRIMARY KEY │
|
|
825
|
+
│ timestamp TEXT NOT NULL │
|
|
826
|
+
│ original_cmd TEXT NOT NULL │
|
|
827
|
+
│ rtk_cmd TEXT NOT NULL │
|
|
828
|
+
│ input_tokens INTEGER NOT NULL │
|
|
829
|
+
│ output_tokens INTEGER NOT NULL │
|
|
830
|
+
│ saved_tokens INTEGER NOT NULL │
|
|
831
|
+
│ savings_pct REAL NOT NULL │
|
|
832
|
+
│ exec_time_ms INTEGER DEFAULT 0 │
|
|
833
|
+
└─────────────────────────────────────────┘
|
|
834
|
+
|
|
835
|
+
Note: exec_time_ms tracks command execution duration
|
|
836
|
+
(added in v0.7.1, historical records default to 0)
|
|
837
|
+
|
|
838
|
+
↓
|
|
839
|
+
|
|
840
|
+
5. CLEANUP (tracking.rs:96-104)
|
|
841
|
+
───────
|
|
842
|
+
Auto-cleanup on each INSERT:
|
|
843
|
+
DELETE FROM commands
|
|
844
|
+
WHERE timestamp < datetime('now', '-90 days')
|
|
845
|
+
|
|
846
|
+
Retention: 90 days (HISTORY_DAYS constant)
|
|
847
|
+
|
|
848
|
+
↓
|
|
849
|
+
|
|
850
|
+
6. REPORTING (gain.rs)
|
|
851
|
+
────────
|
|
852
|
+
$ rtk gain
|
|
853
|
+
|
|
854
|
+
Query:
|
|
855
|
+
SELECT
|
|
856
|
+
COUNT(*) as total_commands,
|
|
857
|
+
SUM(saved_tokens) as total_saved,
|
|
858
|
+
AVG(savings_pct) as avg_savings,
|
|
859
|
+
SUM(exec_time_ms) as total_time_ms,
|
|
860
|
+
AVG(exec_time_ms) as avg_time_ms
|
|
861
|
+
FROM commands
|
|
862
|
+
WHERE timestamp > datetime('now', '-90 days')
|
|
863
|
+
|
|
864
|
+
Output:
|
|
865
|
+
┌──────────────────────────────────────┐
|
|
866
|
+
│ Token Savings Report (90 days) │
|
|
867
|
+
├──────────────────────────────────────┤
|
|
868
|
+
│ Commands executed: 1,234 │
|
|
869
|
+
│ Average savings: 78.5% │
|
|
870
|
+
│ Total tokens saved: 45,678 │
|
|
871
|
+
│ Total exec time: 8m50s (573ms) │
|
|
872
|
+
│ │
|
|
873
|
+
│ Top commands: │
|
|
874
|
+
│ • rtk git status (234 uses) │
|
|
875
|
+
│ • rtk lint (156 uses) │
|
|
876
|
+
│ • rtk test (89 uses) │
|
|
877
|
+
└──────────────────────────────────────┘
|
|
878
|
+
|
|
879
|
+
Note: Time column shows average execution
|
|
880
|
+
duration per command (added in v0.7.1)
|
|
881
|
+
```
|
|
882
|
+
|
|
883
|
+
### Thread Safety
|
|
884
|
+
|
|
885
|
+
```rust
|
|
886
|
+
// tracking.rs:9-11
|
|
887
|
+
lazy_static::lazy_static! {
|
|
888
|
+
static ref TRACKER: Mutex<Option<Tracker>> = Mutex::new(None);
|
|
889
|
+
}
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
**Design**: Single-threaded execution with Mutex for future-proofing.
|
|
893
|
+
**Current State**: No multi-threading, but Mutex enables safe concurrent access if needed.
|
|
894
|
+
|
|
895
|
+
---
|
|
896
|
+
|
|
897
|
+
## Global Flags Architecture
|
|
898
|
+
|
|
899
|
+
### Verbosity System
|
|
900
|
+
|
|
901
|
+
```
|
|
902
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
903
|
+
│ Verbosity Levels │
|
|
904
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
905
|
+
|
|
906
|
+
main.rs:47-49
|
|
907
|
+
#[arg(short, long, action = clap::ArgAction::Count, global = true)]
|
|
908
|
+
verbose: u8,
|
|
909
|
+
|
|
910
|
+
Levels:
|
|
911
|
+
┌─────────┬──────────────────────────────────────────────────────┐
|
|
912
|
+
│ Flag │ Behavior │
|
|
913
|
+
├─────────┼──────────────────────────────────────────────────────┤
|
|
914
|
+
│ (none) │ Compact output only │
|
|
915
|
+
│ -v │ + Debug messages (eprintln! statements) │
|
|
916
|
+
│ -vv │ + Command being executed │
|
|
917
|
+
│ -vvv │ + Raw output before filtering │
|
|
918
|
+
└─────────┴──────────────────────────────────────────────────────┘
|
|
919
|
+
|
|
920
|
+
Example (git.rs:67-69):
|
|
921
|
+
if verbose > 0 {
|
|
922
|
+
eprintln!("Git diff summary:");
|
|
923
|
+
}
|
|
924
|
+
```
|
|
925
|
+
|
|
926
|
+
### Ultra-Compact Mode
|
|
927
|
+
|
|
928
|
+
```
|
|
929
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
930
|
+
│ Ultra-Compact Mode (-u) │
|
|
931
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
932
|
+
|
|
933
|
+
main.rs:51-53
|
|
934
|
+
#[arg(short = 'u', long, global = true)]
|
|
935
|
+
ultra_compact: bool,
|
|
936
|
+
|
|
937
|
+
Features:
|
|
938
|
+
┌──────────────────────────────────────────────────────────────────────┐
|
|
939
|
+
│ • ASCII icons instead of words (✓ ✗ → ⚠) │
|
|
940
|
+
│ • Inline formatting (single-line summaries) │
|
|
941
|
+
│ • Maximum compression for LLM contexts │
|
|
942
|
+
└──────────────────────────────────────────────────────────────────────┘
|
|
943
|
+
|
|
944
|
+
Example (gh_cmd.rs:521):
|
|
945
|
+
if ultra_compact {
|
|
946
|
+
println!("✓ PR #{} merged", number);
|
|
947
|
+
} else {
|
|
948
|
+
println!("Pull request #{} successfully merged", number);
|
|
949
|
+
}
|
|
950
|
+
```
|
|
951
|
+
|
|
952
|
+
---
|
|
953
|
+
|
|
954
|
+
## Error Handling
|
|
955
|
+
|
|
956
|
+
### anyhow::Result<()> Propagation Chain
|
|
957
|
+
|
|
958
|
+
```
|
|
959
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
960
|
+
│ Error Handling Architecture │
|
|
961
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
962
|
+
|
|
963
|
+
Propagation Chain:
|
|
964
|
+
|
|
965
|
+
main() → Result<()>
|
|
966
|
+
↓
|
|
967
|
+
match cli.command {
|
|
968
|
+
Commands::Git { args, .. } => git::run(&args, verbose)?,
|
|
969
|
+
...
|
|
970
|
+
}
|
|
971
|
+
↓ .context("Git command failed")
|
|
972
|
+
git::run(args: &[String], verbose: u8) → Result<()>
|
|
973
|
+
↓ .context("Failed to execute git")
|
|
974
|
+
git::execute_git_command() → Result<String>
|
|
975
|
+
↓ .context("Git process error")
|
|
976
|
+
Command::new("git").output()?
|
|
977
|
+
↓ Error occurs
|
|
978
|
+
anyhow::Error
|
|
979
|
+
↓ Bubble up through ?
|
|
980
|
+
main.rs error display
|
|
981
|
+
↓
|
|
982
|
+
eprintln!("Error: {:#}", err)
|
|
983
|
+
↓
|
|
984
|
+
std::process::exit(1)
|
|
985
|
+
```
|
|
986
|
+
|
|
987
|
+
### Exit Code Preservation (Critical for CI/CD)
|
|
988
|
+
|
|
989
|
+
```
|
|
990
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
991
|
+
│ Exit Code Handling Strategy │
|
|
992
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
993
|
+
|
|
994
|
+
Standard Pattern (git.rs:45-48, PR #5):
|
|
995
|
+
|
|
996
|
+
let output = Command::new("git").args(args).output()?;
|
|
997
|
+
|
|
998
|
+
if !output.status.success() {
|
|
999
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
1000
|
+
eprintln!("{}", stderr);
|
|
1001
|
+
std::process::exit(output.status.code().unwrap_or(1));
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
Exit Codes:
|
|
1005
|
+
┌─────────┬──────────────────────────────────────────────────────┐
|
|
1006
|
+
│ Code │ Meaning │
|
|
1007
|
+
├─────────┼──────────────────────────────────────────────────────┤
|
|
1008
|
+
│ 0 │ Success │
|
|
1009
|
+
│ 1 │ rtk internal error (parsing, filtering, etc.) │
|
|
1010
|
+
│ N │ Preserved exit code from underlying tool │
|
|
1011
|
+
│ │ (e.g., git returns 128, lint returns 1) │
|
|
1012
|
+
└─────────┴──────────────────────────────────────────────────────┘
|
|
1013
|
+
|
|
1014
|
+
Why This Matters:
|
|
1015
|
+
• CI/CD pipelines rely on exit codes to determine build success/failure
|
|
1016
|
+
• Pre-commit hooks need accurate failure signals
|
|
1017
|
+
• Git workflows require proper exit code propagation (PR #5 fix)
|
|
1018
|
+
|
|
1019
|
+
Modules with Exit Code Preservation:
|
|
1020
|
+
• git.rs (all git commands)
|
|
1021
|
+
• lint_cmd.rs (linter failures)
|
|
1022
|
+
• tsc_cmd.rs (TypeScript errors)
|
|
1023
|
+
• vitest_cmd.rs (test failures)
|
|
1024
|
+
• playwright_cmd.rs (E2E test failures)
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
---
|
|
1028
|
+
|
|
1029
|
+
## Configuration System
|
|
1030
|
+
|
|
1031
|
+
### Two-Tier Configuration
|
|
1032
|
+
|
|
1033
|
+
```
|
|
1034
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
1035
|
+
│ Configuration Architecture │
|
|
1036
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
1037
|
+
|
|
1038
|
+
1. User Settings (config.toml)
|
|
1039
|
+
───────────────────────────
|
|
1040
|
+
Location: ~/.config/rtk/config.toml
|
|
1041
|
+
|
|
1042
|
+
Format:
|
|
1043
|
+
[general]
|
|
1044
|
+
default_filter_level = "minimal"
|
|
1045
|
+
enable_tracking = true
|
|
1046
|
+
retention_days = 90
|
|
1047
|
+
|
|
1048
|
+
Loaded by: config.rs (main.rs:650-656)
|
|
1049
|
+
|
|
1050
|
+
2. LLM Integration (CLAUDE.md)
|
|
1051
|
+
────────────────────────────
|
|
1052
|
+
Locations:
|
|
1053
|
+
• Global: ~/.config/rtk/CLAUDE.md
|
|
1054
|
+
• Local: ./CLAUDE.md (project-specific)
|
|
1055
|
+
|
|
1056
|
+
Purpose: Instruct LLM (Claude Code) to use rtk prefix
|
|
1057
|
+
Created by: rtk init [--global]
|
|
1058
|
+
|
|
1059
|
+
Template (init.rs:40-60):
|
|
1060
|
+
# CLAUDE.md
|
|
1061
|
+
Use `rtk` prefix for all commands:
|
|
1062
|
+
- rtk git status
|
|
1063
|
+
- rtk grep "pattern"
|
|
1064
|
+
- rtk read file.rs
|
|
1065
|
+
|
|
1066
|
+
Benefits: 60-90% token reduction
|
|
1067
|
+
```
|
|
1068
|
+
|
|
1069
|
+
### Initialization Flow
|
|
1070
|
+
|
|
1071
|
+
```
|
|
1072
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
1073
|
+
│ rtk init Workflow │
|
|
1074
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
1075
|
+
|
|
1076
|
+
$ rtk init [--global]
|
|
1077
|
+
↓
|
|
1078
|
+
Check existing CLAUDE.md:
|
|
1079
|
+
• --global? → ~/.config/rtk/CLAUDE.md
|
|
1080
|
+
• else → ./CLAUDE.md
|
|
1081
|
+
↓
|
|
1082
|
+
├─ Exists? → Warn user, ask to overwrite
|
|
1083
|
+
└─ Not exists? → Continue
|
|
1084
|
+
↓
|
|
1085
|
+
Prompt: "Initialize rtk for LLM usage? [y/N]"
|
|
1086
|
+
↓ Yes
|
|
1087
|
+
Write template:
|
|
1088
|
+
┌─────────────────────────────────────┐
|
|
1089
|
+
│ # CLAUDE.md │
|
|
1090
|
+
│ │
|
|
1091
|
+
│ Use `rtk` prefix for commands: │
|
|
1092
|
+
│ - rtk git status │
|
|
1093
|
+
│ - rtk lint │
|
|
1094
|
+
│ - rtk test │
|
|
1095
|
+
│ │
|
|
1096
|
+
│ Benefits: 60-90% token reduction │
|
|
1097
|
+
└─────────────────────────────────────┘
|
|
1098
|
+
↓
|
|
1099
|
+
Success: "✓ Initialized rtk for LLM integration"
|
|
1100
|
+
```
|
|
1101
|
+
|
|
1102
|
+
---
|
|
1103
|
+
|
|
1104
|
+
## Module Development Pattern
|
|
1105
|
+
|
|
1106
|
+
### Standard Module Template
|
|
1107
|
+
|
|
1108
|
+
```rust
|
|
1109
|
+
// src/example_cmd.rs
|
|
1110
|
+
|
|
1111
|
+
use anyhow::{Context, Result};
|
|
1112
|
+
use std::process::Command;
|
|
1113
|
+
use crate::{tracking, utils};
|
|
1114
|
+
|
|
1115
|
+
/// Public entry point called by main.rs router
|
|
1116
|
+
pub fn run(args: &[String], verbose: u8) -> Result<()> {
|
|
1117
|
+
// 1. Execute underlying command
|
|
1118
|
+
let raw_output = execute_command(args)?;
|
|
1119
|
+
|
|
1120
|
+
// 2. Apply filtering strategy
|
|
1121
|
+
let filtered = filter_output(&raw_output, verbose);
|
|
1122
|
+
|
|
1123
|
+
// 3. Print result
|
|
1124
|
+
println!("{}", filtered);
|
|
1125
|
+
|
|
1126
|
+
// 4. Track token savings
|
|
1127
|
+
tracking::track(
|
|
1128
|
+
"original_command",
|
|
1129
|
+
"rtk command",
|
|
1130
|
+
&raw_output,
|
|
1131
|
+
&filtered
|
|
1132
|
+
);
|
|
1133
|
+
|
|
1134
|
+
Ok(())
|
|
1135
|
+
}
|
|
1136
|
+
|
|
1137
|
+
/// Execute the underlying tool
|
|
1138
|
+
fn execute_command(args: &[String]) -> Result<String> {
|
|
1139
|
+
let output = Command::new("tool")
|
|
1140
|
+
.args(args)
|
|
1141
|
+
.output()
|
|
1142
|
+
.context("Failed to execute tool")?;
|
|
1143
|
+
|
|
1144
|
+
// Preserve exit codes (critical for CI/CD)
|
|
1145
|
+
if !output.status.success() {
|
|
1146
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
1147
|
+
eprintln!("{}", stderr);
|
|
1148
|
+
std::process::exit(output.status.code().unwrap_or(1));
|
|
1149
|
+
}
|
|
1150
|
+
|
|
1151
|
+
Ok(String::from_utf8_lossy(&output.stdout).to_string())
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/// Apply filtering strategy
|
|
1155
|
+
fn filter_output(raw: &str, verbose: u8) -> String {
|
|
1156
|
+
// Choose strategy: stats, grouping, deduplication, etc.
|
|
1157
|
+
// See "Filtering Strategies" section for options
|
|
1158
|
+
|
|
1159
|
+
if verbose >= 3 {
|
|
1160
|
+
eprintln!("Raw output:\n{}", raw);
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// Apply compression logic
|
|
1164
|
+
let compressed = compress(raw);
|
|
1165
|
+
|
|
1166
|
+
compressed
|
|
1167
|
+
}
|
|
1168
|
+
|
|
1169
|
+
#[cfg(test)]
|
|
1170
|
+
mod tests {
|
|
1171
|
+
use super::*;
|
|
1172
|
+
|
|
1173
|
+
#[test]
|
|
1174
|
+
fn test_filter_output() {
|
|
1175
|
+
let raw = "verbose output here";
|
|
1176
|
+
let filtered = filter_output(raw, 0);
|
|
1177
|
+
assert!(filtered.len() < raw.len());
|
|
1178
|
+
}
|
|
1179
|
+
}
|
|
1180
|
+
```
|
|
1181
|
+
|
|
1182
|
+
### Common Patterns
|
|
1183
|
+
|
|
1184
|
+
#### 1. Package Manager Detection (JS/TS modules)
|
|
1185
|
+
|
|
1186
|
+
```rust
|
|
1187
|
+
// Detect lockfiles
|
|
1188
|
+
let is_pnpm = Path::new("pnpm-lock.yaml").exists();
|
|
1189
|
+
let is_yarn = Path::new("yarn.lock").exists();
|
|
1190
|
+
|
|
1191
|
+
// Build command
|
|
1192
|
+
let mut cmd = if is_pnpm {
|
|
1193
|
+
Command::new("pnpm").arg("exec").arg("--").arg("eslint")
|
|
1194
|
+
} else if is_yarn {
|
|
1195
|
+
Command::new("yarn").arg("exec").arg("--").arg("eslint")
|
|
1196
|
+
} else {
|
|
1197
|
+
Command::new("npx").arg("--no-install").arg("--").arg("eslint")
|
|
1198
|
+
};
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
#### 2. Lazy Static Regex (filter.rs, runner.rs)
|
|
1202
|
+
|
|
1203
|
+
```rust
|
|
1204
|
+
lazy_static::lazy_static! {
|
|
1205
|
+
static ref PATTERN: Regex = Regex::new(r"ERROR:.*").unwrap();
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1208
|
+
// Usage: compiled once, reused across invocations
|
|
1209
|
+
let matches: Vec<_> = PATTERN.find_iter(text).collect();
|
|
1210
|
+
```
|
|
1211
|
+
|
|
1212
|
+
#### 3. Verbosity Guards
|
|
1213
|
+
|
|
1214
|
+
```rust
|
|
1215
|
+
if verbose > 0 {
|
|
1216
|
+
eprintln!("Debug: Processing {} files", count);
|
|
1217
|
+
}
|
|
1218
|
+
|
|
1219
|
+
if verbose >= 2 {
|
|
1220
|
+
eprintln!("Executing: {:?}", cmd);
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
if verbose >= 3 {
|
|
1224
|
+
eprintln!("Raw output:\n{}", raw);
|
|
1225
|
+
}
|
|
1226
|
+
```
|
|
1227
|
+
|
|
1228
|
+
---
|
|
1229
|
+
|
|
1230
|
+
## Build Optimizations
|
|
1231
|
+
|
|
1232
|
+
### Release Profile (Cargo.toml)
|
|
1233
|
+
|
|
1234
|
+
```toml
|
|
1235
|
+
[profile.release]
|
|
1236
|
+
opt-level = 3 # Maximum optimization
|
|
1237
|
+
lto = true # Link-time optimization
|
|
1238
|
+
codegen-units = 1 # Single codegen unit for better optimization
|
|
1239
|
+
strip = true # Remove debug symbols
|
|
1240
|
+
panic = "abort" # Smaller binary size
|
|
1241
|
+
```
|
|
1242
|
+
|
|
1243
|
+
### Performance Characteristics
|
|
1244
|
+
|
|
1245
|
+
```
|
|
1246
|
+
┌────────────────────────────────────────────────────────────────────────┐
|
|
1247
|
+
│ Performance Metrics │
|
|
1248
|
+
└────────────────────────────────────────────────────────────────────────┘
|
|
1249
|
+
|
|
1250
|
+
Binary:
|
|
1251
|
+
• Size: ~4.1 MB (stripped release build)
|
|
1252
|
+
• Startup: ~5-10ms (cold start)
|
|
1253
|
+
• Memory: ~2-5 MB (typical usage)
|
|
1254
|
+
|
|
1255
|
+
Runtime Overhead (estimated):
|
|
1256
|
+
┌──────────────────────┬──────────────┬──────────────┐
|
|
1257
|
+
│ Operation │ rtk Overhead │ Total Time │
|
|
1258
|
+
├──────────────────────┼──────────────┼──────────────┤
|
|
1259
|
+
│ rtk git status │ +8ms │ 58ms │
|
|
1260
|
+
│ rtk grep "pattern" │ +12ms │ 145ms │
|
|
1261
|
+
│ rtk read file.rs │ +5ms │ 15ms │
|
|
1262
|
+
│ rtk lint │ +15ms │ 2.5s │
|
|
1263
|
+
└──────────────────────┴──────────────┴──────────────┘
|
|
1264
|
+
|
|
1265
|
+
Note: Overhead measurements are estimates. Actual performance varies
|
|
1266
|
+
by system, command complexity, and output size.
|
|
1267
|
+
|
|
1268
|
+
Overhead Sources:
|
|
1269
|
+
• Clap parsing: ~2-3ms
|
|
1270
|
+
• Command execution: ~1-2ms
|
|
1271
|
+
• Filtering/compression: ~2-8ms (varies by strategy)
|
|
1272
|
+
• SQLite tracking: ~1-3ms
|
|
1273
|
+
```
|
|
1274
|
+
|
|
1275
|
+
### Compilation
|
|
1276
|
+
|
|
1277
|
+
```bash
|
|
1278
|
+
# Development build (fast compilation, debug symbols)
|
|
1279
|
+
cargo build
|
|
1280
|
+
|
|
1281
|
+
# Release build (optimized, stripped)
|
|
1282
|
+
cargo build --release
|
|
1283
|
+
|
|
1284
|
+
# Check without building (fast feedback)
|
|
1285
|
+
cargo check
|
|
1286
|
+
|
|
1287
|
+
# Run tests
|
|
1288
|
+
cargo test
|
|
1289
|
+
|
|
1290
|
+
# Lint with clippy
|
|
1291
|
+
cargo clippy --all-targets
|
|
1292
|
+
|
|
1293
|
+
# Format code
|
|
1294
|
+
cargo fmt
|
|
1295
|
+
```
|
|
1296
|
+
|
|
1297
|
+
---
|
|
1298
|
+
|
|
1299
|
+
## Extensibility Guide
|
|
1300
|
+
|
|
1301
|
+
### Adding a New Command
|
|
1302
|
+
|
|
1303
|
+
**Step-by-step process to add a new rtk command:**
|
|
1304
|
+
|
|
1305
|
+
#### 1. Create Module File
|
|
1306
|
+
|
|
1307
|
+
```bash
|
|
1308
|
+
touch src/mycmd.rs
|
|
1309
|
+
```
|
|
1310
|
+
|
|
1311
|
+
#### 2. Implement Module (src/mycmd.rs)
|
|
1312
|
+
|
|
1313
|
+
```rust
|
|
1314
|
+
use anyhow::{Context, Result};
|
|
1315
|
+
use std::process::Command;
|
|
1316
|
+
use crate::tracking;
|
|
1317
|
+
|
|
1318
|
+
pub fn run(args: &[String], verbose: u8) -> Result<()> {
|
|
1319
|
+
// Execute underlying command
|
|
1320
|
+
let output = Command::new("mycmd")
|
|
1321
|
+
.args(args)
|
|
1322
|
+
.output()
|
|
1323
|
+
.context("Failed to execute mycmd")?;
|
|
1324
|
+
|
|
1325
|
+
let raw = String::from_utf8_lossy(&output.stdout);
|
|
1326
|
+
|
|
1327
|
+
// Apply filtering strategy
|
|
1328
|
+
let filtered = filter(&raw, verbose);
|
|
1329
|
+
|
|
1330
|
+
// Print result
|
|
1331
|
+
println!("{}", filtered);
|
|
1332
|
+
|
|
1333
|
+
// Track savings
|
|
1334
|
+
tracking::track("mycmd", "rtk mycmd", &raw, &filtered);
|
|
1335
|
+
|
|
1336
|
+
Ok(())
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
fn filter(raw: &str, verbose: u8) -> String {
|
|
1340
|
+
// Implement your filtering logic
|
|
1341
|
+
raw.lines().take(10).collect::<Vec<_>>().join("\n")
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
#[cfg(test)]
|
|
1345
|
+
mod tests {
|
|
1346
|
+
use super::*;
|
|
1347
|
+
|
|
1348
|
+
#[test]
|
|
1349
|
+
fn test_filter() {
|
|
1350
|
+
let raw = "line1\nline2\n";
|
|
1351
|
+
let result = filter(raw, 0);
|
|
1352
|
+
assert!(result.contains("line1"));
|
|
1353
|
+
}
|
|
1354
|
+
}
|
|
1355
|
+
```
|
|
1356
|
+
|
|
1357
|
+
#### 3. Declare Module (main.rs)
|
|
1358
|
+
|
|
1359
|
+
```rust
|
|
1360
|
+
// Add to module declarations (alphabetically)
|
|
1361
|
+
mod mycmd;
|
|
1362
|
+
```
|
|
1363
|
+
|
|
1364
|
+
#### 4. Add Command Enum Variant (main.rs)
|
|
1365
|
+
|
|
1366
|
+
```rust
|
|
1367
|
+
#[derive(Subcommand)]
|
|
1368
|
+
enum Commands {
|
|
1369
|
+
// ... existing commands ...
|
|
1370
|
+
|
|
1371
|
+
/// Description of your command
|
|
1372
|
+
Mycmd {
|
|
1373
|
+
/// Arguments your command accepts
|
|
1374
|
+
#[arg(trailing_var_arg = true, allow_hyphen_values = true)]
|
|
1375
|
+
args: Vec<String>,
|
|
1376
|
+
},
|
|
1377
|
+
}
|
|
1378
|
+
```
|
|
1379
|
+
|
|
1380
|
+
#### 5. Add Router Match Arm (main.rs)
|
|
1381
|
+
|
|
1382
|
+
```rust
|
|
1383
|
+
match cli.command {
|
|
1384
|
+
// ... existing matches ...
|
|
1385
|
+
|
|
1386
|
+
Commands::Mycmd { args } => {
|
|
1387
|
+
mycmd::run(&args, verbose)?;
|
|
1388
|
+
}
|
|
1389
|
+
}
|
|
1390
|
+
```
|
|
1391
|
+
|
|
1392
|
+
#### 6. Test Your Command
|
|
1393
|
+
|
|
1394
|
+
```bash
|
|
1395
|
+
# Build and test
|
|
1396
|
+
cargo build
|
|
1397
|
+
./target/debug/rtk mycmd arg1 arg2
|
|
1398
|
+
|
|
1399
|
+
# Run tests
|
|
1400
|
+
cargo test mycmd::tests
|
|
1401
|
+
|
|
1402
|
+
# Check with clippy
|
|
1403
|
+
cargo clippy --all-targets
|
|
1404
|
+
```
|
|
1405
|
+
|
|
1406
|
+
#### 7. Document Your Command
|
|
1407
|
+
|
|
1408
|
+
Update CLAUDE.md:
|
|
1409
|
+
|
|
1410
|
+
```markdown
|
|
1411
|
+
### New Commands
|
|
1412
|
+
|
|
1413
|
+
**rtk mycmd** - Description of what it does
|
|
1414
|
+
- Strategy: [stats/grouping/filtering/etc.]
|
|
1415
|
+
- Savings: X-Y%
|
|
1416
|
+
- Used by: [workflow description]
|
|
1417
|
+
```
|
|
1418
|
+
|
|
1419
|
+
### Design Checklist
|
|
1420
|
+
|
|
1421
|
+
When implementing a new command, consider:
|
|
1422
|
+
|
|
1423
|
+
- [ ] **Filtering Strategy**: Which of the 9 strategies fits best?
|
|
1424
|
+
- [ ] **Exit Code Preservation**: Does your command need to preserve exit codes for CI/CD?
|
|
1425
|
+
- [ ] **Verbosity Support**: Add debug output for `-v`, `-vv`, `-vvv`
|
|
1426
|
+
- [ ] **Error Handling**: Use `.context()` for meaningful error messages
|
|
1427
|
+
- [ ] **Package Manager Detection**: For JS/TS tools, use the standard detection pattern
|
|
1428
|
+
- [ ] **Tests**: Add unit tests for filtering logic
|
|
1429
|
+
- [ ] **Token Tracking**: Integrate with `tracking::track()`
|
|
1430
|
+
- [ ] **Documentation**: Update CLAUDE.md with token savings and use cases
|
|
1431
|
+
|
|
1432
|
+
---
|
|
1433
|
+
|
|
1434
|
+
## Architecture Decision Records
|
|
1435
|
+
|
|
1436
|
+
### Why Rust?
|
|
1437
|
+
|
|
1438
|
+
- **Performance**: ~5-15ms overhead per command (negligible for user experience)
|
|
1439
|
+
- **Safety**: No runtime errors from null pointers, data races, etc.
|
|
1440
|
+
- **Single Binary**: No runtime dependencies (distribute one executable)
|
|
1441
|
+
- **Cross-Platform**: Works on macOS, Linux, Windows without modification
|
|
1442
|
+
|
|
1443
|
+
### Why SQLite for Tracking?
|
|
1444
|
+
|
|
1445
|
+
- **Zero Config**: No server setup, works out-of-the-box
|
|
1446
|
+
- **Lightweight**: ~100KB database for 90 days of history
|
|
1447
|
+
- **Reliable**: ACID compliance for data integrity
|
|
1448
|
+
- **Queryable**: Rich analytics via SQL (gain report)
|
|
1449
|
+
|
|
1450
|
+
### Why anyhow for Error Handling?
|
|
1451
|
+
|
|
1452
|
+
- **Context**: `.context()` adds meaningful error messages throughout call chain
|
|
1453
|
+
- **Ergonomic**: `?` operator for concise error propagation
|
|
1454
|
+
- **User-Friendly**: Error display shows full context chain
|
|
1455
|
+
|
|
1456
|
+
### Why Clap for CLI Parsing?
|
|
1457
|
+
|
|
1458
|
+
- **Derive Macros**: Less boilerplate (declarative CLI definition)
|
|
1459
|
+
- **Auto-Generated Help**: `--help` generated automatically
|
|
1460
|
+
- **Type Safety**: Parse arguments directly into typed structs
|
|
1461
|
+
- **Global Flags**: `-v` and `-u` work across all commands
|
|
1462
|
+
|
|
1463
|
+
---
|
|
1464
|
+
|
|
1465
|
+
## Resources
|
|
1466
|
+
|
|
1467
|
+
- **README.md**: User guide, installation, examples
|
|
1468
|
+
- **CLAUDE.md**: Developer documentation, module details, PR history
|
|
1469
|
+
- **Cargo.toml**: Dependencies, build profiles, package metadata
|
|
1470
|
+
- **src/**: Source code organized by module
|
|
1471
|
+
- **.github/workflows/**: CI/CD automation (multi-platform builds, releases)
|
|
1472
|
+
|
|
1473
|
+
---
|
|
1474
|
+
|
|
1475
|
+
## Glossary
|
|
1476
|
+
|
|
1477
|
+
| Term | Definition |
|
|
1478
|
+
|------|------------|
|
|
1479
|
+
| **Token** | Unit of text processed by LLMs (~4 characters on average) |
|
|
1480
|
+
| **Filtering** | Reducing output size while preserving essential information |
|
|
1481
|
+
| **Proxy Pattern** | rtk sits between user and tool, transforming output |
|
|
1482
|
+
| **Exit Code Preservation** | Passing through tool's exit code for CI/CD reliability |
|
|
1483
|
+
| **Package Manager Detection** | Identifying pnpm/yarn/npm to execute JS/TS tools correctly |
|
|
1484
|
+
| **Verbosity Levels** | `-v/-vv/-vvv` for progressively more debug output |
|
|
1485
|
+
| **Ultra-Compact** | `-u` flag for maximum compression (ASCII icons, inline format) |
|
|
1486
|
+
|
|
1487
|
+
---
|
|
1488
|
+
|
|
1489
|
+
**Last Updated**: 2026-02-22
|
|
1490
|
+
**Architecture Version**: 2.2
|
|
1491
|
+
**rtk Version**: 0.28.0
|