Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
127 changes: 127 additions & 0 deletions .github/copilot-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# Copilot Instructions for rtk

**rtk (Rust Token Killer)** is a CLI proxy that filters and compresses command outputs before they reach an LLM context, saving 60–90% of tokens. It wraps common tools (`git`, `cargo`, `grep`, `pnpm`, `go`, etc.) and outputs condensed summaries instead of raw output.

## Using rtk in this session

**Always prefix commands with `rtk` when running shell commands** — this is the entire point of the project and reduces token consumption for every operation you perform.

```bash
# Instead of: Use:
git status rtk git status
git log -10 rtk git log -10
cargo test rtk cargo test
cargo clippy --all-targets rtk cargo clippy --all-targets
grep -r "pattern" src/ rtk grep -r "pattern" src/
```

**rtk meta-commands** (always use these directly, no prefix needed):
```bash
rtk gain # Show token savings analytics for this session
rtk gain --history # Full command history with per-command savings
rtk discover # Scan session history for missed rtk opportunities
rtk proxy <cmd> # Run a command raw (no filtering) but still track it
```

**Verify rtk is installed before starting:**
```bash
rtk --version # Should print: rtk X.Y.Z
rtk gain # Should show a dashboard (not "command not found")
```

> ⚠️ **Name collision**: `rtk gain` failing means you have `reachingforthejack/rtk` (Rust Type Kit) installed instead of this project. Run `which rtk` and check the binary source.

## Build, Test & Lint

```bash
# Development build
cargo build

# Run all tests
cargo test

# Run a single test by name
cargo test test_filter_git_log

# Run all tests in a module
cargo test git::tests::

# Run tests with stdout
cargo test -- --nocapture

# Pre-commit gate (must all pass before any PR)
cargo fmt --all --check && cargo clippy --all-targets && cargo test

# Smoke tests (requires installed binary)
bash scripts/test-all.sh
```

PRs target the **`develop`** branch, not `main`. All commits require a DCO sign-off (`git commit -s`).

## Architecture

```
main.rs ← Clap Commands enum → specialized module (git.rs, *_cmd.rs, etc.)
execute subprocess
filter/compress output
tracking::TimedExecution → SQLite (~/.local/share/rtk/tracking.db)
```

Key modules:
- **`main.rs`** — Clap `Commands` enum routes every subcommand to its module. Each arm calls `tracking::TimedExecution::start()` before running, then `.track(...)` after.
- **`filter.rs`** — Language-aware filtering with `FilterLevel` (`none` / `minimal` / `aggressive`) and `Language` enum. Used by `read` and `smart` commands.
- **`tracking.rs`** — SQLite persistence for token savings, scoped per project path. Powers `rtk gain`.
- **`tee.rs`** — On filter failure, saves raw output to `~/.local/share/rtk/tee/` and prints a one-line hint so the LLM can re-read without re-running the command.
- **`utils.rs`** — Shared helpers: `truncate`, `strip_ansi`, `execute_command`, package-manager auto-detection (pnpm/yarn/npm/npx).

New commands follow this structure: one file `src/<cmd>_cmd.rs` with a `pub fn run(...)` entry point, registered in the `Commands` enum in `main.rs`.

## Key Conventions

### Error handling
- Use `anyhow::Result` throughout (this is a binary, not a library).
- Always attach context: `operation.context("description")?` — never bare `?` without context.
- No `unwrap()` in production code; `expect("reason")` is acceptable only in tests.
- Every filter must fall back to raw command execution on error — never break the user's workflow.

### Regex
- Compile once with `lazy_static!`, never inside a function body:
```rust
lazy_static! {
static ref RE: Regex = Regex::new(r"pattern").unwrap();
}
```

### Testing
- Unit tests live **inside the module file** in `#[cfg(test)] mod tests { ... }` — not in `tests/`.
- Fixtures are real captured command output in `tests/fixtures/<cmd>_raw.txt`, loaded with `include_str!("../tests/fixtures/...")`.
- Each test module defines its own local `fn count_tokens(text: &str) -> usize` (word-split approximation) — there is no shared utility for this.
- Token savings assertions use `assert!(savings >= 60.0, ...)`.
- Snapshot tests use `assert_snapshot!()` from the `insta` crate; review with `cargo insta review`.

### Adding a new command
1. Create `src/<cmd>_cmd.rs` with `pub fn run(...)`.
2. Add `mod <cmd>_cmd;` at the top of `main.rs`.
3. Add a variant to the `Commands` enum with `#[arg(trailing_var_arg = true, allow_hyphen_values = true)]` for pass-through flags.
4. Route the variant in the `match` block, wrapping execution with `tracking::TimedExecution`.
5. Write a fixture from real output, then unit tests in the module file.
6. Update `README.md` (command list + savings %) and `CHANGELOG.md`.

### Exit codes
Preserve the underlying command's exit code. Use `std::process::exit(code)` when the child process exits non-zero.

### Performance constraints
- Startup must stay under 10ms — no async runtime (no `tokio`/`async-std`).
- No blocking I/O at startup; config is loaded on-demand.
- Binary size target: <5 MB stripped.

