122 Commits

Author SHA1 Message Date
Patrick szymkowiak 079ee9a4ea fix: skip rewrite for gh --json/--jq/--template (#196)
gh commands with --json, --jq, or --template produce structured output
that rtk gh filtering would corrupt. Skip rewrite so callers get raw JSON.
2026-03-05 23:08:08 +01:00
Florian BRUNIAUX 6c13d23436 fix: RTK_DISABLED ignored, 2>&1 broken, json TOML error (#345, #346, #347)
- #345: rewrite_segment() now returns None when env_prefix contains
  RTK_DISABLED= — prevents rewriting commands the user explicitly
  opted out of
- #346: rewrite_compound() detects redirect operators (2>&1, >&2,
  &>/dev/null, &>>) before treating bare & as background operator —
  fixes corrupted compound commands containing stderr redirects
- #347: validate_json_extension() helper added to json_cmd.rs — early
  rejection with clear error message for TOML/YAML/XML/CSV/etc. files,
  with a dedicated tip for Cargo.toml (suggests rtk deps)

Tests: 16 new unit tests (4 + 6 + 6), 7 new hook integration tests.
All 672 tests pass. Quality gate clean.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 22:50:17 +01:00
patrick szymkowiak 3141fecf95 feat: warn when installed hook is outdated (#344) (#350)
- Add `# rtk-hook-version: 2` header to hook file
- New `hook_check` module: detect outdated hook on startup, warn 1/day
- Homebrew caveats: remind users to run `rtk init -g` after upgrade
2026-03-05 22:33:17 +01:00
Florian BRUNIAUX a8d6106f73 fix: curl JSON size guard (#297) + exclude_commands config (#243) (#342)
* fix: curl size guard (#297) + exclude_commands config (#243)

**#297 — curl JSON inflation guard**
- `rtk curl` no longer replaces small JSON responses with a type schema
  that is larger than the original payload
- Size guard in `filter_curl_output()`: only return schema when
  `schema.len() <= original.len()`
- Inspired by PR #324 (@rursache)

**#243 — exclude_commands config**
- Add `[hooks] exclude_commands = ["curl"]` in config.toml to prevent
  the auto-rewrite hook from rewriting specific commands
- Survives `rtk init -g` re-runs (config.toml is user-owned)
- Implemented in `rewrite_cmd::run()` via `HooksConfig` struct
- Threaded through `rewrite_command()` → `rewrite_compound()` → `rewrite_segment()`

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: add #297 and #243 to CHANGELOG + exclude_commands in README

* docs: fix version references 0.24.0 → 0.25.0 across all docs

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 20:46:40 +01:00
patrick szymkowiak baff6a2334 feat: anonymous telemetry ping (1/day, opt-out) (#334)
* feat: add anonymous telemetry ping (1/day, opt-out)

Fire-and-forget daily ping to track active installs for BPI metrics.
- SHA-256 device hash (hostname:user), no PII
- Opt-out: RTK_TELEMETRY_DISABLED=1 or config.toml [telemetry] enabled=false
- Token injected at compile time via option_env!, not in source
- 23h throttle via marker file, 2s timeout, non-blocking thread

* ci: inject RTK_TELEMETRY_TOKEN at build time in release workflow

All 3 build jobs (binary, DEB, RPM) now receive the token from
GitHub secrets so option_env!() compiles it into release binaries.

* refactor: externalize telemetry URL via option_env!(RTK_TELEMETRY_URL)

URL and token are both injected at compile time — no hardcoded
server address in source code. Builds without the env var simply
skip telemetry silently.
2026-03-05 15:34:49 +01:00
Florian BRUNIAUX f447a3d5b1 feat: rtk rewrite — single source of truth for LLM hook rewrites (#241)
* feat: add `rtk rewrite` command — single source of truth for hook rewrites

Implements `rtk rewrite <cmd>` as the canonical rewrite engine for all
LLM hook integrations (Claude Code, Gemini CLI, future tools).

- Add `rewrite_prefixes` field to `RtkRule` in discover/registry.rs
- Add public `rewrite_command()` with compound command support (&&, ||, ;, |)
- Add `rewrite_segment()`, `rewrite_compound()`, `strip_word_prefix()` helpers
- Handle already-rtk commands (exit 0, identical output)
- Handle unsupported/ignored commands (exit 1, no output)
- Add 20 unit tests covering all branches and edge cases
- Create `src/rewrite_cmd.rs` thin CLI wrapper
- Register `Commands::Rewrite` in main.rs
- Simplify `.claude/hooks/rtk-rewrite.sh` from 357 → 60 lines

Hooks no longer need duplicate mapping logic — a single
`REWRITTEN=$(rtk rewrite "$CMD") || exit 0` handles everything.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: fix version references and module count for 0.22.2

- README.md, CLAUDE.md, ARCHITECTURE.md: 0.20.1 → 0.22.2
- ARCHITECTURE.md: module count 48 → 51 (added rewrite_cmd + 2 from master)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: update hook coverage check to verify registry (not hook script)

Since PR #241, the hook delegates to `rtk rewrite` — command mappings
live in src/discover/registry.rs, not the bash hook script.

Update the "Verify hook coverage" CI step to:
- Check that the hook calls `rtk rewrite` (new architecture)
- Check that registry.rs has rewrite_prefixes for all Python/Go commands

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat: support single `&` background operator in compound rewrites

Per feedback from @xDelph: AI agents increasingly use `cmd1 & cmd2`
for parallel execution. This commit adds support alongside existing
`&&`, `||`, `;`, and `|` operators.

Changes:
- rewrite_compound: add match arm for single `&` (after `&&` check)
- rewrite_command: add `" & "` to has_compound detection
- init: show "installed/updated" vs "already up to date" so users
  know whether rtk init changed the hook on re-run

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: add Python/Go commands to rewrite registry

Add `ruff`, `pytest`, `pip`, `go`, and `golangci-lint` to both
PATTERNS and RULES in registry.rs so the CI coverage check passes
and `rtk rewrite` correctly identifies these commands.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update version reference to 0.23.0 in README

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update version reference to 0.23.0 in CLAUDE.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update version and module count to 0.23.0/52 in ARCHITECTURE.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix: address P0 review feedback on rtk-rewrite PR

P0.1 - Replace 218-line bash hook with 58-line thin delegating hook.
  All rewrite logic now lives exclusively in `rtk rewrite` (Rust registry).
  `rtk init` installs the thin hook via include_str!.

P0.2 - Fix `head -20 file` crash at runtime.
  Generic prefix replacement produced `rtk read -20 file` (invalid clap args).
  Now translates `head -N file` → `rtk read file --max-lines N` and skips
  unsupported head flags (e.g. -c) by returning exit 1.

P0.3 - Add version guard in hook for rtk < 0.23.0.
  Prints a warning to stderr instead of silently doing nothing.

Also adds missing registry entries vs old hook:
  - gh release, cargo install
  - docker run/exec/build, kubectl describe/apply
  - tree, diff

474 tests pass, 0 clippy warnings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: cargo fmt

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* refactor(discover): extract rules/patterns into rules.rs

Per aeppling review: registry.rs was too large (~1500 lines).
Extract all static data into src/discover/rules.rs:
- RtkRule struct (with pub fields)
- PATTERNS const array
- RULES const array
- IGNORED_PREFIXES and IGNORED_EXACT const arrays

registry.rs now contains only logic + tests.
rules.rs is the single place to add a new command mapping.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update version reference to 0.24.0 in README

* docs: update version references to 0.24.0 in CLAUDE.md and ARCHITECTURE.md

* fix(discover): add aws and psql to rewrite registry

rtk aws and rtk psql modules exist since PR #216 but were missing
from the registry rules — rewrite was silently skipping them.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* test(discover): exhaustive regression tests for rewrite registry

Cover all command categories added in PR #241 and missing from
the previous test suite:
- aws / psql (PR #216 modules, gap detected during non-reg run)
- Python: ruff, pytest, python -m pytest, pip, uv pip
- Go: go test/build/vet, golangci-lint
- JS/TS: vitest, pnpm vitest, prisma, prettier, pnpm list
- Compound operators: || and ; rewrites, 4-segment chains,
  mixed supported/unsupported segments, all-unsupported → None
- sudo prefix rewrite, env var prefix rewrite
- find with native flags
- Registry invariants: PATTERNS/RULES aligned, all rules valid,
  all patterns are valid regex

Before: 559 tests. After: 607 tests.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* feat(migration): document breaking change + detect outdated hook in rtk config

CHANGELOG [Unreleased]:
- Migration required: rtk init --global needed after upgrade
- Documents upgrade path and explains no immediate breakage

rtk init --show / rtk config now detects old hook (inline if-else)
vs new thin delegator (rtk rewrite) and prints actionable warning:

  ⚠️  Hook: ~/.claude/hooks/rtk-rewrite.sh (outdated — inline logic, not thin delegator)
     → Run `rtk init --global` to upgrade to the single source of truth hook

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs: update hook references for thin delegator (0.24.0)

- ARCHITECTURE.md: fix hook description (shell script → thin delegator)
- INSTALL.md: add upgrade path for old hook users (pre-0.24 breaking change)
- SECURITY.md: add registry.rs + hook files to Tier 1 critical files

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* docs(arch): fix module count 52→55, add aws/psql/rewrite to module map

Adds aws_cmd, psql_cmd (PR #216) and rewrite_cmd (this PR) to the
Complete Module Map table. Updates total from 52 to 55 to match main.rs.

Fixes CI validate-docs.sh module count mismatch.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: fix hook coverage check to search rules.rs instead of registry.rs

After the PR #241 refactor, rewrite_prefixes constants moved from
registry.rs to rules.rs. Update grep to search the whole discover/
directory so it finds them in the right file.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-05 14:20:01 +01:00
Sahil Gandhi 7ac5bc4bd3 fix(find): accept native find flags (-name, -type, etc.) (#211)
* fix(find): accept native find flags (-name, -type, etc.)

* fix: apply cargo fmt after merge

* Merge master into fix/find-positional-args

Update git -C test for new global options support, apply fmt.
2026-03-05 09:10:01 +01:00
USerik 4eb6cf4b1a fix(playwright): fix JSON parser to match real Playwright output format (#193)
Three bugs caused `rtk playwright test` to always fail with EXIT: 1:

1. **Wrong argument order**: `--reporter=json` was inserted before the
   subcommand (e.g. `playwright --reporter=json test`), but Playwright
   requires flags after the subcommand (`playwright test --reporter=json`).

2. **Float duration deserialization**: `PlaywrightStats.duration` was typed
   as `u64`, but real Playwright output emits a float (e.g. `3519.703...`),
   causing serde_json to fail with a type error.

3. **Wrong suite structure**: Suites contain `specs` (not `tests`). Each
   spec has `ok: bool` and `tests` (per-browser executions). The old struct
   used `tests: Vec<PlaywrightTest>` directly in suites, which never matched
   real output and always failed JSON parsing.

Fixes:
- Move `--reporter=json` to after the first arg (the subcommand)
- Change `duration: u64` to `duration: f64` in `PlaywrightStats`
- Rewrite `PlaywrightSuite` → `specs: Vec<PlaywrightSpec>`
- Add `PlaywrightSpec` with `ok: bool` and `tests: Vec<PlaywrightExecution>`
- Add `PlaywrightExecution` with `status` and `results: Vec<PlaywrightAttempt>`
- Update `PlaywrightAttempt` to use `errors: Vec<PlaywrightError>` (array)
- Add tests covering float duration, real suite structure, and failure details

Co-authored-by: devkeruse <devkeruse@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 12:14:11 +01:00
Itai b934466364 feat: add AWS CLI and psql modules with token-optimized output (#216)
AWS CLI (rtk aws):
- Specialized filters for sts, s3 ls, ec2, ecs, rds, cloudformation
- Generic fallback injects --output json for structured ops only
  (describe-*, list-*, get-*); mutating ops (s3 cp, deploy) pass through
- join_with_overflow() and truncate_iso_date() helpers in utils.rs

psql (rtk psql):
- Table format: strips separators/padding, outputs tab-separated
- Expanded format: compresses -[ RECORD N ]- blocks to key=val one-liners
- Passthrough for COPY results, notices, and non-table output
- 40%+ savings (table), 60%+ savings (expanded display)

Co-authored-by: itai.sagi <itai.sagi@clarityo.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-04 11:12:25 +01:00
Guillaume Deslandes 772b5012ed feat: passthrough fallback when Clap parse fails + review fixes (#200)
* feat: passthrough fallback when Clap parse fails

When RTK cannot parse a command (e.g. `rtk git -C /path status`),
instead of exiting with error code 2, it now falls back to running
the raw command directly. This keeps developer workflows unbroken.

- Replace Cli::parse() with try_parse() + run_fallback()
- Add parse_failures SQLite table for failure analytics
- Add `rtk gain --failures` / `-F` to view failure log
- Fallback preserves stdin/stdout/stderr via Stdio::inherit()
- --help and --version still work normally

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: guard RTK meta-commands from fallback to raw execution

When Clap fails to parse a meta-command like `rtk gain --badtypo`,
show the Clap error directly instead of trying to execute `gain`
as a binary from $PATH. Adds RTK_META_COMMANDS constant listing
gain, discover, learn, init, config, proxy, hook-audit, cc-economics.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: move timer start before command execution in fallback

TimedExecution::start() was called after the command finished,
so all fallback commands showed ~0ms in rtk gain --history.
Now the timer captures actual command runtime.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add cleanup_old() call to record_parse_failure()

The parse_failures table was never cleaned up because only record()
called cleanup_old(). Now parse failures also trigger 90-day retention
cleanup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: strip ANSI codes from Clap error before SQLite storage

Clap errors may contain terminal color codes. Strip them with
utils::strip_ansi() before storing in the parse_failures table
to avoid garbled output and wasted space.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: smoke test cargo test assertion and missing skip_test calls

- cargo test check now matches RTK's filtered output format ("passed")
  in addition to raw "test result:" and "FAILURES"
- Fix undefined `skip` calls to use existing `skip_test` function
  with proper (name, reason) arguments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: fmt upstream files after rebase

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: update version refs to 0.23.0 and module count to 51

Upstream v0.23.0 release bumped Cargo.toml version and added mypy_cmd
module. Update docs to match for CI validation.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-04 10:38:33 +01:00
aesoft a6ccefe8e7 Merge pull request #192 from guillaumedeslandes/fix/git-global-options
fix: support git global options (-C, -c, --git-dir, --work-tree, --no-pager, --no-optional-locks, --bare, --literal-pathspecs)
2026-03-03 15:38:13 +01:00
aesoft a413e57301 missing bracket 2026-03-03 10:49:54 +01:00
aesoft c9087a4b07 Merge branch 'master' into feat/hook-integrity 2026-03-03 09:48:54 +01:00
polamin 5cfaeccaba fix(git): propagate exit codes in push/pull/fetch/stash/worktree (#234)
run_push, run_pull, run_fetch, run_stash (pop/apply/drop/push and
default), and run_worktree all printed FAILED messages on error but
returned Ok(()) instead of propagating git's exit code.

This breaks CI/CD pipelines and shell scripts that rely on exit code
semantics (e.g. `git push || handle_error`).

Fix: call std::process::exit(output.status.code().unwrap_or(1)) in
each failure branch, matching the pattern already used in run_log,
run_diff, run_add, and run_branch.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-02 15:52:38 +01:00
Guillaume Deslandes eed018814b fix: update version refs to 0.23.0, module count to 51, fmt upstream files
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:55:40 +01:00
Guillaume Deslandes 68ca7126d4 fix: support additional git global options (--no-pager, --no-optional-locks, --bare, --literal-pathspecs)
Extends the existing git global options support to accept boolean flags
that the hook already strips for subcommand matching but Clap was rejecting.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:54:39 +01:00
Guillaume Deslandes 982084ee34 fix: support git global options (-C, -c, --git-dir, --work-tree)
git -C <path> and other global options were rejected because Clap
expected a subcommand immediately after `git`. Add these options to
the Git command variant and thread them through all git subcommand
handlers via a git_cmd() helper that prepends global args before
the subcommand.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-01 12:54:39 +01:00
heAdz0r 2b550eebd6 feat(gain): add per-project token savings with -p flag (#128)
* feat(gain): add per-project token savings with -p flag

Record project_path (cwd) in tracking database and add filtered query
methods. `rtk gain -p` shows savings scoped to the current project
directory instead of global aggregates.

- tracking.rs: Add project_path column with auto-migration, index,
  and filtered variants for all query methods (summary, daily, weekly,
  monthly, recent)
- gain.rs: Add resolve_project_scope(), shorten_path(), scope-aware
  header, pass project filter to all queries and exports
- main.rs: Add --project/-p flag to Gain command

Backward-compatible: existing rows get empty project_path, unfiltered
queries delegate to filtered(None) which returns all data.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(tracking): use GLOB instead of LIKE for project path filtering

Address reviewer feedback on PR #128:

1. Replace SQL LIKE with GLOB in all project-scoped queries to prevent
   `_` and `%` characters in path names from being interpreted as
   wildcards (e.g., `my_project` matching `myXproject`). GLOB uses `*`
   for wildcard matching which is safer for file system paths.

2. Guard the startup `UPDATE commands SET project_path = ''` migration
   with an `EXISTS` check so it only runs when NULL rows actually exist,
   avoiding a no-op UPDATE on every startup after the first migration.

3. Add `DEFAULT ''` to the ALTER TABLE migration so new installs never
   create NULL project_path values.

4. Add 3 new unit tests for project_filter_params GLOB behavior.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 20:07:01 +01:00
Rmohid e8ef341853 feat: add mypy command with grouped error output (#109)
* feat: add mypy command with grouped error output (80% token reduction)

Add rtk mypy module that parses mypy type checker output, groups errors
by file and error code, and produces compact summaries. Includes
discovery registry pattern, hook rewrites, and 11 new tests (324 total).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review - remove specs/ and clean CLAUDE.md

Remove process docs (spec, plan, tasks, research, checklists) from
specs/001-mypy-cmd/ and strip "Active Technologies" / "Recent Changes"
sections from CLAUDE.md per PR #109 review feedback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address review - deduplicate mypy handler, delegate from lint_cmd

Make lint_cmd.rs delegate to mypy_cmd::filter_mypy_output() instead of
maintaining a separate, less capable implementation. The mypy_cmd version
handles column numbers, note continuations, file-less errors, and shows
full error details. Removes 145 lines of duplicate code + 2 redundant tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Robert Mohid <rmohid@Roberts-Mini.ht.home>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 19:51:40 +01:00
stringer fan a6f65f11da fix: eliminate duplicate output when grep-ing function names from git show (#248) 2026-02-28 18:55:50 +01:00
Ricardo Liberato b405e48ca6 fix: surface build failures in go test summary (#274)
`go test -json` emits build failures as `build-output`/`build-fail`
actions with `ImportPath` (not `Package`/`Test`), followed by a
package-level `fail` with a `FailedBuild` field. The JSON parser
only counted failures when `event.test.is_some()`, so build errors
were silently dropped from the summary — showing "891 passed" when
a package failed to compile.

Now parses `build-output`, `build-fail`, and `FailedBuild` fields.
Build failures count toward the total and display with `[build failed]`
plus the compiler error lines.

Before:
  ✓ Go test: 891 passed in 74 packages

After:
  Go test: 891 passed, 1 failed in 74 packages
  ═══════════════════════════════════════

  📦 stream [build failed]
    handler_test.go:64:9: undefined: undefinedVariable

Co-authored-by: Ricardo Liberato <ricardo@liberato.pt>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 18:50:48 +01:00
hoklims 48965c85d2 fix(registry): "fi" in IGNORED_PREFIXES shadows find commands (#246)
The shell keyword "fi" was listed as a bare prefix in IGNORED_PREFIXES,
causing classify_command("find ...") to return Ignored because
"find".starts_with("fi") is true. This prevented find commands from
being rewritten by rtk rewrite and the hook system.

Move "fi" and "done" from IGNORED_PREFIXES to IGNORED_EXACT so they
only match as exact standalone keywords, not as prefixes of other
commands.

Fixes #170, #204, #236 (partially — find classification was the root
blocker for rtk rewrite).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-28 18:45:30 +01:00
Ed Saipetch 461856c8fd fix(playwright): fix JSON parser and binary resolution (#215)
Three bugs fixed:

1. JSON struct mismatch: The parser expected `suites[].tests[]` but
   Playwright's JSON reporter outputs `suites[].specs[].tests[]`.
   Added PlaywrightSpec layer, use `spec.ok` for failure detection,
   and `errors: Vec<PlaywrightError>` (array, not Option).

2. Float duration: Playwright emits `duration` as f64, not u64.
   Changed struct field type to avoid deserialization failures.

3. pyenv shim collision: `which playwright` can resolve to a pyenv
   shim (Python's playwright) which lacks a `test` subcommand.
   Bypass `which` entirely and resolve through the detected package
   manager (npx/pnpm/yarn).

Also strips user-supplied `--reporter` flags to avoid conflicts with
the forced `--reporter=json`.
2026-02-20 17:31:12 +01:00
polamin f1be88565e fix: propagate rg exit code in rtk grep for CLI parity (#227)
rtk grep previously returned exit 0 for all failure modes (no match,
invalid regex, missing file) where rg returns non-zero. This broke
shell workflows relying on exit code semantics.

Now propagates rg exit codes: 1 (no match), 2 (error) and surfaces
stderr for error cases so users see why the command failed.

Fixes #162

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-20 17:15:17 +01:00
aesoft 03ca65e9a5 Merge branch 'master' into fix/grep-accept-n-flag 2026-02-20 10:54:04 +01:00
aesoft 292225f2dd Merge pull request #203 from axelbellec/master
fix(git): support multiple -m flags in git commit
2026-02-19 15:52:14 +01:00
Jean-Baptiste Ronssin 4208015cce fix: smart markdown body filter for gh issue/pr view (#188) (#214)
Replace truncated body display (3 lines + 80 chars) with intelligent
markdown filtering that preserves all meaningful content while removing
noise (HTML comments, badges, image-only lines, horizontal rules,
excessive blank lines). Code blocks are preserved untouched.

Closes #188

Co-authored-by: Jb <jb@Jbs-MacBook-Pro.local>
2026-02-19 15:51:35 +01:00
Axel Bellec c18553a55c fix(git): support multiple -m flags in git commit
Change message from String to Vec<String> so Clap accepts repeated
-m flags, matching native git behavior for multi-paragraph commits.
Extract build_commit_command() for testability. Add 6 tests covering
Clap parsing and command construction. Closes #202.
2026-02-19 14:16:35 +01:00
Guillaume Deslandes 88dc752220 fix: git branch creation silently swallowed by list mode (#194)
`rtk git branch <name>` fell into the listing code path which injected
`-a --no-color`, turning the creation into a pattern-filtered listing
(silent no-op with exit 0). Detect positional args without list-mode
flags and route them to direct passthrough so branch creation, including
`git branch <name> <start-point>`, works correctly.

Adds regression integration tests for both cases.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:56:36 +01:00
heAdz0r 70d1b04093 fix(grep): translate BRE \| alternation and strip -r flag for rg (#206)
Two bugs when passing grep-idiom arguments to rtk grep:

1. Pattern alternation `\|` (BRE syntax) returns 0 results because rg
   uses PCRE-style `|`, not BRE `\|`. Users coming from grep/awk muscle
   memory naturally write `rtk grep "fn foo\|fn bar" src/` and get
   empty output with no error.
   Fix: translate `\|` → `|` in pattern before passing to rg.

2. `-r`/`--recursive` flag (grep idiom) silently breaks the search.
   In rg, `-r` means `--replace` and requires an argument — passing it
   alone causes rg to exit with an error. The `.or_else(|_| ...)` fallback
   only catches IO errors (command not found), so the grep fallback never
   triggers; stdout is empty and rtk reports "0 for '<pattern>'".
   Fix: filter out `-r`/`--recursive` from extra_args before building
   the rg command (rg is recursive by default, flag is a no-op).

Adds two regression tests documenting both behaviours.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-18 23:47:23 +01:00
polamin 8e826fc89f fix: propagate linter exit code in rtk lint (#207)
rtk lint was always returning exit code 0 regardless of the underlying
linter's exit status, causing false-green CI behavior when lint should fail.

Uses !output.status.success() pattern consistent with all other RTK modules,
which correctly handles both non-zero exit codes and signal termination.

Fixes #185

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 23:41:06 +01:00
heAdz0r 393fa5ba2b feat: add rtk wc command for compact word/line/byte counts (#175)
Adds a new `wc` subcommand that compresses wc output by:
- Stripping alignment padding: `      30      96     978 file.py` → `30L 96W 978B`
- Removing common path prefixes in multi-file output
- Returning bare numbers for single-flag modes (-l, -w, -c, -m)
- Showing Σ totals instead of verbose "total" lines

Includes 15 unit tests covering single/multi-file, stdin, all flag
modes, and common prefix detection.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 15:54:48 +01:00
shadowofdoom d196c2d2df fix: gh run view drops --log-failed, --log, --json flags (#159)
view_run() only passed the run ID to `gh run view`, silently
dropping all subsequent args. This made it impossible to inspect
CI failure logs when the rtk-rewrite hook was active.

Add should_passthrough_run_view() to detect flags that produce
output incompatible with the summary filter, and pass through
to gh directly when found.

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 22:59:54 +01:00
polamin 510c491238 feat(docker): add docker compose support (#110)
* feat(docker): add docker compose support with compact output

Add token-optimized output for docker compose commands:
- `docker compose ps`: compact service listing with status and ports
- `docker compose logs`: deduplicated log output via existing log engine
- `docker compose build`: summary with build time, services, and step count
- Unsupported compose subcommands pass through directly

Includes 11 tests (8 compose format tests + 3 for existing compact_ports).

Fixes #101

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: address PR #110 review — use --format for compose ps, fix bytes/chars bugs

- Switch format_compose_ps to parse structured --format tab output
  instead of fragile human-readable table (eliminates contains("Up")
  false positives and byte/char confusion)
- Fix find('[') byte offset used with chars().skip() in build parsing
- Use .next_back() instead of .last() on DoubleEndedIterator
- Consistent error handling with .context()? in run_compose_ps
- Strengthen test assertion for compose logs
- Add tests for no-ports and long-image-path cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: propagate exit codes on compose failures + remove Vec allocation

Address PR #110 review feedback from pszymkowiak:
- Check output.status.success() after .output() in run_compose_ps,
  run_compose_logs, and run_compose_build to prevent silent failures
  when Docker daemon is not running
- Replace .collect::<Vec<&str>>().len() with .count() in
  format_compose_build step counting to avoid unnecessary allocation

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: update version references to 0.20.1 after upstream rebase

Sync documentation version strings with Cargo.toml after rebasing
on upstream master. Fixes CI validate-docs check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: drop upstream workflow file from feature branch

This workflow was pulled in during rebase on upstream master.
Not part of the docker compose feature — let upstream manage it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* chore: restore validate-docs workflow from upstream

Workflow was unintentionally removed while resolving a push
permission issue. Restored to match upstream master.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-17 22:45:16 +01:00
Florian BRUNIAUX 70c37867e7 feat: add hook audit mode for verifiable rewrite metrics (#151)
Add opt-in audit logging to the RTK rewrite hook and a new `rtk hook-audit`
command to analyze the collected metrics. This provides verifiable data on
hook reliability (rewrite rate, skip reasons, top commands) instead of
relying on anecdotal claims.

- Add `_rtk_audit_log()` function to rtk-rewrite.sh (opt-in via RTK_HOOK_AUDIT=1)
- Log each invocation: timestamp | action | original_cmd | rewritten_cmd
- Actions: rewrite, skip:already_rtk, skip:heredoc, skip:no_match, skip:no_deps, skip:empty
- New `src/hook_audit_cmd.rs` module with log parser and stats display
- New `rtk hook-audit --since N` subcommand (default: last 7 days)
- 8 Rust unit tests + 7 hook bash tests for audit mode
- Make HOOK var overridable in test suite for repo-local testing

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 22:05:28 +01:00
Florian BRUNIAUX a08a62b4e3 feat: tee raw output to file for LLM re-read without re-run (#134)
* feat: tee raw output to file for LLM re-read without re-run (#86)

When RTK filters command output, LLM agents lose failure details
(stack traces, assertions) and re-run the same command 2-3x. The tee
feature saves raw output to ~/.local/share/rtk/tee/ on failure and
prints a one-line hint so the agent can read the file instead.

- Add src/tee.rs: core module with tee_raw(), tee_and_hint(), rotation
- Add TeeConfig to config.rs: enabled, mode, max_files, max_file_size
- Integrate in 7 modules: cargo, runner, vitest, pytest, lint, tsc, go
- Default: failures only, skip <500 chars, 20 file rotation, 1MB cap
- Env overrides: RTK_TEE=0 (disable), RTK_TEE_DIR (custom directory)
- 14 unit tests, 352 total tests passing, zero regressions

Closes #86

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: add tee.rs to ARCHITECTURE.md module count (47 modules)

CI validate-docs requires main.rs module count == ARCHITECTURE.md count.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-16 20:03:03 +01:00
heAdz0r 7d69299c9f fix(grep): accept -n flag for grep/rg compatibility
`rtk grep -n "pattern" path` failed with "unexpected argument '-n'"
because clap didn't recognize -n before the positional arguments.

Users naturally place -n before the pattern (muscle memory from
grep/rg). The flag is a no-op since grep_cmd::run() already passes
-n to ripgrep unconditionally, but clap must accept it.

Adds -n/--line-numbers as an explicit bool field to the Grep command
and ignores it in the match arm. Test added in grep_cmd::tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 23:22:19 +03:00
heAdz0r 606b86ed43 feat(gain): colored dashboard with efficiency meter and impact bars (#129)
* feat(gain): add colored dashboard with efficiency meter and impact bars

Upgrade `rtk gain` output with rich terminal visualization:
- Colored percentage cells (green ≥70%, yellow ≥40%, red <40%)
- Efficiency meter: ASCII progress bar with savings percentage
- Impact bars: proportional block charts per command
- Styled section headers (bold green)
- Command names in bright cyan
- History tier indicators (▲/■/•)
- Dynamic column widths for clean table alignment
- KPI-style key-value layout for summary metrics

All visual features are TTY-aware via std::io::IsTerminal — output
degrades gracefully to plain text when piped or redirected.

No new dependencies (uses existing `colored` crate).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add dashboard visualization screenshot

Shows colored terminal output of `rtk gain -p` with:
- Project-scoped token savings header
- Efficiency meter with color gradient
- Per-command table with colored Avg% and Impact bars

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 15:50:24 +01:00
heAdz0r 356c0d61a3 refactor(init): add upsert_rtk_block for idempotent CLAUDE.md management (#123)
Replace naive append-or-skip logic in run_claude_md_mode with
upsert_rtk_block() that handles all 4 cases:
- Added: no existing block → append
- Updated: stale block → replace in-place
- Unchanged: current block → no-op
- Malformed: opening marker without closing → warn safely

Includes 4 unit tests covering each case.
2026-02-15 15:15:08 +01:00
playb0t 1d7d9d065e fix(integrity): address security review findings
Addresses all findings from FlorianBruniaux's review of PR #119:

C-02 (CRITICAL): Remove RTK_SKIP_INTEGRITY env var bypass
  - Any process in the environment could disable verification
  - If attacker can modify hook, they can also set env var
  - Recovery path: `rtk init -g --auto-patch` to re-baseline

I-02 (IMPORTANT): Invert is_operational_command to whitelist
  - Explicitly enumerate commands requiring integrity check
  - New commands fail open (no check) rather than creating
    false confidence about protection coverage

I-03 (IMPORTANT): Strict hash file parsing
  - Validate exact sha256sum format: "<64 hex>  <filename>"
  - Reject single-space separator, missing filename, garbage
  - Prevents silent acceptance of corrupted hash files

N-01 (NIT): Safe hash truncation in error output
  - Use .get(..16).unwrap_or() instead of &[..16] slice
  - Prevents potential panic on malformed stored hash

N-02 (NIT): Remove bypass hint from error message
  - "To override: RTK_SKIP_INTEGRITY=1" leaked mechanism
  - Resolved automatically by C-02 (env var removed entirely)

Tests: 14 → 16 (+hash_only_no_filename, +wrong_separator)
2026-02-15 20:38:46 +07:00
igor 68fd570f2b feat(cargo): add cargo nextest support with failures-only output (#107)
Add token-optimized filtering for `cargo nextest` output: strip
compilation noise, PASS lines, and post-summary recaps; show only
failure details and a compact summary. Extract flush_failure_block
helper to DRY up failure block collection (was repeated 4 times).

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-15 14:14:28 +01:00
Florian BRUNIAUX 4cae6b6c9a feat(python): add lint dispatcher + universal format command (#100)
* feat(cargo): aggregate test output into single line (#83)

Problem: `cargo test` shows 24+ summary lines even when all pass.
An LLM only needs to know IF something failed, not 24x "ok".

Before (24 lines):
```
✓ test result: ok. 2 passed; 0 failed; ...
✓ test result: ok. 0 passed; 0 failed; ...
... (x24)
```

After (1 line):
```
✓ cargo test: 137 passed (24 suites, 1.45s)
```

Changes:
- Add AggregatedTestResult struct with regex parsing
- Merge multiple test summaries when all pass
- Format: "N passed, M ignored, P filtered out (X suites, Ys)"
- Fallback to original behavior if parsing fails
- Failures still show full details (no aggregation)

Tests: 6 new + 1 modified, covering all cases:
- Multi-suite aggregation
- Single suite (singular "suite")
- Zero tests
- With ignored/filtered out
- Failures → no aggregation (detail preserved)
- Regex fallback

Closes #83

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: add Python and Go support (#88)

* feat(cargo): aggregate test output into single line (#83)

Problem: `cargo test` shows 24+ summary lines even when all pass.
An LLM only needs to know IF something failed, not 24x "ok".

Before (24 lines):
```
✓ test result: ok. 2 passed; 0 failed; ...
✓ test result: ok. 0 passed; 0 failed; ...
... (x24)
```

After (1 line):
```
✓ cargo test: 137 passed (24 suites, 1.45s)
```

Changes:
- Add AggregatedTestResult struct with regex parsing
- Merge multiple test summaries when all pass
- Format: "N passed, M ignored, P filtered out (X suites, Ys)"
- Fallback to original behavior if parsing fails
- Failures still show full details (no aggregation)

Tests: 6 new + 1 modified, covering all cases:
- Multi-suite aggregation
- Single suite (singular "suite")
- Zero tests
- With ignored/filtered out
- Failures → no aggregation (detail preserved)
- Regex fallback

Closes #83

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: add Python and Go language support

Implements comprehensive support for Python and Go development tooling
with 70-90% token reduction across all commands.

Python commands (3):
- rtk ruff: Linter/formatter with JSON (check) and text (format) parsing (80%+)
- rtk pytest: Test runner with state machine text parser (90%+)
- rtk pip: Package manager with auto-detect uv (70-85%)

Go commands (4):
- rtk go test: NDJSON streaming parser for interleaved test events (90%+)
- rtk go build: Text filter showing errors only (80%)
- rtk go vet: Text filter for issues (75%)
- rtk golangci-lint: JSON parser grouped by rule (85%)

Architecture:
- Standalone Python commands (mirror lint/prettier pattern)
- Go sub-enum (mirror git/cargo pattern)
- 5 new modules: ruff_cmd, pytest_cmd, pip_cmd, go_cmd, golangci_cmd
- Hook integration in rtk-rewrite.sh for transparent rewrites
- Comprehensive tests (47 new tests, all passing)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(benchmark): add Python and Go commands

Add benchmark sections for Python (ruff, pytest, pip) and Go (go test/build/vet, golangci-lint) to validate >80% token savings in CI pipeline.

Sections conditionally execute based on project markers (pyproject.toml, go.mod) and tool availability.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: make install-local.sh self-contained (#89)

- Build from source automatically instead of requiring a pre-built binary
- Default install dir to ~/.cargo/bin
- Skip rebuild when binary is up to date
- Warn if install dir is not in PATH

* chore(master): release 0.15.0 (#90)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* fix(vitest): robust JSON extraction for pnpm/dotenv prefixes (#92)

* fix(vitest): robust JSON extraction for pnpm/dotenv prefixes

Problem: RTK's vitest parser forces --reporter=json but pnpm/dotenv prepend
non-JSON text to stdout (banners, env messages), causing 100% Tier 1 failure
and useless 500-char passthrough.

Solution:
- Add extract_json_object() to parser/mod.rs (shared utility)
- Algorithm: find "numTotalTests" or first standalone {, brace-balance forward
- VitestParser now tries direct parse → extract+parse → regex → passthrough
- Replace hardcoded Command::new("pnpm") with package_manager_exec("vitest")
- Delete orphan doc comment on line 203

Impact:
- Before: 100% Tier 3 passthrough with pnpm workflows
- After: Tier 1 success with prefixes, maintains 99.5% token savings

Tests:
- 6 tests for extract_json_object (clean, pnpm, dotenv, nested, no-json, strings)
- 3 tests for VitestParser with prefixes
- All 277 tests pass

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(benchmark): add vitest, pnpm, and gh commands

Add benchmarks for recently implemented commands:
- vitest run (PR #92 - JSON extraction fix)
- pnpm list/outdated (PR #6)
- gh pr list/run list (existing gh support)

These commands are now tested in CI to ensure token savings are maintained.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: improve CI reliability and hook coverage (#95)

* feat(cargo): aggregate test output into single line (#83)

Problem: `cargo test` shows 24+ summary lines even when all pass.
An LLM only needs to know IF something failed, not 24x "ok".

Before (24 lines):
```
✓ test result: ok. 2 passed; 0 failed; ...
✓ test result: ok. 0 passed; 0 failed; ...
... (x24)
```

After (1 line):
```
✓ cargo test: 137 passed (24 suites, 1.45s)
```

Changes:
- Add AggregatedTestResult struct with regex parsing
- Merge multiple test summaries when all pass
- Format: "N passed, M ignored, P filtered out (X suites, Ys)"
- Fallback to original behavior if parsing fails
- Failures still show full details (no aggregation)

Tests: 6 new + 1 modified, covering all cases:
- Multi-suite aggregation
- Single suite (singular "suite")
- Zero tests
- With ignored/filtered out
- Failures → no aggregation (detail preserved)
- Regex fallback

Closes #83

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix(ci): prevent Python/Go benchmark sections from being silently skipped

**Problem:**
Python and Go benchmark sections were silently skipped in CI because
the RTK repository doesn't contain pyproject.toml or go.mod files.
The sections only ran when these project files existed.

**Solution:**
1. Create temporary fixtures with minimal project structure:
   - Python: pyproject.toml + sample.py + test_sample.py
   - Go: go.mod + main.go + main_test.go
2. Resolve RTK to absolute path to work after cd into temp dirs
3. Install required tools in CI workflow:
   - Python: ruff, pytest
   - Go: stable version + golangci-lint

**Impact:**
- Python/Go sections now appear in CI benchmark output
- Self-contained fixtures ensure consistent benchmarking
- No dependency on RTK project structure

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(hooks): add missing RTK command rewrites

Add 8 missing command rewrites to rtk-rewrite.sh and rtk-suggest.sh:
- cargo check/install/fmt
- tree, find, diff
- head → rtk read (with --max-lines transformation)
- wget

Fixes BSD sed compatibility for head transformation by using literal
spaces instead of \s+ (which doesn't work on macOS).

Impact: ~18.2K tokens saved on previously missed commands discovered
by `rtk discover`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(master): release 0.15.1 (#96)

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>

* feat(python): add lint dispatcher + universal format command

Phase 1: Enhanced rtk lint
- Add pylint JSON2 parser (80-85% token savings)
- Add mypy text parser (75-80% token savings)
- Smart dispatcher: Python tools (pip) vs JS tools (npm)
- Reuse ruff_cmd JSON parser for rtk lint ruff

Phase 2: New rtk format command
- Universal formatter: black/ruff/prettier
- Auto-detect from pyproject.toml/package.json
- Implement black output parser (70-85% savings)
- Reuse existing prettier/ruff formatters

Phase 3: Hook integration
- Auto-rewrite: pylint → rtk lint pylint
- Auto-rewrite: mypy → rtk lint mypy
- Auto-rewrite: black --check → rtk format black

Files changed:
- src/lint_cmd.rs: +454 lines (pylint/mypy parsers, dispatcher)
- src/format_cmd.rs: +386 lines (NEW - universal formatter)
- src/ruff_cmd.rs: Export filter functions as pub
- src/prettier_cmd.rs: Export filter_prettier_output as pub
- src/main.rs: Add Commands::Format + routing
- hooks/rtk-rewrite.sh: Add Python tool rewrite rules

Testing: 10 new unit tests, all 313 tests passing
Impact: 80-90% token savings on Python workflows

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Michael Coen <mhcoen@gmail.com>
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2026-02-14 14:22:48 +01:00
playb0t 95677e9786 feat(security): add SHA-256 hook integrity verification
Add runtime integrity verification for rtk-rewrite.sh to detect
unauthorized modifications to the PreToolUse hook.

The hook auto-approves all rewritten commands via permissionDecision:
"allow", bypassing Claude Code's permission prompts. Any modification
by a local process (malicious dependency, compromised npm postinstall,
etc.) becomes a command injection vector.

Changes:
- New module: src/integrity.rs (SHA-256 hash, verify, runtime gate)
- rtk init: stores hash after hook installation (idempotent)
- rtk verify: new subcommand for manual integrity check
- rtk init --show: displays integrity status
- rtk uninstall: cleans up hash file
- Operational commands check integrity at startup (fail closed)
- Emergency bypass: RTK_SKIP_INTEGRITY=1

Ref: SA-2025-RTK-001 (Finding F-01)
Ref: PromptArmor hook hijacking (Oct 2025)
Ref: CVE-2025-54794, CVE-2025-54795
2026-02-14 18:37:40 +07:00
Christian C. Berclaz 04e6bb032c fix(git): fix for issue #82
Returns "Not a git repository" when git status is executed in a
non-repo folder
2026-02-14 10:27:51 +01:00
polamin 155e26423d fix: prevent UTF-8 panics on multi-byte characters (#93)
* fix: prevent UTF-8 panics on multi-byte characters (Thai, emoji, CJK)

Replace all byte-indexed string slicing (`&s[..n]`) with char-aware
operations across 10 locations in 7 files. Rust strings are UTF-8, so
byte slicing can land mid-character on Thai (3 bytes), emoji (4 bytes),
or CJK text and panic at runtime.

Fixes applied:
- git.rs: filter_log_output, format_status_output
- log_cmd.rs: error/warning message truncation (2 locations)
- env_cmd.rs: long value display, mask_value
- parser/mod.rs: truncate_output
- grep_cmd.rs: clean_line (pattern context + fallback truncation)
- wget_cmd.rs: compact_url, truncate_line, parse_error

Also adds safe_char_boundary() utility and 20 regression tests covering
Thai, emoji, and CJK input across all affected modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: collect chars once in compact_url to avoid redundant iteration

Address review feedback: collect chars into Vec once instead of
calling .chars() multiple times. Also fixes byte-vs-char length check.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: remove unused safe_char_boundary function and tests

Address review: function was defined but never called anywhere.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: convert remaining is_char_boundary to full .chars() approach

Address review: use consistent .chars() everywhere instead of
mixing byte-based is_char_boundary with char-based operations.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-13 18:03:32 +01:00
Florian BRUNIAUX e5adba8b21 fix(vitest): robust JSON extraction for pnpm/dotenv prefixes (#92)
* fix(vitest): robust JSON extraction for pnpm/dotenv prefixes

Problem: RTK's vitest parser forces --reporter=json but pnpm/dotenv prepend
non-JSON text to stdout (banners, env messages), causing 100% Tier 1 failure
and useless 500-char passthrough.

Solution:
- Add extract_json_object() to parser/mod.rs (shared utility)
- Algorithm: find "numTotalTests" or first standalone {, brace-balance forward
- VitestParser now tries direct parse → extract+parse → regex → passthrough
- Replace hardcoded Command::new("pnpm") with package_manager_exec("vitest")
- Delete orphan doc comment on line 203

Impact:
- Before: 100% Tier 3 passthrough with pnpm workflows
- After: Tier 1 success with prefixes, maintains 99.5% token savings

Tests:
- 6 tests for extract_json_object (clean, pnpm, dotenv, nested, no-json, strings)
- 3 tests for VitestParser with prefixes
- All 277 tests pass

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* chore(benchmark): add vitest, pnpm, and gh commands

Add benchmarks for recently implemented commands:
- vitest run (PR #92 - JSON extraction fix)
- pnpm list/outdated (PR #6)
- gh pr list/run list (existing gh support)

These commands are now tested in CI to ensure token savings are maintained.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 23:11:24 +01:00
Florian BRUNIAUX a005bb15c0 feat: add Python and Go support (#88)
* feat(cargo): aggregate test output into single line (#83)

Problem: `cargo test` shows 24+ summary lines even when all pass.
An LLM only needs to know IF something failed, not 24x "ok".

Before (24 lines):
```
✓ test result: ok. 2 passed; 0 failed; ...
✓ test result: ok. 0 passed; 0 failed; ...
... (x24)
```

After (1 line):
```
✓ cargo test: 137 passed (24 suites, 1.45s)
```

Changes:
- Add AggregatedTestResult struct with regex parsing
- Merge multiple test summaries when all pass
- Format: "N passed, M ignored, P filtered out (X suites, Ys)"
- Fallback to original behavior if parsing fails
- Failures still show full details (no aggregation)

Tests: 6 new + 1 modified, covering all cases:
- Multi-suite aggregation
- Single suite (singular "suite")
- Zero tests
- With ignored/filtered out
- Failures → no aggregation (detail preserved)
- Regex fallback

Closes #83

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: add Python and Go language support

Implements comprehensive support for Python and Go development tooling
with 70-90% token reduction across all commands.

Python commands (3):
- rtk ruff: Linter/formatter with JSON (check) and text (format) parsing (80%+)
- rtk pytest: Test runner with state machine text parser (90%+)
- rtk pip: Package manager with auto-detect uv (70-85%)

Go commands (4):
- rtk go test: NDJSON streaming parser for interleaved test events (90%+)
- rtk go build: Text filter showing errors only (80%)
- rtk go vet: Text filter for issues (75%)
- rtk golangci-lint: JSON parser grouped by rule (85%)

Architecture:
- Standalone Python commands (mirror lint/prettier pattern)
- Go sub-enum (mirror git/cargo pattern)
- 5 new modules: ruff_cmd, pytest_cmd, pip_cmd, go_cmd, golangci_cmd
- Hook integration in rtk-rewrite.sh for transparent rewrites
- Comprehensive tests (47 new tests, all passing)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat(benchmark): add Python and Go commands

Add benchmark sections for Python (ruff, pytest, pip) and Go (go test/build/vet, golangci-lint) to validate >80% token savings in CI pipeline.

Sections conditionally execute based on project markers (pyproject.toml, go.mod) and tool availability.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 21:51:10 +01:00
Florian BRUNIAUX 06b10491f9 feat(cargo): aggregate test output into single line (#83) (#85)
Problem: `cargo test` shows 24+ summary lines even when all pass.
An LLM only needs to know IF something failed, not 24x "ok".

Before (24 lines):
```
✓ test result: ok. 2 passed; 0 failed; ...
✓ test result: ok. 0 passed; 0 failed; ...
... (x24)
```

After (1 line):
```
✓ cargo test: 137 passed (24 suites, 1.45s)
```

Changes:
- Add AggregatedTestResult struct with regex parsing
- Merge multiple test summaries when all pass
- Format: "N passed, M ignored, P filtered out (X suites, Ys)"
- Fallback to original behavior if parsing fails
- Failures still show full details (no aggregation)

Tests: 6 new + 1 modified, covering all cases:
- Multi-suite aggregation
- Single suite (singular "suite")
- Zero tests
- With ignored/filtered out
- Failures → no aggregation (detail preserved)
- Regex fallback

Closes #83

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-12 20:36:13 +01:00
Joan Bordeau 93364b5457 feat(sqlite): add custom sqlite db location 2026-02-10 21:42:46 +01:00
Florian BRUNIAUX 645a773a65 feat(cargo): add cargo install filtering with 80-90% token reduction
Strip dep compilation, downloading, locking noise from cargo install output.
Keep only installed/replaced/error lines with compact summary format.

Closes #69

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 11:36:14 +01:00