Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
192 commits
Select commit Hold shift + click to select a range
3709dd6
feat(hook): add support for Kimi AI agent
williamkoller Apr 23, 2026
9b8c0b5
feat(sbt): add SBT (Scala Build Tool) support
randomBrainstormer Mar 20, 2026
d078578
feat(sbt): capture failure details, add integration test routing, han…
randomBrainstormer Apr 6, 2026
1640eaa
fix(sbt): add Debug derive to SbtCommands to satisfy Commands enum re…
randomBrainstormer May 14, 2026
3586e8b
fix(sbt): add munit summary support and fix misleading parse error label
randomBrainstormer May 17, 2026
189409b
feat(sbt): support -batch flag and scoped task notation (Test/compile)
randomBrainstormer May 25, 2026
5e3e54a
feat: add uv run support
Apr 19, 2026
99307b2
fix(cargo): surface --message-format=json build errors instead of "co…
luccinmasirika Jun 12, 2026
877c7ef
refactor(cargo): single-pass json diagnostics and a shared build summary
luccinmasirika Jun 13, 2026
5a0a86d
fix(copilot): add missing 'get' to kubectl example in init template
K0IN Jun 17, 2026
5ee9c82
fix(cargo): cap json build diagnostics like the human-text path
luccinmasirika Jun 18, 2026
2319194
test(cargo): assert token savings on the json build-failure path
luccinmasirika Jun 18, 2026
d061c23
test(cargo): cover the json build success path
luccinmasirika Jun 18, 2026
30fe7a4
feat(php): consolidated PHP tooling (php, artisan, phpunit, phpstan, …
iliaal Apr 30, 2026
a58de9d
fix(run): propagate signal exit code instead of unwrap_or(1)
guyoron1 Jun 28, 2026
113ae11
Merge pull request #2681 from guyoron1/fix/run-signal-exit-code
aeppling Jun 28, 2026
c37eed9
fix(test): adapt new rewrite_command signature in php-tooling tests
iliaal May 17, 2026
214a79a
feat(pipe): expose PHP tool filters as stdin pipe filters
EliW Jun 18, 2026
0b75581
fix(php): align pint/phpstan parsers with current tool schemas
iliaal Jun 22, 2026
88e56ce
fix(php): rewrite ./vendor/bin/<tool> form for phpunit/pest/paratest/…
iliaal Jun 23, 2026
4a37246
fix(php): classify php subcommands in the PASSTHROUGH list
iliaal Jun 26, 2026
8eae6b7
fix(php): route php_tool_command through resolved_command
iliaal Jun 26, 2026
07c231e
fix(php): address phpstan review feedback (resolution, text fallback,…
iliaal Jun 27, 2026
128d04f
fix(php): anchor phpunit failure-heading detection to the "N) " format
iliaal Jun 27, 2026
b5c3f7f
perf(php): resolve cwd once in pint output instead of per file
iliaal Jun 27, 2026
6117bb4
refactor(php): standardize tool regexes and normalize invocation in r…
iliaal Jun 28, 2026
ff0b9ac
fix(hook): handle AskRewrite in Cursor hook when no rules configured
guyoron1 Jun 25, 2026
3362325
fix(hook): include ask rules in cursor_has_explicit_rules check
guyoron1 Jun 25, 2026
a0c16ef
fix(hook): use ask permission for AskRewrite in Cursor hook
guyoron1 Jun 29, 2026
36dd8f2
fix(hook): use ask action in audit log for AskRewrite verdict
guyoron1 Jun 29, 2026
73b8cb3
feat(hook): wire TOML filters into the rewrite path + reversible trun…
aeppling Jun 29, 2026
d0a77cd
fix(ccusage): accept `period` key from current ccusage
mvanhorn Jun 29, 2026
0cc15dc
fix(php): default pint applied_fixers when key is absent
iliaal Jun 29, 2026
487a2e7
fix(openclaw): handle exit code 3 from rtk rewrite
kzzalews Jun 30, 2026
8825480
fix(php): strip ANSI in phpunit filter and split errors from failures
iliaal Jun 30, 2026
7cc46b4
fix(php): detect phpstan analyse after global flags; avoid duplicate …
iliaal Jun 30, 2026
28366ce
fix(php): drop bogus pest.php check in test-runner detection
iliaal Jun 30, 2026
50a8743
fix(pipe): anchor phpunit auto-detection to the leading banner
iliaal Jun 30, 2026
5d2928c
perf(php): cache composer_bin_dirs to avoid per-segment file reads
iliaal Jun 30, 2026
8a73571
Merge branch 'develop' into feat/hook-kimi-ai-dev
williamkoller Jun 30, 2026
fa0afa5
feat(hook): add Cargo.lock
williamkoller Jun 30, 2026
531bc93
feat(hook): add Cargo.lock
williamkoller Jun 30, 2026
8fe4a40
fix(hook): kimi init writes AGENTS.md instead of dead .kimirules
williamkoller Jun 30, 2026
f768170
docs(readme): kimi agent uses AGENTS.md not .kimirules
williamkoller Jun 30, 2026
a5f0774
Merge pull request #1405 from alexanderkreidich/fix/1379-uv-run-support
aeppling Jun 30, 2026
bd10b00
Follow up on #2609:
KuSh Jun 29, 2026
27f9739
fix(parser): use byte offsets instead of char indices in extract_json…
guyoron1 Jun 30, 2026
7e42d92
fix(uv): pass guard_raw arg to print_with_hint after signature change
guyoron1 Jul 1, 2026
96f9422
fix(uv): remove uv run from transparent prefixes to fix rewrite confl…
guyoron1 Jul 1, 2026
0632d83
style: apply rustfmt to BUILTIN_TRANSPARENT_PREFIXES
guyoron1 Jul 1, 2026
8cdc861
Merge pull request #2768 from guyoron1/fix/uv-transparent-prefix-conf…
aeppling Jul 1, 2026
28948d9
Merge branch 'develop' into fix/cjk-emoji-char-boundary-panic
guyoron1 Jul 1, 2026
6e34bba
fix(cicd): next release pr target fork compatibel
aeppling Jul 1, 2026
fe75595
test(php): add paratest unit tests to satisfy test-presence gate
iliaal Jul 1, 2026
5e34f61
Merge remote-tracking branch 'origin/develop' into feat/php-tooling
iliaal Jul 1, 2026
d67fc28
test(php): add token-savings assertion to paratest per cli-testing guide
iliaal Jul 1, 2026
ce30f37
Merge pull request #2771 from rtk-ai/fix/cicd/pr-targer-next-release
aeppling Jul 1, 2026
1052c1c
Merge pull request #1649 from iliaal/feat/php-tooling
KuSh Jul 1, 2026
1130a7c
feat(trust): gate custom filters (project + user-global) + init opt-i…
aeppling Jul 1, 2026
32dda24
Merge pull request #2751 from guyoron1/fix/cjk-emoji-char-boundary-panic
KuSh Jul 1, 2026
84f9528
docs: document custom filters + trust in the config guide
aeppling Jul 1, 2026
b52e02b
chore(scripts): switch benches to mockhttp.org as httpbin.org is stru…
KuSh Jul 1, 2026
9d3b678
fix(trust): skip untrusted filters silently on the command path
aeppling Jul 1, 2026
e0a83ac
feat(trust): rtk trust uses the same opt-in prompt as init
aeppling Jul 1, 2026
a9c33e2
fix(trust): label detected filters as project- or global-scoped
aeppling Jul 1, 2026
bbeee4c
docs(trust): fix README to match silent skip; note global-filter migr…
aeppling Jul 1, 2026
a4872d9
fix(gain): note untrusted custom filters so they can be re-trusted
aeppling Jul 1, 2026
34c0bcf
perf(hook): match TOML filters with a match-only RegexSet
aeppling Jul 1, 2026
9ae0872
fix(cargo): skip "N warnings generated" record in json diagnostics
luccinmasirika Jul 1, 2026
b47653a
fix(cargo): strip ansi from json rendered diagnostics
luccinmasirika Jul 1, 2026
cf1caf9
fix(cargo): tee-hint dropped json diagnostics beyond the cap
luccinmasirika Jul 1, 2026
ff6cebd
refactor(cargo): extract shared json diag count merge
luccinmasirika Jul 1, 2026
9f20821
refactor(cargo): single-alloc cargo_build_success_line
luccinmasirika Jul 1, 2026
5ea03f3
feat(cargo): route --message-format=json builds through explicit arg …
luccinmasirika Jul 1, 2026
d125280
test(cargo): drop flaky tee-capture test
luccinmasirika Jul 1, 2026
6c9a60d
fix(cargo): tighten json warning-summary skip to ends_with
luccinmasirika Jul 1, 2026
315a943
fix(filter): address review findings in the TOML filter path
aeppling Jul 2, 2026
2d487cb
fix(trust): surface parse errors, skip already-trusted files, fail lo…
aeppling Jul 2, 2026
47b22e0
fix(analytics): truncate display strings on UTF-8 char boundaries
aeppling Jul 2, 2026
129104e
docs(windows): document native binary hook support
pszymkowiak Jul 2, 2026
90b4f5f
fix(cargo): label check output as "check" instead of "build"
luccinmasirika Jul 3, 2026
f9b720c
fix(cargo): surface --message-format=json test compile errors
luccinmasirika Jul 3, 2026
9f63151
Merge pull request #2791 from rtk-ai/docs/windows-native-hook
aeppling Jul 3, 2026
58abbf2
fix(cargo): surface --message-format=json clippy errors
luccinmasirika Jul 3, 2026
7fb9459
fix(cargo): surface --message-format=json install errors
luccinmasirika Jul 3, 2026
e4bfe35
fix(cargo): restore failure check before trusting reused build filter
luccinmasirika Jul 3, 2026
751bf3e
revert(cargo): drop --message-format=json install routing
luccinmasirika Jul 3, 2026
25d6a09
fix(cargo): honor last --message-format when the flag is repeated
luccinmasirika Jul 3, 2026
c02becb
refactor(cargo): thread label to handler in filter_cargo_build_labeled
luccinmasirika Jul 3, 2026
59ed885
fix(cargo): match the human path when skipping the json warning summary
luccinmasirika Jul 3, 2026
add35e0
fix(git): compact git stash show instead of forcing -p
aeppling Jul 3, 2026
c9468ee
fix(analytics): floor prefix slices to char boundary instead of full-…
aeppling Jul 3, 2026
1f74614
fix(cargo): keep "No issues found" wording for clean clippy json runs
luccinmasirika Jul 3, 2026
e200764
Merge pull request #2789 from rtk-ai/fix/analytics-utf8-char-boundary…
aeppling Jul 3, 2026
0ac5f5c
fix(git): compress git stash show summary line
aeppling Jul 3, 2026
6c3d81f
no commas
aeppling Jul 3, 2026
b619ecf
test(git): update stash summary tests for comma-free format
aeppling Jul 3, 2026
1b38eec
Merge pull request #2801 from rtk-ai/fix/git-stash-show-diffstat
aeppling Jul 3, 2026
31f9d43
Merge pull request #2748 from rtk-ai/feat/toml-filters-in-hook
aeppling Jul 3, 2026
8654edb
refactor(cargo): parse json diagnostics into typed structs, count ICE
luccinmasirika Jul 3, 2026
e0c5306
refactor(cargo): put failure summary line first, reuse join_with_over…
luccinmasirika Jul 3, 2026
a3e65e9
fix(cargo): give the json batch filter the exit code
luccinmasirika Jul 3, 2026
39a4d28
refactor(cargo): drop dead json parse from the streamed build handler
luccinmasirika Jul 3, 2026
6f67c9e
refactor(cargo): drop CargoBuildHandler new/for_check wrappers
luccinmasirika Jul 3, 2026
1143e6f
docs(cargo): explain why run_test has no json branch
luccinmasirika Jul 3, 2026
38028b7
feat(hooks): add Factory Droid integration
krimvp Jun 4, 2026
9dd8211
fix(hooks): don't force-allow Droid rewrites
krimvp Jun 4, 2026
53efe76
refactor(hooks): route Droid hook through shared decision flow
krimvp Jul 4, 2026
da36ba3
fix(hooks): install Droid hook into canonical hooks.json
krimvp Jul 4, 2026
70ed82a
fix(hooks): source Droid verdicts from Droid's own permission lists
krimvp Jul 5, 2026
0a032aa
docs(agents): add Factory Droid to supported-agents guide
krimvp Jul 5, 2026
cae9b71
fix(grep): preserve -- separator between non-adjacent match blocks
guyoron1 Jul 5, 2026
34a0f0e
fix(grep): only insert -- separator when context flags are active
guyoron1 Jul 6, 2026
6871e55
Merge pull request #2836 from guyoron1/fix/grep-context-separator
aeppling Jul 6, 2026
aee61f2
chore(docs): add core contributor
aeppling Jul 6, 2026
0b28e47
Update README.md
aeppling Jul 6, 2026
11fa991
Merge pull request #2853 from rtk-ai/docs/add-contributor
aeppling Jul 6, 2026
3d40742
fix(hooks): make Droid verdicts deny-only from explicit lists in all …
krimvp Jul 6, 2026
a4b7f74
Merge pull request #2422 from luccinmasirika/fix/cargo-json-build-fai…
aeppling Jul 7, 2026
d823aaf
Merge pull request #2732 from mvanhorn/fix/ccusage-period-key
aeppling Jul 7, 2026
bb01d6c
feat: add git checkout support
kenwoodjw Jul 6, 2026
48b883f
fix: detect absolute rtk path in Claude hook settings
TaKO8Ki Jul 8, 2026
be1844c
Merge pull request #2859 from kenwoodjw/feature/git-checkout-support
aeppling Jul 8, 2026
b0c0b20
fix: remove absolute Claude hook commands
TaKO8Ki Jul 8, 2026
8a8b356
Merge pull request #2267 from krimvp/feat/droid-integration
aeppling Jul 8, 2026
a26d8bb
chore(docs): clean contributing outdated
aeppling Jul 9, 2026
c31b605
Merge pull request #2911 from rtk-ai/chore/docs/contributing
aeppling Jul 9, 2026
5d32d07
Merge pull request #2885 from TaKO8Ki/fix/claude-hook-absolute-rtk-path
aeppling Jul 9, 2026
debac0f
fix(init): honor RTK_TELEMETRY_DISABLED in consent prompt (#1307)
ousamabenyounes Apr 17, 2026
d19f7f4
refactor(telemetry): single source of truth for env opt-out check
ousamabenyounes Jun 18, 2026
46ca979
test(telemetry): move env opt-out regression test
ousamabenyounes Jul 6, 2026
d378159
Merge branch 'develop' into feat/hook-kimi-ai-dev
williamkoller Jul 9, 2026
a521234
Merge remote-tracking branch 'origin/develop' into feat/sbt/add-sbt-s…
randomBrainstormer Jul 15, 2026
224bde3
docs: add Takayuki Maeda as core contributor
TaKO8Ki Jul 16, 2026
7624c43
Merge pull request #1478 from williamkoller/feat/hook-kimi-ai-dev
pszymkowiak Jul 18, 2026
b49568f
fix(copilot): remove unnecessary test
K0IN Jul 18, 2026
f9d8c77
Merge pull request #2472 from K0IN/fix-copilot-kubectl-get-missing
TaKO8Ki Jul 18, 2026
f568976
doc: update instructions to reflect current project state
KuSh Jul 20, 2026
788088b
tests: fix tests on non english machines
KuSh Jul 20, 2026
8c7f0dc
docs(install): pin cargo install to the master release branch
aeppling Jul 20, 2026
dfd1810
fix(uv): preserve program output and restore inner-command filtering
aeppling Jul 18, 2026
4ee1e94
docs(install): drop rationale from cargo install section
aeppling Jul 20, 2026
16a6dab
Update installation.md
aeppling Jul 20, 2026
1b64612
Merge pull request #3110 from rtk-ai/docs/pin-cargo-install-to-master
aeppling Jul 20, 2026
37a0fc6
review: use LC_ALL over LANG
KuSh Jul 20, 2026
d92996e
Merge pull request #3015 from rtk-ai/docs/add-tako8ki-core-contributor
aeppling Jul 20, 2026
625d23a
Merge pull request #3104 from KuSh/fix-tests-on-non-english-env
TaKO8Ki Jul 20, 2026
898ee09
Merge pull request #3103 from KuSh/update-doc
aeppling Jul 20, 2026
66e09cb
Merge pull request #2477 from ousamabenyounes/fix/issue-1307
KuSh Jul 20, 2026
ef38103
fix(sbt): address review feedback on output guard, routing, and dead …
randomBrainstormer Jul 21, 2026
59b604b
refactor(sbt): route all handlers through the shared runner
randomBrainstormer Jul 21, 2026
4f86523
Merge pull request #752 from randomBrainstormer/feat/sbt/add-sbt-scal…
aeppling Jul 21, 2026
ee149ee
fix: tokenize |& as a single pipe operator
TaKO8Ki Jul 21, 2026
590445e
fix: rewrite only safe final pipeline commands
TaKO8Ki Jul 21, 2026
297d664
docs: explain safe pipeline rewriting
TaKO8Ki Jul 21, 2026
50b4bf6
add pipeline_final_safe to `Scala/SBT`
TaKO8Ki Jul 21, 2026
2ccf6a5
simplify the comment for `pipeline_final_safe` field
TaKO8Ki Jul 21, 2026
c02f4d0
fix(sbt): filter testOnly/testQuick like sbt test
aeppling Jul 22, 2026
f6a4c41
fix(sbt): drop sbt 0.13 legacy task names
aeppling Jul 22, 2026
973b3b6
fix(sbt): separate tee label for selective test tasks
aeppling Jul 22, 2026
3dff2aa
fix(sbt): compute tee label before args_display move
aeppling Jul 22, 2026
cf1cc5c
test(sbt): cover rewrite of testOnly incl. quoted forms
aeppling Jul 22, 2026
194f392
Merge pull request #3140 from rtk-ai/fix/sbt-testonly-filter
aeppling Jul 22, 2026
84aa4d6
fix(hooks): emit permissionDecision allow for simple Copilot CLI rewr…
guyoron1 Jul 22, 2026
bf14bf5
fix(uv): make tee recovery hints resolve to the data they promise
aeppling Jul 22, 2026
a75bf0e
docs(uv): describe uv run passthrough accurately
aeppling Jul 22, 2026
86b34df
fix(python): stop reporting a failed tool run as clean
aeppling Jul 22, 2026
44982e0
fix(ruff): honour a user-supplied --output-format
aeppling Jul 22, 2026
a1673f7
docs: scope savings claims to bash output and document the estimator
aeppling Jul 22, 2026
b104758
docs: lower the filter gate to 20% and trim redundant explainer links
aeppling Jul 22, 2026
c1f6ede
docs: show the cost breakdown as containment, not a transformation chain
aeppling Jul 22, 2026
3f009d1
docs: revert internal and low-value files, drop the two-estimator table
aeppling Jul 22, 2026
d01b1a8
docs: drop the unsourced $3/Mtok figure from TELEMETRY.md
aeppling Jul 22, 2026
ead80a3
docs: name the metric "reduction sortie bash" throughout FEATURES.md
aeppling Jul 22, 2026
f02f5b1
docs: keep the cost breakdown diagram in one place
aeppling Jul 22, 2026
66b95cb
fix(search): stream piped grep and rg output
TaKO8Ki Jul 23, 2026
5479330
add a live passthrough test for rg -o
TaKO8Ki Jul 23, 2026
1c5a23c
fix(rewrite): keep pipeline-final wc commands raw
TaKO8Ki Jul 23, 2026
2ad7118
Merge pull request #3156 from rtk-ai/chore/docs/savings-infos
aeppling Jul 23, 2026
6c57836
fix(benchmark): use deterministic curl and wget responses
TaKO8Ki Jul 23, 2026
8dd5a34
fix(benchmark): use stable indexed MockHTTP responses
TaKO8Ki Jul 23, 2026
3304d90
Merge pull request #3170 from TaKO8Ki/fix/deterministic-network-bench…
TaKO8Ki Jul 23, 2026
ca41893
Merge pull request #3128 from TaKO8Ki/fix/pipe-rewrite-safety
TaKO8Ki Jul 23, 2026
523c803
chore: declare RtkRule default to avoid repeating unecessary attributes
KuSh Jul 23, 2026
b6b0ec2
Update src/cmds/python/uv_cmd.rs
aeppling Jul 23, 2026
20ad62b
fix conflict
aeppling Jul 23, 2026
fa82e18
fix(ruff): bound output when user sets --output-format
aeppling Jul 23, 2026
aba7643
fix(pytest): strip ANSI so colored runs don't dump raw output
aeppling Jul 23, 2026
8dd4aac
Merge pull request #3061 from rtk-ai/fix/uv-run-preserve-stdout
aeppling Jul 24, 2026
bee2178
Merge pull request #3181 from rtk-ai/rtk-rule-defaults
TaKO8Ki Jul 24, 2026
0df6929
fix(hooks): distinguish explicit ask from default in AskRewrite
guyoron1 Jul 25, 2026
23d1e89
Merge pull request #3149 from guyoron1/fix/copilot-permission-decision
KuSh Jul 25, 2026
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
201 changes: 110 additions & 91 deletions .claude/rules/cli-testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,69 +2,72 @@

Comprehensive testing rules for RTK CLI tool development.

## Snapshot Testing (🔴 Critical)
## Unit Testing (🔴 Critical)

**Priority**: 🔴 **Triggers**: All filter changes, output format modifications

Use `insta` crate for output validation. This is the **primary testing strategy** for RTK filters.
Use plain `#[cfg(test)] mod tests` block colocated in the same file as the filter,
using `assert_eq!`/`assert!` directly against expected output.

### Basic Snapshot Test
### Basic Unit Test

```rust
use insta::assert_snapshot;

#[test]
fn test_git_log_output() {
let input = include_str!("../tests/fixtures/git_log_raw.txt");
let output = filter_git_log(input);
#[cfg(test)]
mod tests {
use super::*;

// Snapshot test - will fail if output changes
assert_snapshot!(output);
#[test]
fn test_git_log_output() {
let input = "abc1234 fix: handle empty commit\ndef5678 feat: add filter\n";
let output = filter_git_log(input);
assert_eq!(output, "abc1234 fix: handle empty commit\ndef5678 feat: add filter");
}
}
```

### Workflow
### Fixture strategy

Two patterns coexist, pick based on what the filter needs:

1. **Write test**: Add `assert_snapshot!(output);` in test
2. **Run tests**: `cargo test` (creates new snapshots on first run)
3. **Review snapshots**: `cargo insta review` (interactive review)
4. **Accept changes**: `cargo insta accept` (if output is correct)
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`,
`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.
`src/cmds/jvm/mvn_cmd.rs` is the reference example (23+ `include_str!` fixtures). Fixtures
for these live in `tests/fixtures/` (e.g. `tests/fixtures/mvn_test_pass_slice_raw.txt`,
`tests/fixtures/gradlew_build_raw.txt`, `tests/fixtures/glab_mr_list_raw.json`).

### When to Use

- **Every new filter**: All filters must have snapshot test
- **Output format changes**: When modifying filter logic
- **Regression detection**: Catch unintended changes
- **Every new filter**: cover the common case and at least one edge case (empty input, error output).
- **Output format changes**: update the relevant `assert_eq!` expectations when filter logic changes.
- **Regression detection**: prefer a real fixture (`include_str!`) over an inline string once
output size/format makes hand-written strings brittle or unrepresentative.

### Example Workflow

```bash
# 1. Create fixture from real command
git log -20 > tests/fixtures/git_log_raw.txt
# 1. Capture real output for a fixture-backed test (only needed for the include_str! pattern)
mvn test > tests/fixtures/mvn_test_example_raw.txt

# 2. Write test with assert_snapshot!
cat > src/cmds/git/git.rs <<'EOF'
# 2. Write the test in the module under test
cat >> src/cmds/jvm/mvn_cmd.rs <<'EOF'
#[cfg(test)]
mod tests {
use insta::assert_snapshot;
use super::*;

#[test]
fn test_git_log_format() {
let input = include_str!("../tests/fixtures/git_log_raw.txt");
let output = filter_git_log(input);
assert_snapshot!(output);
fn test_mvn_test_example() {
let input = include_str!("../../../tests/fixtures/mvn_test_example_raw.txt");
let output = filter_mvn_test(input);
assert!(output.contains("FAILED") || output.contains("PASSED"));
}
}
EOF

# 3. Run test (creates snapshot)
cargo test test_git_log_format

# 4. Review snapshot
cargo insta review
# Press 'a' to accept, 'r' to reject

# 5. Snapshot saved in src/cmds/git/snapshots/git__tests__*.snap
# 3. Run the test
cargo test test_mvn_test_example
```

## Token Accuracy Testing (🔴 Critical)
Expand All @@ -84,7 +87,7 @@ mod tests {

#[test]
fn test_git_log_savings() {
let input = include_str!("../tests/fixtures/git_log_raw.txt");
let input = "..."; // inline string or include_str! fixture
let output = filter_git_log(input);

let input_tokens = count_tokens(input);
Expand Down Expand Up @@ -116,15 +119,13 @@ pnpm list > tests/fixtures/pnpm_list_raw.txt
# let input = include_str!("../tests/fixtures/git_log_raw.txt");
```

### Savings Targets by Filter
### Savings Target

| Filter | Expected Savings | Rationale |
|--------|------------------|-----------|
| `git log` | 80%+ | Condense commits to hash + message |
| `cargo test` | 90%+ | Show failures only |
| `gh pr view` | 87%+ | Remove ASCII art, verbose metadata |
| `pnpm list` | 70%+ | Compact dependency tree |
| `docker ps` | 60%+ | Essential fields only |
There is a single enforced floor, not a per-filter table: **≥60% savings is the release
blocker** (see CLAUDE.md's "Pre-commit Gate" / performance targets). Individual filters often
exceed this by a wide margin, but don't assert specific per-command percentages (e.g. "87% for
`gh pr view`") unless you've verified the actual number against that filter's own fixtures —
asserted thresholds vary per filter and doc tables listing invented numbers rot immediately.

**Release blocker**: If savings drop below 60% for any filter, investigate and fix before merge.

Expand Down Expand Up @@ -161,18 +162,13 @@ fn test_shell_escaping() {

### Testing Platforms

**macOS (primary)**:
**Linux/macOS (primary)**:
```bash
cargo test # Local testing
```

**Linux (via Docker)**:
```bash
docker run --rm -v $(pwd):/rtk -w /rtk rust:latest cargo test
```

**Windows (via CI)**:
Trust GitHub Actions CI/CD pipeline or test manually if Windows machine available.
Trust GitHub Actions CI/CD pipeline or test manually if a Windows machine is available.

### Shell Differences

Expand All @@ -186,7 +182,13 @@ Trust GitHub Actions CI/CD pipeline or test manually if Windows machine availabl

**Priority**: 🟡 **Triggers**: New filter, command routing changes, release preparation

Integration tests execute real commands via RTK to verify end-to-end behavior.
Integration tests live as top-level files in `tests/` (not colocated with `src/`), e.g.
`tests/grep_context_test.rs`, `tests/grep_faithful_format_test.rs`,
`tests/guard_integration_test.rs`, `tests/search_compress_test.rs`,
`tests/search_error_test.rs`, `tests/search_faithful_test.rs`. These exercise cross-cutting
behavior (search/grep compression, guard rails, faithful formatting) rather than a single
filter module, and several of them draw on `tests/fixtures/` (real captured aws/glab/gradlew/
mvn/phpstan/dotnet output) alongside their own inline cases.

### Real Command Execution

Expand Down Expand Up @@ -218,18 +220,21 @@ fn test_real_git_log() {
# 1. Install RTK locally
cargo install --path .

# 2. Run integration tests
# 2. Run all tests, including top-level tests/*.rs integration tests
cargo test --all

# 3. Run ignored (real-process) integration tests
cargo test --ignored

# 3. Run specific test
# 4. Run specific test
cargo test --ignored test_real_git_log
```

### When to Run

- **Before release**: Always run integration tests
- **After filter changes**: Verify filter works with real command
- **After hook changes**: Verify Claude Code integration works
- **Before release**: Always run integration tests.
- **After filter changes**: Verify the filter works with real command output.
- **After hook changes**: Verify Claude Code integration works.

## Performance Testing (🟡 Important)

Expand Down Expand Up @@ -303,94 +308,109 @@ rtk/
│ │ ├── git/
│ │ │ ├── git.rs # Filter implementation
│ │ │ │ └── #[cfg(test)] mod tests { ... }
│ │ │ └── snapshots/ # Insta snapshots for git module
│ │ ├── js/ # JS/TS ecosystem filters
│ │ ├── python/ # Python ecosystem filters
│ │ ├── jvm/ # gradlew, mvn — reference example for include_str! fixtures
│ │ ├── php/ # php, artisan, phpunit, phpstan, pest, paratest, ecs, pint
│ │ └── ...
│ ├── core/ # Shared infrastructure
│ ├── hooks/ # Hook system
│ └── analytics/ # Token savings analytics
├── tests/
│ ├── common/
│ │ └── mod.rs # Shared test utilities (count_tokens)
│ ├── fixtures/ # Real command output
│ │ ├── git_log_raw.txt
│ │ ├── cargo_test_raw.txt
│ │ ├── gh_pr_view_raw.txt
│ │ └── dotnet/ # Dotnet-specific fixtures
│ └── integration_test.rs # Integration tests (#[ignore])
│ ├── fixtures/ # Real captured command output
│ │ ├── mvn_test_pass_slice_raw.txt
│ │ ├── gradlew_build_raw.txt
│ │ ├── glab_mr_list_raw.json
│ │ └── ...
│ ├── grep_context_test.rs # Top-level integration tests
│ ├── grep_faithful_format_test.rs
│ ├── guard_integration_test.rs
│ ├── search_compress_test.rs
│ ├── search_error_test.rs
│ └── search_faithful_test.rs
```

**Best practices**:
- **Unit tests**: Embedded in module (`#[cfg(test)] mod tests`)
- **Fixtures**: Real command output in `tests/fixtures/`
- **Snapshots**: Auto-generated in `src/cmds/<ecosystem>/snapshots/` (by insta)
- **Shared utils**: `tests/common/mod.rs` (count_tokens, helpers)
- **Integration**: `tests/` with `#[ignore]` attribute
- **Unit tests**: Embedded in module (`#[cfg(test)] mod tests`), colocated with the filter.
- **Fixtures**: Prefer inline strings for small/quick cases; use `include_str!` from
`tests/fixtures/` (real command output) once a case gets large or format-sensitive — see
`src/cmds/jvm/mvn_cmd.rs` for the pattern.
- **`count_tokens` helper**: currently duplicated per test module — don't assume a shared
`tests/common/mod.rs` exists.
- **Integration**: top-level `tests/*.rs` files, some with `#[ignore]`-tagged real-process tests.

## Testing Checklist

When adding/modifying a filter:

### Implementation Phase
- [ ] Create fixture from real command output
- [ ] Add snapshot test with `assert_snapshot!()`
- [ ] Add token accuracy test (verify ≥60% savings)
- [ ] Write a unit test in the filter's own `#[cfg(test)] mod tests` block (inline string, or
`include_str!` fixture for larger/real output)
- [ ] Add a token accuracy test (verify ≥60% savings) using a locally-defined `count_tokens`
- [ ] Test cross-platform shell escaping (if applicable)

### Quality Checks
- [ ] Run `cargo test --all` (all tests pass)
- [ ] Run `cargo insta review` (review snapshots)
- [ ] Run `cargo test --ignored` (integration tests pass)
- [ ] Benchmark startup time with `hyperfine` (<10ms)

### Before Merge
- [ ] All tests passing (`cargo test --all`)
- [ ] Snapshots reviewed and accepted (`cargo insta accept`)
- [ ] Token savings ≥60% verified
- [ ] Cross-platform tests passed (macOS + Linux)
- [ ] Cross-platform tests passed (Linux + macOS)
- [ ] Performance benchmarks passed (<10ms startup)

### Before Release
- [ ] Integration tests passed (`cargo test --ignored`)
- [ ] Performance regression check (hyperfine comparison)
- [ ] Memory usage verified (<5MB with `time -l`)
- [ ] Cross-platform CI passed (macOS + Linux + Windows)
- [ ] Cross-platform CI passed (Linux + macOS + Windows)

## Common Testing Patterns

### Pattern: Snapshot + Token Accuracy
### Pattern: Inline Fixture + Token Accuracy

**Use case**: Testing filter output format and savings
**Use case**: Testing filter output format and savings for a small/synthetic case

```rust
#[cfg(test)]
mod tests {
use super::*;
use insta::assert_snapshot;

fn count_tokens(text: &str) -> usize {
text.split_whitespace().count()
}

#[test]
fn test_output_format() {
let input = include_str!("../tests/fixtures/cmd_raw.txt");
let input = "raw command output here";
let output = filter_cmd(input);
assert_snapshot!(output);
assert_eq!(output, "expected filtered output");
}

#[test]
fn test_token_savings() {
let input = include_str!("../tests/fixtures/cmd_raw.txt");
let input = "raw command output here";
let output = filter_cmd(input);

let savings = 100.0 - (count_tokens(&output) as f64 / count_tokens(input) as f64 * 100.0);
assert!(savings >= 60.0, "Expected 60% savings, got {:.1}%", savings);
assert!(savings >= 60.0, "Expected >=60% savings, got {:.1}%", savings);
}
}
```

### Pattern: `include_str!` Fixture (real captured output)

**Use case**: Filter output is large enough or format-sensitive enough that hand-written
strings would drift from reality — see `src/cmds/jvm/mvn_cmd.rs`.

```rust
#[test]
fn test_mvn_test_pass() {
let input = include_str!("../../../tests/fixtures/mvn_test_pass_slice_raw.txt");
let output = filter_mvn_test(input);
assert!(output.contains("BUILD SUCCESS"));
}
```

### Pattern: Edge Case Testing

**Use case**: Testing filter robustness
Expand Down Expand Up @@ -460,12 +480,11 @@ let output = filter_git_log(input);
// Synthetic data doesn't reflect real command output
```

✅ **DO** use real command fixtures
✅ **DO** assert directly on expected output
```rust
// ✅ RIGHT
let input = include_str!("../tests/fixtures/git_log_raw.txt");
let output = filter_git_log(input);
// Real output from `git log -20`
assert_eq!(output, "expected output");
```

❌ **DON'T** skip cross-platform tests
Expand Down
17 changes: 14 additions & 3 deletions .claude/rules/search-strategy.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,23 @@ src/
│ ├── filter.rs ← Language-aware code filtering engine
│ ├── toml_filter.rs ← TOML DSL filter engine
│ ├── display_helpers.rs ← Terminal formatting helpers
│ └── telemetry.rs ← Analytics ping
│ ├── telemetry.rs ← Analytics ping
│ ├── telemetry_cmd.rs ← rtk telemetry command
│ ├── args_utils.rs ← Shared CLI arg parsing helpers
│ ├── constants.rs ← Shared constants
│ ├── guard.rs ← Guard-rail checks
│ ├── runner.rs ← Command execution runner
│ └── stream.rs ← Streaming output handling
├── hooks/ ← Hook system
│ ├── init.rs ← rtk init command
│ ├── rewrite_cmd.rs ← rtk rewrite command
│ ├── hook_cmd.rs ← Gemini/Copilot hook processors
│ ├── hook_check.rs ← Hook status detection
│ ├── hook_audit_cmd.rs ← rtk hook audit command
│ ├── verify_cmd.rs ← rtk verify command
│ ├── trust.rs ← Project trust/untrust
│ ├── permissions.rs ← Hook permission handling
│ ├── constants.rs ← Shared hook constants
│ └── integrity.rs ← SHA-256 hook verification
├── analytics/ ← Token savings analytics
│ ├── gain.rs ← rtk gain command
Expand All @@ -47,11 +56,13 @@ src/
│ ├── dotnet/ ← dotnet, binlog, trx, format_report
│ ├── cloud/ ← aws, container (docker/kubectl), curl, wget, psql
│ ├── system/ ← ls, tree, read, grep, find, wc, env, json, log, deps, summary, format, local_llm
│ └── ruby/ ← rake, rspec, rubocop
│ ├── ruby/ ← rake, rspec, rubocop
│ ├── jvm/ ← gradlew, mvn
│ └── php/ ← php, artisan, phpunit, phpstan, pest, paratest, ecs, pint
├── discover/ ← Claude Code history analysis
├── learn/ ← CLI correction detection
├── parser/ ← Parser infrastructure
└── filters/ ← 60 TOML filter configs
└── filters/ ← 63 TOML filter configs
```

## Common Search Patterns
Expand Down
Loading
Loading