### Branch naming
```
fix(scope): short-description
feat(scope): short-description
chore(scope): short-description
```
`scope` is the affected component (e.g. `git`, `filter`, `tracking`).
12 changes: 12 additions & 0 deletions .github/hooks/rtk-rewrite.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"hooks": {
"PreToolUse": [
{
"type": "command",
"command": "rtk hook",
"cwd": ".",
"timeout": 5
}
]
}
}
4 changes: 2 additions & 2 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -297,8 +297,8 @@ SHARED utils.rs Helpers N/A ✓

### Module Count Breakdown

- **Command Modules**: 34 (directly exposed to users)
- **Infrastructure Modules**: 20 (utils, filter, tracking, tee, config, init, gain, toml_filter, verify_cmd, etc.)
- **Command Modules**: 44 (directly exposed to users)
- **Infrastructure Modules**: 22 (utils, filter, tracking, tee, config, init, gain, toml_filter, verify_cmd, trust, etc.)
- **Git Commands**: 7 operations (status, diff, log, add, commit, push, branch/checkout)
- **JS/TS Tooling**: 8 modules (modern frontend/fullstack development)
- **Python Tooling**: 3 modules (ruff, pytest, pip)
Expand Down
23 changes: 22 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ rtk gain # Should show token savings stats
# 1. Install hook for Claude Code (recommended)
rtk init --global
# Follow instructions to register in ~/.claude/settings.json
# Claude Code only by default (use --opencode for OpenCode)
# Claude Code only by default (use --opencode for OpenCode, --gemini for Gemini CLI)

# 2. Restart Claude Code, then test
git status # Automatically rewritten to rtk git status
Expand Down Expand Up @@ -287,6 +287,27 @@ rtk init --show # Verify installation

After install, **restart Claude Code**.

## Gemini CLI Support (Global)

RTK supports Gemini CLI via a native Rust hook processor. The hook intercepts `run_shell_command` tool calls and rewrites them to `rtk` equivalents using the same rewrite engine as Claude Code.

**Install Gemini hook:**
```bash
rtk init -g --gemini
```

**What it creates:**
- `~/.gemini/hooks/rtk-hook-gemini.sh` (thin wrapper calling `rtk hook gemini`)
- `~/.gemini/GEMINI.md` (RTK awareness instructions)
- Patches `~/.gemini/settings.json` with BeforeTool hook

**Uninstall:**
```bash
rtk init -g --gemini --uninstall
```

**Restart Required**: Restart Gemini CLI, then test with `git status` in a session.

## OpenCode Plugin (Global)

OpenCode supports plugins that can intercept tool execution. RTK provides a global plugin that mirrors the Claude auto-rewrite behavior by rewriting Bash tool commands to `rtk ...` before they execute. This plugin is **not** installed by default.
Expand Down
32 changes: 32 additions & 0 deletions hooks/cline-rtk-rules.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RTK - Rust Token Killer (Cline)

**Usage**: Token-optimized CLI proxy for shell commands.

## Rule

Always prefix shell commands with `rtk` to minimize token consumption.

Examples:

```bash
rtk git status
rtk cargo test
rtk ls src/
rtk grep "pattern" src/
rtk find "*.rs" .
rtk docker ps
rtk gh pr list
```

## Meta Commands

```bash
rtk gain # Show token savings
rtk gain --history # Command history with savings
rtk discover # Find missed RTK opportunities
rtk proxy <cmd> # Run raw (no filtering, for debugging)
```

## Why

RTK filters and compresses command output before it reaches the LLM context, saving 60-90% tokens on common operations. Always use `rtk <cmd>` instead of raw commands.
60 changes: 60 additions & 0 deletions hooks/copilot-rtk-awareness.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# RTK — Copilot Integration (VS Code Copilot Chat + Copilot CLI)

**Usage**: Token-optimized CLI proxy (60-90% savings on dev operations)

## What's automatic

The `.github/copilot-instructions.md` file is loaded at session start by both Copilot CLI and VS Code Copilot Chat.
It instructs Copilot to prefix commands with `rtk` automatically.

The `.github/hooks/rtk-rewrite.json` hook adds a `PreToolUse` safety net via `rtk hook` —
a cross-platform Rust binary that intercepts raw bash tool calls and rewrites them.
No shell scripts, no `jq` dependency, works on Windows natively.

## Meta commands (always use directly)

```bash
rtk gain # Token savings dashboard for this session
rtk gain --history # Per-command history with savings %
rtk discover # Scan session history for missed rtk opportunities
rtk proxy <cmd> # Run raw (no filtering) but still track it
```

## Installation verification

```bash
rtk --version # Should print: rtk X.Y.Z
rtk gain # Should show a dashboard (not "command not found")
which rtk # Verify correct binary path
```

> ⚠️ **Name collision**: If `rtk gain` fails, you may have `reachingforthejack/rtk`
> (Rust Type Kit) installed instead. Check `which rtk` and reinstall from rtk-ai/rtk.

## How the hook works

