Skip to content
Merged
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
}
]
}
}
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` |
Loading
Loading