chore: import upstream snapshot with attribution

This commit is contained in:
wehub-resource-sync
2026-07-13 12:01:57 +08:00
commit 9dda3e2451
399 changed files with 118131 additions and 0 deletions
+446
View File
@@ -0,0 +1,446 @@
# RTK Token Savings Audit Guide
Complete guide to analyzing your rtk token savings with temporal breakdowns and data exports.
## Overview
The `rtk gain` command provides comprehensive analytics for tracking your token savings across time periods.
**Database Location**: `~/.local/share/rtk/history.db`
**Retention Policy**: 90 days
**Scope**: Global across all projects, worktrees, and Claude sessions
## Quick Reference
```bash
# Default summary view
rtk gain
# Temporal breakdowns
rtk gain --daily # All days since tracking started
rtk gain --weekly # Aggregated by week
rtk gain --monthly # Aggregated by month
rtk gain --all # Show all breakdowns at once
# Export formats
rtk gain --all --format json > savings.json
rtk gain --all --format csv > savings.csv
# Combined flags
rtk gain --graph --history --quota # Classic view with extras
rtk gain --daily --weekly --monthly # Multiple breakdowns
# Reset all tracking data
rtk gain --reset # prompts [y/N] before deleting
rtk gain --reset --yes # skip prompt (CI/scripts)
```
## Command Options
### Temporal Flags
| Flag | Description | Output |
|------|-------------|--------|
| `--daily` | Day-by-day breakdown | All days with full metrics |
| `--weekly` | Week-by-week breakdown | Aggregated by Sunday-Saturday weeks |
| `--monthly` | Month-by-month breakdown | Aggregated by calendar month |
| `--all` | All time breakdowns | Daily + Weekly + Monthly combined |
### Classic Flags (still available)
| Flag | Description |
|------|-------------|
| `--graph` | ASCII graph of last 30 days |
| `--history` | Recent 10 commands |
| `--quota` | Monthly quota analysis (Pro/5x/20x tiers) |
| `--tier <TIER>` | Quota tier: pro, 5x, 20x (default: 20x) |
### Reset Flag
| Flag | Description |
|------|-------------|
| `--reset` | Permanently delete all tracking data (commands + parse failures) |
| `--yes` | Skip the confirmation prompt (for CI/scripts) |
> **Warning**: `--reset` is irreversible. It clears both the `commands` and `parse_failures` tables atomically. A `[y/N]` confirmation prompt is shown by default. In non-interactive environments (piped stdin), it defaults to `N` unless `--yes` is passed.
### Export Formats
| Format | Flag | Use Case |
|--------|------|----------|
| `text` | `--format text` (default) | Terminal display |
| `json` | `--format json` | Programmatic analysis, APIs |
| `csv` | `--format csv` | Excel, data analysis, plotting |
## Output Examples
### Daily Breakdown
```
📅 Daily Breakdown (3 days)
════════════════════════════════════════════════════════════════
Date Cmds Input Output Saved Save%
────────────────────────────────────────────────────────────────
2026-01-28 89 380.9K 26.7K 355.8K 93.4%
2026-01-29 102 894.5K 32.4K 863.7K 96.6%
2026-01-30 5 749 55 694 92.7%
────────────────────────────────────────────────────────────────
TOTAL 196 1.3M 59.2K 1.2M 95.6%
```
**Metrics explained:**
- **Cmds**: Number of rtk commands executed
- **Input**: Estimated tokens from raw command output
- **Output**: Actual tokens after rtk filtering
- **Saved**: Input - Output (tokens prevented from reaching LLM)
- **Save%**: Percentage reduction (Saved / Input × 100)
### Weekly Breakdown
```
📊 Weekly Breakdown (1 weeks)
════════════════════════════════════════════════════════════════════════
Week Cmds Input Output Saved Save%
────────────────────────────────────────────────────────────────────────
01-26 → 02-01 196 1.3M 59.2K 1.2M 95.6%
────────────────────────────────────────────────────────────────────────
TOTAL 196 1.3M 59.2K 1.2M 95.6%
```
**Week definition**: Sunday to Saturday (ISO week starting Sunday at 00:00)
### Monthly Breakdown
```
📆 Monthly Breakdown (1 months)
════════════════════════════════════════════════════════════════
Month Cmds Input Output Saved Save%
────────────────────────────────────────────────────────────────
2026-01 196 1.3M 59.2K 1.2M 95.6%
────────────────────────────────────────────────────────────────
TOTAL 196 1.3M 59.2K 1.2M 95.6%
```
**Month format**: YYYY-MM (calendar month)
### JSON Export
```json
{
"summary": {
"total_commands": 196,
"total_input": 1276098,
"total_output": 59244,
"total_saved": 1220217,
"avg_savings_pct": 95.62
},
"daily": [
{
"date": "2026-01-28",
"commands": 89,
"input_tokens": 380894,
"output_tokens": 26744,
"saved_tokens": 355779,
"savings_pct": 93.41
}
],
"weekly": [...],
"monthly": [...]
}
```
**Use cases:**
- API integration
- Custom dashboards
- Automated reporting
- Data pipeline ingestion
### CSV Export
```csv
# Daily Data
date,commands,input_tokens,output_tokens,saved_tokens,savings_pct
2026-01-28,89,380894,26744,355779,93.41
2026-01-29,102,894455,32445,863744,96.57
# Weekly Data
week_start,week_end,commands,input_tokens,output_tokens,saved_tokens,savings_pct
2026-01-26,2026-02-01,196,1276098,59244,1220217,95.62
# Monthly Data
month,commands,input_tokens,output_tokens,saved_tokens,savings_pct
2026-01,196,1276098,59244,1220217,95.62
```
**Use cases:**
- Excel analysis
- Python/R data science
- Google Sheets dashboards
- Matplotlib/seaborn plotting
## Analysis Workflows
### Weekly Progress Tracking
```bash
# Generate weekly report every Monday
rtk gain --weekly --format csv > reports/week-$(date +%Y-%W).csv
# Compare this week vs last week
rtk gain --weekly | tail -3
```
### Monthly Cost Analysis
```bash
# Export monthly data for budget review
rtk gain --monthly --format json | jq '.monthly[] |
{month, saved_tokens, quota_pct: (.saved_tokens / 6000000 * 100)}'
```
### Data Science Analysis
```python
import pandas as pd
import subprocess
# Get CSV data
result = subprocess.run(['rtk', 'gain', '--all', '--format', 'csv'],
capture_output=True, text=True)
# Parse daily data
lines = result.stdout.split('\n')
daily_start = lines.index('# Daily Data') + 2
daily_end = lines.index('', daily_start)
daily_df = pd.read_csv(pd.StringIO('\n'.join(lines[daily_start:daily_end])))
# Plot savings trend
daily_df['date'] = pd.to_datetime(daily_df['date'])
daily_df.plot(x='date', y='savings_pct', kind='line')
```
### Excel Analysis
1. Export CSV: `rtk gain --all --format csv > rtk-data.csv`
2. Open in Excel
3. Create pivot tables:
- Daily trends (line chart)
- Weekly totals (bar chart)
- Savings % distribution (histogram)
### Dashboard Creation
```bash
# Generate dashboard data daily via cron
0 0 * * * rtk gain --all --format json > /var/www/dashboard/rtk-stats.json
# Serve with static site
cat > index.html <<'EOF'
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<canvas id="savings"></canvas>
<script>
fetch('rtk-stats.json')
.then(r => r.json())
.then(data => {
new Chart(document.getElementById('savings'), {
type: 'line',
data: {
labels: data.daily.map(d => d.date),
datasets: [{
label: 'Daily Savings %',
data: data.daily.map(d => d.savings_pct)
}]
}
});
});
</script>
EOF
```
## Understanding Token Savings
### Token Estimation
rtk estimates tokens using `text.len() / 4` (4 characters per token average).
**Accuracy**: ±10% compared to actual LLM tokenization (sufficient for trends).
### Savings Calculation
```
Input Tokens = estimate_tokens(raw_command_output)
Output Tokens = estimate_tokens(rtk_filtered_output)
Saved Tokens = Input - Output
Savings % = (Saved / Input) × 100
```
### Typical Savings by Command
| Command | Typical Savings | Mechanism |
|---------|----------------|-----------|
| `rtk git status` | 77-93% | Compact stat format |
| `rtk eslint` | 84% | Group by rule |
| `rtk jest` | 94-99% | Show failures only |
| `rtk vitest` | 94-99% | Show failures only |
| `rtk find` | 75% | Tree format |
| `rtk pnpm list` | 70-90% | Compact dependencies |
| `rtk grep` | 70% | Truncate + group |
## Database Management
### Inspect Raw Data
```bash
# Location
ls -lh ~/.local/share/rtk/history.db
# Schema
sqlite3 ~/.local/share/rtk/history.db ".schema"
# Recent records
sqlite3 ~/.local/share/rtk/history.db \
"SELECT timestamp, rtk_cmd, saved_tokens FROM commands
ORDER BY timestamp DESC LIMIT 10"
# Total database size
sqlite3 ~/.local/share/rtk/history.db \
"SELECT COUNT(*),
SUM(saved_tokens) as total_saved,
MIN(DATE(timestamp)) as first_record,
MAX(DATE(timestamp)) as last_record
FROM commands"
```
### Backup & Restore
```bash
# Backup
cp ~/.local/share/rtk/history.db ~/backups/rtk-history-$(date +%Y%m%d).db
# Restore
cp ~/backups/rtk-history-20260128.db ~/.local/share/rtk/history.db
# Export for migration
sqlite3 ~/.local/share/rtk/history.db .dump > rtk-backup.sql
```
### Cleanup
```bash
# Manual cleanup (older than 90 days)
sqlite3 ~/.local/share/rtk/history.db \
"DELETE FROM commands WHERE timestamp < datetime('now', '-90 days')"
# Reset all data
rm ~/.local/share/rtk/history.db
# Next rtk command will recreate database
```
## Integration Examples
### GitHub Actions CI/CD
```yaml
# .github/workflows/rtk-stats.yml
name: RTK Stats Report
on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
jobs:
stats:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Install rtk
run: cargo install --path .
- name: Generate report
run: |
rtk gain --weekly --format json > stats/week-$(date +%Y-%W).json
- name: Commit stats
run: |
git add stats/
git commit -m "Weekly rtk stats"
git push
```
### Slack Bot
```python
import subprocess
import json
import requests
def send_rtk_stats():
result = subprocess.run(['rtk', 'gain', '--format', 'json'],
capture_output=True, text=True)
data = json.loads(result.stdout)
message = f"""
📊 *RTK Token Savings Report*
Total Saved: {data['summary']['total_saved']:,} tokens
Savings Rate: {data['summary']['avg_savings_pct']:.1f}%
Commands: {data['summary']['total_commands']}
"""
requests.post(SLACK_WEBHOOK_URL, json={'text': message})
```
## Troubleshooting
### No data showing
```bash
# Check if database exists
ls -lh ~/.local/share/rtk/history.db
# Check record count
sqlite3 ~/.local/share/rtk/history.db "SELECT COUNT(*) FROM commands"
# Run a tracked command to generate data
rtk git status
```
### Export fails
```bash
# Check for pipe errors
rtk gain --format json 2>&1 | tee /tmp/rtk-debug.log | jq .
# Use release build to avoid warnings
cargo build --release
./target/release/rtk gain --format json
```
### Incorrect statistics
Token estimation is a heuristic. For precise measurements:
```bash
# Install tiktoken
pip install tiktoken
# Validate estimation
rtk git status > output.txt
python -c "
import tiktoken
enc = tiktoken.get_encoding('cl100k_base')
text = open('output.txt').read()
print(f'Actual tokens: {len(enc.encode(text))}')
print(f'rtk estimate: {len(text) // 4}')
"
```
## Best Practices
1. **Regular Exports**: `rtk gain --all --format json > monthly-$(date +%Y%m).json`
2. **Trend Analysis**: Compare week-over-week savings to identify optimization opportunities
3. **Command Profiling**: Use `--history` to see which commands save the most
4. **Backup Before Cleanup**: Always backup before manual database operations
5. **CI Integration**: Track savings across team in shared dashboards
## See Also
- [README.md](../README.md) - Full rtk documentation
- [CLAUDE.md](../CLAUDE.md) - Claude Code integration guide
- [ARCHITECTURE.md](../contributing/ARCHITECTURE.md) - Technical architecture
File diff suppressed because it is too large Load Diff
+583
View File
@@ -0,0 +1,583 @@
# RTK Tracking API Documentation
Comprehensive documentation for RTK's token savings tracking system.
## Table of Contents
- [Overview](#overview)
- [Architecture](#architecture)
- [Public API](#public-api)
- [Usage Examples](#usage-examples)
- [Data Formats](#data-formats)
- [Integration Examples](#integration-examples)
- [Database Schema](#database-schema)
## Overview
RTK's tracking system records every command execution to provide analytics on token savings. The system:
- Stores command history in SQLite (~/.local/share/rtk/tracking.db)
- Tracks input/output tokens, savings percentage, and execution time
- Automatically cleans up records older than 90 days
- Provides aggregation APIs (daily/weekly/monthly)
- Exports to JSON/CSV for external integrations
## Architecture
### Data Flow
```
rtk command execution
TimedExecution::start()
[command runs]
TimedExecution::track(original_cmd, rtk_cmd, input, output)
Tracker::record(original_cmd, rtk_cmd, input_tokens, output_tokens, exec_time_ms)
SQLite database (~/.local/share/rtk/tracking.db)
Aggregation APIs (get_summary, get_all_days, etc.)
CLI output (rtk gain) or JSON/CSV export
```
### Storage Location
- **Linux**: `~/.local/share/rtk/tracking.db`
- **macOS**: `~/Library/Application Support/rtk/tracking.db`
- **Windows**: `%APPDATA%\rtk\tracking.db`
### Data Retention
Records older than **90 days** are automatically deleted on each write operation to prevent unbounded database growth.
## Public API
### Core Types
#### `Tracker`
Main tracking interface for recording and querying command history.
```rust
pub struct Tracker {
conn: Connection, // SQLite connection
}
impl Tracker {
/// Create new tracker instance (opens/creates database)
pub fn new() -> Result<Self>;
/// Record a command execution
pub fn record(
&self,
original_cmd: &str, // Standard command (e.g., "ls -la")
rtk_cmd: &str, // RTK command (e.g., "rtk ls")
input_tokens: usize, // Estimated input tokens
output_tokens: usize, // Actual output tokens
exec_time_ms: u64, // Execution time in milliseconds
) -> Result<()>;
/// Get overall summary statistics
pub fn get_summary(&self) -> Result<GainSummary>;
/// Get daily statistics (all days)
pub fn get_all_days(&self) -> Result<Vec<DayStats>>;
/// Get weekly statistics (grouped by week)
pub fn get_by_week(&self) -> Result<Vec<WeekStats>>;
/// Get monthly statistics (grouped by month)
pub fn get_by_month(&self) -> Result<Vec<MonthStats>>;
/// Get recent command history (limit = max records)
pub fn get_recent(&self, limit: usize) -> Result<Vec<CommandRecord>>;
}
```
#### `GainSummary`
Aggregated statistics across all recorded commands.
```rust
pub struct GainSummary {
pub total_commands: usize, // Total commands recorded
pub total_input: usize, // Total input tokens
pub total_output: usize, // Total output tokens
pub total_saved: usize, // Total tokens saved
pub avg_savings_pct: f64, // Average savings percentage
pub total_time_ms: u64, // Total execution time (ms)
pub avg_time_ms: u64, // Average execution time (ms)
pub by_command: Vec<(String, usize, usize, f64, u64)>, // Top 10 commands
pub by_day: Vec<(String, usize)>, // Last 30 days
}
```
#### `DayStats`
Daily statistics (Serializable for JSON export).
```rust
#[derive(Debug, Serialize)]
pub struct DayStats {
pub date: String, // ISO date (YYYY-MM-DD)
pub commands: usize, // Commands executed this day
pub input_tokens: usize, // Total input tokens
pub output_tokens: usize, // Total output tokens
pub saved_tokens: usize, // Total tokens saved
pub savings_pct: f64, // Savings percentage
pub total_time_ms: u64, // Total execution time (ms)
pub avg_time_ms: u64, // Average execution time (ms)
}
```
#### `WeekStats`
Weekly statistics (Serializable for JSON export).
```rust
#[derive(Debug, Serialize)]
pub struct WeekStats {
pub week_start: String, // ISO date (YYYY-MM-DD)
pub week_end: String, // ISO date (YYYY-MM-DD)
pub commands: usize,
pub input_tokens: usize,
pub output_tokens: usize,
pub saved_tokens: usize,
pub savings_pct: f64,
pub total_time_ms: u64,
pub avg_time_ms: u64,
}
```
#### `MonthStats`
Monthly statistics (Serializable for JSON export).
```rust
#[derive(Debug, Serialize)]
pub struct MonthStats {
pub month: String, // YYYY-MM format
pub commands: usize,
pub input_tokens: usize,
pub output_tokens: usize,
pub saved_tokens: usize,
pub savings_pct: f64,
pub total_time_ms: u64,
pub avg_time_ms: u64,
}
```
#### `CommandRecord`
Individual command record from history.
```rust
pub struct CommandRecord {
pub timestamp: DateTime<Utc>, // UTC timestamp
pub rtk_cmd: String, // RTK command used
pub saved_tokens: usize, // Tokens saved
pub savings_pct: f64, // Savings percentage
}
```
#### `TimedExecution`
Helper for timing command execution (preferred API).
```rust
pub struct TimedExecution {
start: Instant,
}
impl TimedExecution {
/// Start timing a command execution
pub fn start() -> Self;
/// Track command with elapsed time
pub fn track(&self, original_cmd: &str, rtk_cmd: &str, input: &str, output: &str);
/// Track passthrough commands (timing-only, no token counting)
pub fn track_passthrough(&self, original_cmd: &str, rtk_cmd: &str);
}
```
### Utility Functions
```rust
/// Estimate token count (~4 chars = 1 token)
pub fn estimate_tokens(text: &str) -> usize;
/// Format OsString args for display
pub fn args_display(args: &[OsString]) -> String;
/// Legacy tracking function (deprecated, use TimedExecution)
#[deprecated(note = "Use TimedExecution instead")]
pub fn track(original_cmd: &str, rtk_cmd: &str, input: &str, output: &str);
```
## Usage Examples
### Basic Tracking
```rust
use rtk::tracking::{TimedExecution, Tracker};
fn main() -> anyhow::Result<()> {
// Start timer
let timer = TimedExecution::start();
// Execute command
let input = execute_original_command()?;
let output = execute_rtk_command()?;
// Track execution
timer.track("ls -la", "rtk ls", &input, &output);
Ok(())
}
```
### Querying Statistics
```rust
use rtk::tracking::Tracker;
fn main() -> anyhow::Result<()> {
let tracker = Tracker::new()?;
// Get overall summary
let summary = tracker.get_summary()?;
println!("Total commands: {}", summary.total_commands);
println!("Total saved: {} tokens", summary.total_saved);
println!("Average savings: {:.1}%", summary.avg_savings_pct);
// Get daily breakdown
let days = tracker.get_all_days()?;
for day in days.iter().take(7) {
println!("{}: {} commands, {} tokens saved",
day.date, day.commands, day.saved_tokens);
}
// Get recent history
let recent = tracker.get_recent(10)?;
for cmd in recent {
println!("{}: {} saved {:.1}%",
cmd.timestamp, cmd.rtk_cmd, cmd.savings_pct);
}
Ok(())
}
```
### Passthrough Commands
For commands that stream output or run interactively (no output capture):
```rust
use rtk::tracking::TimedExecution;
fn main() -> anyhow::Result<()> {
let timer = TimedExecution::start();
// Execute streaming command (e.g., git tag --list)
execute_streaming_command()?;
// Track timing only (input_tokens=0, output_tokens=0)
timer.track_passthrough("git tag --list", "rtk git tag --list");
Ok(())
}
```
## Data Formats
### JSON Export Schema
#### DayStats JSON
```json
{
"date": "2026-02-03",
"commands": 42,
"input_tokens": 15420,
"output_tokens": 3842,
"saved_tokens": 11578,
"savings_pct": 75.08,
"total_time_ms": 8450,
"avg_time_ms": 201
}
```
#### WeekStats JSON
```json
{
"week_start": "2026-01-27",
"week_end": "2026-02-02",
"commands": 284,
"input_tokens": 98234,
"output_tokens": 19847,
"saved_tokens": 78387,
"savings_pct": 79.80,
"total_time_ms": 56780,
"avg_time_ms": 200
}
```
#### MonthStats JSON
```json
{
"month": "2026-02",
"commands": 1247,
"input_tokens": 456789,
"output_tokens": 91358,
"saved_tokens": 365431,
"savings_pct": 80.00,
"total_time_ms": 249560,
"avg_time_ms": 200
}
```
### CSV Export Schema
```csv
date,commands,input_tokens,output_tokens,saved_tokens,savings_pct,total_time_ms,avg_time_ms
2026-02-03,42,15420,3842,11578,75.08,8450,201
2026-02-02,38,14230,3557,10673,75.00,7600,200
2026-02-01,45,16890,4223,12667,75.00,9000,200
```
## Integration Examples
### GitHub Actions - Track Savings in CI
```yaml
# .github/workflows/track-rtk-savings.yml
name: Track RTK Savings
on:
schedule:
- cron: '0 0 * * 1' # Weekly on Monday
workflow_dispatch:
jobs:
track-savings:
runs-on: ubuntu-latest
steps:
- name: Install RTK
run: cargo install --git https://github.com/rtk-ai/rtk
- name: Export weekly stats
run: |
rtk gain --weekly --format json > rtk-weekly.json
cat rtk-weekly.json
- name: Upload artifact
uses: actions/upload-artifact@v3
with:
name: rtk-metrics
path: rtk-weekly.json
- name: Post to Slack
if: success()
env:
SLACK_WEBHOOK: ${{ secrets.SLACK_WEBHOOK }}
run: |
SAVINGS=$(jq -r '.[0].saved_tokens' rtk-weekly.json)
PCT=$(jq -r '.[0].savings_pct' rtk-weekly.json)
curl -X POST -H 'Content-type: application/json' \
--data "{\"text\":\"📊 RTK Weekly: ${SAVINGS} tokens saved (${PCT}%)\"}" \
$SLACK_WEBHOOK
```
### Custom Dashboard Script
```python
#!/usr/bin/env python3
"""
Export RTK metrics to Grafana/Datadog/etc.
"""
import json
import subprocess
from datetime import datetime
def get_rtk_metrics():
"""Fetch RTK metrics as JSON."""
result = subprocess.run(
["rtk", "gain", "--all", "--format", "json"],
capture_output=True,
text=True
)
return json.loads(result.stdout)
def export_to_datadog(metrics):
"""Send metrics to Datadog."""
import datadog
datadog.initialize(api_key="YOUR_API_KEY")
for day in metrics.get("daily", []):
datadog.api.Metric.send(
metric="rtk.tokens_saved",
points=[(datetime.now().timestamp(), day["saved_tokens"])],
tags=[f"date:{day['date']}"]
)
datadog.api.Metric.send(
metric="rtk.savings_pct",
points=[(datetime.now().timestamp(), day["savings_pct"])],
tags=[f"date:{day['date']}"]
)
if __name__ == "__main__":
metrics = get_rtk_metrics()
export_to_datadog(metrics)
print(f"Exported {len(metrics.get('daily', []))} days to Datadog")
```
### Rust Integration (Using RTK as Library)
```rust
// In your Cargo.toml
// [dependencies]
// rtk = { git = "https://github.com/rtk-ai/rtk" }
use rtk::tracking::{Tracker, TimedExecution};
use anyhow::Result;
fn main() -> Result<()> {
// Track your own commands
let timer = TimedExecution::start();
let input = run_expensive_operation()?;
let output = run_optimized_operation()?;
timer.track(
"expensive_operation",
"optimized_operation",
&input,
&output
);
// Query aggregated stats
let tracker = Tracker::new()?;
let summary = tracker.get_summary()?;
println!("Total savings: {} tokens ({:.1}%)",
summary.total_saved,
summary.avg_savings_pct
);
// Export to JSON for external tools
let days = tracker.get_all_days()?;
let json = serde_json::to_string_pretty(&days)?;
std::fs::write("metrics.json", json)?;
Ok(())
}
```
## Database Schema
### Table: `commands`
```sql
CREATE TABLE commands (
id INTEGER PRIMARY KEY,
timestamp TEXT NOT NULL, -- RFC3339 UTC timestamp
original_cmd TEXT NOT NULL, -- Original command (e.g., "ls -la")
rtk_cmd TEXT NOT NULL, -- RTK command (e.g., "rtk ls")
input_tokens INTEGER NOT NULL, -- Estimated input tokens
output_tokens INTEGER NOT NULL, -- Actual output tokens
saved_tokens INTEGER NOT NULL, -- input_tokens - output_tokens
savings_pct REAL NOT NULL, -- (saved/input) * 100
exec_time_ms INTEGER DEFAULT 0 -- Execution time in milliseconds
);
CREATE INDEX idx_timestamp ON commands(timestamp);
```
### Automatic Cleanup
On every write operation (`Tracker::record`), records older than 90 days are deleted:
```rust
fn cleanup_old(&self) -> Result<()> {
let cutoff = Utc::now() - chrono::Duration::days(90);
self.conn.execute(
"DELETE FROM commands WHERE timestamp < ?1",
params![cutoff.to_rfc3339()],
)?;
Ok(())
}
```
### Migration Support
The system automatically adds new columns if they don't exist (e.g., `exec_time_ms` was added later):
```rust
// Safe migration on Tracker::new()
let _ = conn.execute(
"ALTER TABLE commands ADD COLUMN exec_time_ms INTEGER DEFAULT 0",
[],
);
```
## Performance Considerations
- **SQLite WAL mode**: Not enabled (may add in future for concurrent writes)
- **Index on timestamp**: Enables fast date-range queries
- **Automatic cleanup**: Prevents database from growing unbounded
- **Token estimation**: ~4 chars = 1 token (simple, fast approximation)
- **Aggregation queries**: Use SQL GROUP BY for efficient aggregation
## Security & Privacy
- **Local storage only**: Tracking database never leaves the machine
- **Telemetry requires consent**: RTK can send a daily anonymous usage ping (version, OS, command counts, token savings). Disabled by default, requires explicit consent via `rtk init` or `rtk telemetry enable`. Manage with `rtk telemetry status/disable/forget`. Override: `RTK_TELEMETRY_DISABLED=1`
- **User control**: Users can delete `~/.local/share/rtk/tracking.db` anytime
- **90-day retention**: Old data automatically purged
## Troubleshooting
### Database locked error
If you see "database is locked" errors:
- Ensure only one RTK process writes at a time
- Check file permissions on `~/.local/share/rtk/tracking.db`
- Delete and recreate: `rm ~/.local/share/rtk/tracking.db && rtk gain`
### Missing exec_time_ms column
Older databases may not have the `exec_time_ms` column. RTK automatically migrates on first use, but you can force it:
```bash
sqlite3 ~/.local/share/rtk/tracking.db \
"ALTER TABLE commands ADD COLUMN exec_time_ms INTEGER DEFAULT 0"
```
### Incorrect token counts
Token estimation uses `~4 chars = 1 token`. This is approximate. For precise counts, integrate with your LLM's tokenizer API.
## Future Enhancements
Planned improvements (contributions welcome):
- [ ] Export to Prometheus/OpenMetrics format
- [ ] Support for custom retention periods (not just 90 days)
- [ ] SQLite WAL mode for concurrent writes
- [ ] Per-project tracking (multiple databases)
- [ ] Integration with Claude API for precise token counts
- [ ] Web dashboard (localhost) for visualizing trends
## See Also
- [README.md](../README.md) - Main project documentation
- [COMMAND_AUDIT.md](../claudedocs/COMMAND_AUDIT.md) - List of all RTK commands
- [Rust docs](https://docs.rs/) - Run `cargo doc --open` for API docs