`rtk hook` reads `PreToolUse` JSON from stdin, detects the agent format, and responds appropriately:

**VS Code Copilot Chat** (supports `updatedInput` — transparent rewrite, no denial):
1. Agent runs `git status` → `rtk hook` intercepts via `PreToolUse`
2. `rtk hook` detects VS Code format (`tool_name`/`tool_input` keys)
3. Returns `hookSpecificOutput.updatedInput.command = "rtk git status"`
4. Agent runs the rewritten command silently — no denial, no retry

**GitHub Copilot CLI** (deny-with-suggestion — CLI ignores `updatedInput` today, see [issue #2013](https://github.com/github/copilot-cli/issues/2013)):
1. Agent runs `git status` → `rtk hook` intercepts via `PreToolUse`
2. `rtk hook` detects Copilot CLI format (`toolName`/`toolArgs` keys)
3. Returns `permissionDecision: deny` with reason: `"Token savings: use 'rtk git status' instead"`
4. Copilot reads the reason and re-runs `rtk git status`

When Copilot CLI adds `updatedInput` support, only `rtk hook` needs updating — no config changes.

## Integration comparison

| Tool | Mechanism | Hook output | File |
|-----------------------|-----------------------------------------|--------------------------|------------------------------------|
| Claude Code | `PreToolUse` hook with `updatedInput` | Transparent rewrite | `hooks/rtk-rewrite.sh` |
| VS Code Copilot Chat | `PreToolUse` hook with `updatedInput` | Transparent rewrite | `.github/hooks/rtk-rewrite.json` |
| GitHub Copilot CLI | `PreToolUse` deny-with-suggestion | Denial + retry | `.github/hooks/rtk-rewrite.json` |
| OpenCode | Plugin `tool.execute.before` | Transparent rewrite | `hooks/opencode-rtk.ts` |
| (any) | Custom instructions | Prompt-level guidance | `.github/copilot-instructions.md` |
54 changes: 54 additions & 0 deletions hooks/cursor-rtk-rewrite.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#!/usr/bin/env bash
# rtk-hook-version: 1
# RTK Cursor Agent hook — rewrites shell commands to use rtk for token savings.
# Works with both Cursor editor and cursor-cli (they share ~/.cursor/hooks.json).
# Cursor preToolUse hook format: receives JSON on stdin, returns JSON on stdout.
# Requires: rtk >= 0.23.0, jq
#
# This is a thin delegating hook: all rewrite logic lives in `rtk rewrite`,
# which is the single source of truth (src/discover/registry.rs).
# To add or change rewrite rules, edit the Rust registry — not this file.

if ! command -v jq &>/dev/null; then
echo "[rtk] WARNING: jq is not installed. Hook cannot rewrite commands. Install jq: https://jqlang.github.io/jq/download/" >&2
exit 0
fi

if ! command -v rtk &>/dev/null; then
echo "[rtk] WARNING: rtk is not installed or not in PATH. Hook cannot rewrite commands. Install: https://github.com/rtk-ai/rtk#installation" >&2
exit 0
fi

# Version guard: rtk rewrite was added in 0.23.0.
RTK_VERSION=$(rtk --version 2>/dev/null | grep -oE '[0-9]+\.[0-9]+\.[0-9]+' | head -1)
if [ -n "$RTK_VERSION" ]; then
MAJOR=$(echo "$RTK_VERSION" | cut -d. -f1)
MINOR=$(echo "$RTK_VERSION" | cut -d. -f2)
if [ "$MAJOR" -eq 0 ] && [ "$MINOR" -lt 23 ]; then
echo "[rtk] WARNING: rtk $RTK_VERSION is too old (need >= 0.23.0). Upgrade: cargo install rtk" >&2
exit 0
fi
fi

INPUT=$(cat)
CMD=$(echo "$INPUT" | jq -r '.tool_input.command // empty')

if [ -z "$CMD" ]; then
echo '{}'
exit 0
fi

# Delegate all rewrite logic to the Rust binary.
# rtk rewrite exits 1 when there's no rewrite — hook passes through silently.
REWRITTEN=$(rtk rewrite "$CMD" 2>/dev/null) || { echo '{}'; exit 0; }

# No change — nothing to do.
if [ "$CMD" = "$REWRITTEN" ]; then
echo '{}'
exit 0
fi

jq -n --arg cmd "$REWRITTEN" '{
"permission": "allow",
"updated_input": { "command": $cmd }
}'
32 changes: 32 additions & 0 deletions hooks/rtk-awareness-codex.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# RTK - Rust Token Killer (Codex CLI)

**Usage**: Token-optimized CLI proxy for shell commands.

## Rule

Always prefix shell commands with `rtk`.

Examples:

```bash
rtk git status
rtk cargo test
rtk npm run build
rtk pytest -q
```

## Meta Commands

```bash
rtk gain # Token savings analytics
rtk gain --history # Recent command savings history
rtk proxy <cmd> # Run raw command without filtering
```

## Verification

```bash
rtk --version
rtk gain
which rtk
```
Loading
Loading