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
2 changes: 1 addition & 1 deletion .claude/agents/rtk-testing-specialist.md
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,7 @@ rtk/
├── src/
│ ├── cmds/
│ │ ├── git/
│ │ │ ├── git.rs # Filter implementation
│ │ │ ├── git_cmd.rs # Filter implementation
│ │ │ │ └── #[cfg(test)] mod tests { ... } # Unit tests
│ │ │ └── snapshots/ # Insta snapshots for git module
│ │ ├── js/
Expand Down
2 changes: 1 addition & 1 deletion .claude/agents/rust-rtk.md
Original file line number Diff line number Diff line change
Expand Up @@ -315,7 +315,7 @@ fn test_real_git_log() {
- `src/core/config.rs` - User configuration (~/.config/rtk/config.toml)

**Command modules** (`src/cmds/<ecosystem>/`):
- `src/cmds/git/` - git.rs, gh_cmd.rs, gt_cmd.rs, diff_cmd.rs
- `src/cmds/git/` - git_cmd.rs, gh_cmd.rs, gt_cmd.rs, diff_cmd.rs
- `src/cmds/rust/` - cargo_cmd.rs, runner.rs
- `src/cmds/js/` - lint_cmd.rs, tsc_cmd.rs, next_cmd.rs, prettier_cmd.rs, playwright_cmd.rs, prisma_cmd.rs, vitest_cmd.rs, pnpm_cmd.rs, npm_cmd.rs
- `src/cmds/python/` - ruff_cmd.rs, pytest_cmd.rs, mypy_cmd.rs, pip_cmd.rs
Expand Down
2 changes: 1 addition & 1 deletion .claude/agents/system-architect.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Think in terms of filter families, not individual commands. Every new `*_cmd.rs`
```
src/main.rs
├── Commands enum (clap derive)
│ ├── Git(GitArgs) → cmds/git/git.rs
│ ├── Git(GitArgs) → cmds/git/git_cmd.rs
│ ├── Cargo(CargoArgs) → cmds/rust/runner.rs
│ ├── Gh(GhArgs) → cmds/git/gh_cmd.rs
│ ├── Grep(GrepArgs) → cmds/system/grep_cmd.rs
Expand Down
4 changes: 2 additions & 2 deletions .claude/rules/cli-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ mod tests {
Two patterns coexist, pick based on what the filter needs:

1. **Inline literal strings** (most common for `src/cmds/**` unit tests) — build a small
representative string directly in the test body. Used throughout `src/cmds/git/git.rs`,
representative string directly in the test body. Used throughout `src/cmds/git/git_cmd.rs`,
`src/cmds/git/gh_cmd.rs`, etc. Good for quick coverage of a specific format/edge case.
2. **Real captured fixtures via `include_str!`** — used when the raw output is large or
format-sensitive enough that inline strings would be unreadable or drift from reality.
Expand Down Expand Up @@ -306,7 +306,7 @@ rtk/
├── src/
│ ├── cmds/
│ │ ├── git/
│ │ │ ├── git.rs # Filter implementation
│ │ │ ├── git_cmd.rs # Filter implementation
│ │ │ │ └── #[cfg(test)] mod tests { ... }
│ │ ├── jvm/ # gradlew, mvn — reference example for include_str! fixtures
│ │ ├── php/ # php, artisan, phpunit, phpstan, pest, paratest, ecs, pint
Expand Down
2 changes: 1 addition & 1 deletion .claude/rules/search-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ Grep pattern="fn filter_git_log\|fn run\b" type="rust"

```
Glob pattern="src/cmds/**/*_cmd.rs"
# Also: src/cmds/git/git.rs, src/cmds/rust/runner.rs, src/cmds/cloud/container.rs
# Also: src/cmds/git/git_cmd.rs, src/cmds/rust/runner.rs, src/cmds/cloud/container.rs
```

### "Find all LazyLock regex definitions"
Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/rtk-tdd/references/testing-patterns.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ more noise
}
```

Used in: `git.rs`, `grep_cmd.rs`, `lint_cmd.rs`, `tsc_cmd.rs`, `vitest_cmd.rs`, `pnpm_cmd.rs`, `next_cmd.rs`, `prettier_cmd.rs`, `playwright_cmd.rs`, `prisma_cmd.rs`
Used in: `git_cmd.rs`, `grep_cmd.rs`, `lint_cmd.rs`, `tsc_cmd.rs`, `vitest_cmd.rs`, `pnpm_cmd.rs`, `next_cmd.rs`, `prettier_cmd.rs`, `playwright_cmd.rs`, `prisma_cmd.rs`

### Pattern 2: Pure Computation

Expand Down
10 changes: 10 additions & 0 deletions .git-blame-ignore-revs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Bulk-formatting commits, ignored so `git blame` reports the author who wrote
# a line rather than whoever last reformatted it. GitHub's web blame applies
# this automatically; locally it needs opting in once:
#
# git config blame.ignoreRevsFile .git-blame-ignore-revs
#
# Only commits with no behaviour change belong here.

# chore: reformat under rustfmt style edition 2024
ad63139576f77767299ac1c0f4bca5b52fe80442
7 changes: 7 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,13 @@ git pull origin develop
git checkout -b feat/scope-your-clear-description
```

Opt in once per clone so `git blame` skips the bulk-formatting commits listed in
`.git-blame-ignore-revs` and reports whoever actually wrote each line:

```bash
git config blame.ignoreRevsFile .git-blame-ignore-revs
```

### 2. Make Your Changes

**Respect the existing folder structure.** Place new files where similar files already live. Do not reorganize without prior discussion.
Expand Down
69 changes: 57 additions & 12 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[package]
name = "rtk"
version = "0.48.0"
edition = "2021"
edition = "2024"
rust-version = "1.91"
authors = ["Patrick Szymkowiak"]
description = "Rust Token Killer - High-performance CLI proxy to minimize LLM token consumption"
Expand Down Expand Up @@ -41,7 +41,6 @@ getrandom = "0.4"
flate2 = "1.0"
quick-xml = "0.37"
which = "8"
automod = "1"

[target.'cfg(unix)'.dependencies]
libc = "0.2"
Expand All @@ -55,6 +54,12 @@ windows-sys = { version = "0.59", features = [
[build-dependencies]
toml = "0.8"

[dev-dependencies]
# Safe, closure-scoped env mutation for tests. std::env::set_var is unsafe
# in edition 2024 and has no safe std replacement; temp-env restores the
# previous value on completion or panic and serializes callers internally.
temp-env = "0.3"

[profile.release]
opt-level = 3
lto = true
Expand Down
51 changes: 51 additions & 0 deletions build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ fn main() {
println!("cargo:rustc-link-arg=/STACK:8388608");
}

check_cmds_modules_declared();

let filters_dir = Path::new("src/filters");
let out_dir = std::env::var("OUT_DIR").expect("OUT_DIR must be set by Cargo");
let dest = Path::new(&out_dir).join("builtin_filters.toml");
Expand Down Expand Up @@ -64,3 +66,52 @@ fn main() {

fs::write(&dest, combined).expect("Failed to write combined builtin_filters.toml");
}

/// Every `.rs` under `src/cmds/<ecosystem>/` must be declared in that folder's
/// `mod.rs`. An undeclared file is not part of the crate: it is never compiled,
/// never linted, and its tests never run, all without any diagnostic.
fn check_cmds_modules_declared() {
println!("cargo:rerun-if-changed=src/cmds");

let cmds = Path::new("src/cmds");
let ecosystems = fs::read_dir(cmds).expect("src/cmds/ directory must exist");

for ecosystem in ecosystems {
let dir = ecosystem.expect("failed to read src/cmds/ entry").path();
let mod_rs = dir.join("mod.rs");
if !dir.is_dir() || !mod_rs.exists() {
continue;
}

let declared = fs::read_to_string(&mod_rs)
.unwrap_or_else(|e| panic!("Failed to read {}: {}", mod_rs.display(), e));

let entries = fs::read_dir(&dir)
.unwrap_or_else(|e| panic!("Failed to read {}: {}", dir.display(), e));

for entry in entries {
let path = entry.expect("failed to read directory entry").path();
if path.extension().is_none_or(|ext| ext != "rs") {
continue;
}
let stem = path
.file_stem()
.expect("a .rs path always has a stem")
.to_string_lossy()
.to_string();
if stem == "mod" {
continue;
}
if !declared.contains(&format!("mod {stem};")) {
panic!(
"{} is not declared in {}.\n\
Add `pub mod {};` there, keeping the list alphabetical.\n\
An undeclared module is never compiled, linted, or tested.",
path.display(),
mod_rs.display(),
stem
);
}
}
}
}
6 changes: 3 additions & 3 deletions docs/contributing/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -765,7 +765,7 @@ Levels:
│ -vvv │ + Raw output before filtering │
└─────────┴──────────────────────────────────────────────────────┘

Example (git.rs:67-69):
Example (git_cmd.rs:67-69):
if verbose > 0 {
eprintln!("Git diff summary:");
}
Expand Down Expand Up @@ -839,7 +839,7 @@ std::process::exit(1)
│ Exit Code Handling Strategy │
└────────────────────────────────────────────────────────────────────────┘

Standard Pattern (git.rs:45-48, PR #5):
Standard Pattern (git_cmd.rs:45-48, PR #5):

let output = Command::new("git").args(args).output()?;

Expand All @@ -865,7 +865,7 @@ Why This Matters:
• Git workflows require proper exit code propagation (PR #5 fix)

Modules with Exit Code Preservation:
• git.rs (all git commands)
• git_cmd.rs (all git commands)
• lint_cmd.rs (linter failures)
• tsc_cmd.rs (TypeScript errors)
• vitest_cmd.rs (test failures)
Expand Down
4 changes: 2 additions & 2 deletions docs/contributing/CODING_PRACTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ Our goal is to keep the codebase consistent and easy to extend. PRs that deviate
New to RTK? The fastest path to a mergeable first PR:

1. **Read the flow once.** Start at [`CONTRIBUTING.md`](../../CONTRIBUTING.md), then skim [`docs/contributing/TECHNICAL.md`](TECHNICAL.md) to see how a command flows from `main.rs` → a `*_cmd.rs` filter → tracking → stdout.
2. **Look at a good example.** [`src/cmds/git/git.rs`](../../src/cmds/git/git.rs) is a representative filter — it shows the `run()` entry point, `LazyLock` regex setup, filter helpers, and embedded tests all in one file.
2. **Look at a good example.** [`src/cmds/git/git_cmd.rs`](../../src/cmds/git/git_cmd.rs) is a representative filter — it shows the `run()` entry point, `LazyLock` regex setup, filter helpers, and embedded tests all in one file.
3. **Know the shared helpers before reimplementing.** Two files cover most of what you need:
- [`src/core/runner.rs`](../../src/core/runner.rs) — command execution wrappers: `run_filtered()` (run a command, then apply your filter function), `run_passthrough()` (run unfiltered but tracked), `run_streamed()` (streaming filter).
- [`src/core/utils.rs`](../../src/core/utils.rs) — shared utilities: `resolved_command()`, `strip_ansi()`, `truncate()`, `count_tokens()`, and more.
Expand Down Expand Up @@ -81,7 +81,7 @@ Use explicit, descriptive names for variables, just like for functions.

Do not hardcode repetitive patterns or values that control behavior — extract them into named constants at the top of the file. For anything a user might want to tune (thresholds, limits, display cutoffs), use `config::limits()` so it flows through `~/.config/rtk/config.toml`.

Example from `src/cmds/git/git.rs`:
Example from `src/cmds/git/git_cmd.rs`:

```rust
let limits = config::limits();
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/resources/troubleshooting.md
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ cargo build --release
cargo install --path . --force
```

Minimum required Rust version: 1.70+.
Minimum required Rust version: 1.91 (edition 2024 needs Cargo 1.85 or newer).

## OpenCode not using RTK

Expand Down
6 changes: 6 additions & 0 deletions rustfmt.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# cargo fmt passes --edition from Cargo.toml, but a bare `rustfmt` invocation --
# editor format-on-save, rust-analyzer.rustfmt.overrideCommand, a pre-commit
# hook -- has no crate context and falls back to edition 2015, which cannot even
# parse this crate's let-chains. Pin it here so those agree with CI.
edition = "2024"
style_edition = "2024"
Loading
Loading