fix(vitest): robust JSON extraction for pnpm/dotenv prefixes#92
Conversation
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>
Add benchmarks for recently implemented commands: - vitest run (PR rtk-ai#92 - JSON extraction fix) - pnpm list/outdated (PR rtk-ai#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>
There was a problem hiding this comment.
Pull request overview
Improves RTK’s vitest integration by making JSON parsing resilient to non-JSON stdout prefixes introduced by pnpm/dotenv workflows, and aligns vitest execution with the project’s package-manager abstraction.
Changes:
- Added a shared
extract_json_object()helper to slice a JSON object out of prefixed output before parsing. - Updated
VitestParser::parse()to retry JSON parsing using extracted JSON when direct parsing fails. - Switched vitest execution from hardcoded
pnpmtoutils::package_manager_exec("vitest"), and added regression tests.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
src/vitest_cmd.rs |
Uses JSON-extraction fallback during Tier 1 parsing; runs vitest via package_manager_exec; adds parser tests for pnpm/dotenv prefixes. |
src/parser/mod.rs |
Introduces extract_json_object() plus unit tests for prefixed/nested JSON extraction scenarios. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let chars: Vec<char> = input[start_pos..].chars().collect(); | ||
|
|
||
| for (i, &ch) in chars.iter().enumerate() { | ||
| if escape_next { | ||
| escape_next = false; |
There was a problem hiding this comment.
extract_json_object builds a Vec<char> from input[start_pos..] and then later computes slice indices using the loop index. This mixes char positions with byte offsets, which can panic on non-ASCII content (e.g., Unicode in test names) and is also not allocation-free. Consider iterating with char_indices() (or bytes with explicit quote/escape handling) so end_pos is computed as a byte offset without allocating.
| let start_pos = if let Some(pos) = input.find("\"numTotalTests\"") { | ||
| // Walk backward to find opening brace of this object | ||
| input[..pos].rfind('{').unwrap_or(0) | ||
| } else { |
There was a problem hiding this comment.
When locating the JSON start via "numTotalTests", rfind('{').unwrap_or(0) will fall back to byte offset 0 if no { is found before the marker. That would cause the extraction to include any non-JSON prefix and make the subsequent brace-balance return an invalid slice. Prefer returning None (or falling back to the line-based { search) when rfind('{') returns None.
| // Fallback: find first `{` on its own line or after whitespace | ||
| let mut found_start = None; | ||
| for (idx, line) in input.lines().enumerate() { | ||
| let trimmed = line.trim(); | ||
| if trimmed.starts_with('{') { | ||
| // Calculate byte offset | ||
| found_start = Some( | ||
| input[..] | ||
| .lines() | ||
| .take(idx) | ||
| .map(|l| l.len() + 1) | ||
| .sum::<usize>(), | ||
| ); | ||
| break; | ||
| } |
There was a problem hiding this comment.
The fallback byte-offset calculation sums l.len() + 1 for prior .lines(), which assumes a single-byte newline. For \r\n inputs, the computed start_pos will be wrong and can lead to incorrect slicing/panics. Consider computing the offset using match_indices('\n'), char_indices(), or maintaining a running byte offset while iterating through the original string.
| // Fallback: find first `{` on its own line or after whitespace | |
| let mut found_start = None; | |
| for (idx, line) in input.lines().enumerate() { | |
| let trimmed = line.trim(); | |
| if trimmed.starts_with('{') { | |
| // Calculate byte offset | |
| found_start = Some( | |
| input[..] | |
| .lines() | |
| .take(idx) | |
| .map(|l| l.len() + 1) | |
| .sum::<usize>(), | |
| ); | |
| break; | |
| } | |
| // Fallback: find first `{` on its own line or after whitespace. | |
| // | |
| // We avoid reconstructing a byte offset from `.lines()` (which drops | |
| // the original line endings) and instead scan the original string | |
| // with `char_indices`, tracking line starts explicitly. This works | |
| // correctly for both `\n` and `\r\n` inputs. | |
| let mut found_start: Option<usize> = None; | |
| let mut seen_non_ws_on_line = false; | |
| for (idx, ch) in input.char_indices() { | |
| if ch == '\n' || ch == '\r' { | |
| // Start a new logical line after this character | |
| seen_non_ws_on_line = false; | |
| continue; | |
| } | |
| if ch.is_whitespace() { | |
| // Leading whitespace is allowed before the opening brace | |
| continue; | |
| } | |
| if ch == '{' && !seen_non_ws_on_line { | |
| found_start = Some(idx); | |
| break; | |
| } | |
| // Any other non-whitespace character before `{` means this line | |
| // does not start with a JSON object. | |
| seen_non_ws_on_line = true; |
* 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 rtk-ai#92 - JSON extraction fix)
- pnpm list/outdated (PR rtk-ai#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>
Resolved conflicts: - Version bumped to 0.15.4 (Cargo.toml, Cargo.lock, .release-please-manifest.json) - CHANGELOG.md: Added upstream releases (0.15.4, 0.15.3, 0.15.2) - Hooks: Adopted POSIX character classes ([[:space:]]) from upstream - src/parser/mod.rs: Added multibyte UTF-8 tests from upstream - src/ruff_cmd.rs: Kept functions public for lint/format dispatcher feature Upstream changes integrated: - rtk-ai#120: git status fix for non-repo folders - rtk-ai#93: UTF-8 panic prevention on multibyte chars - rtk-ai#98: POSIX grep compatibility in hooks - rtk-ai#95, rtk-ai#92: CI reliability and hook coverage improvements Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* 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>
* 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 rtk-ai#92 - JSON extraction fix)
- pnpm list/outdated (PR rtk-ai#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(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 rtk-ai#92 - JSON extraction fix)
- pnpm list/outdated (PR rtk-ai#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>
) * feat(cargo): aggregate test output into single line (rtk-ai#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 rtk-ai#83 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * feat: add Python and Go support (rtk-ai#88) * feat(cargo): aggregate test output into single line (rtk-ai#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 rtk-ai#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 (rtk-ai#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 (rtk-ai#90) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> * fix(vitest): robust JSON extraction for pnpm/dotenv prefixes (rtk-ai#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 rtk-ai#92 - JSON extraction fix) - pnpm list/outdated (PR rtk-ai#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 (rtk-ai#95) * feat(cargo): aggregate test output into single line (rtk-ai#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 rtk-ai#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 (rtk-ai#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>
Summary
Fixes 100% Tier 1 failure when vitest runs through pnpm/dotenv workflows by implementing robust JSON extraction that strips non-JSON prefixes.
Problem
RTK's vitest parser forces
--reporter=jsonbut pnpm and dotenv prepend non-JSON text to stdout:"Scope: all 6 workspace projects\n WARN deprecated...""[dotenv] Loading environment variables from .env"This caused:
--reporter=jsonsuppresses100% failure rate in real pnpm workflows. Not an edge case.
Solution
1. Add
extract_json_object()tosrc/parser/mod.rs"numTotalTests"(vitest-specific) or first{on its own line, then brace-balance forward&strslice into input2. Update
VitestParser::parse()insrc/vitest_cmd.rsextract_json_object()→ parse extracted3. Replace hardcoded
pnpmwithpackage_manager_execCommand::new("pnpm")→utils::package_manager_exec("vitest")4. Fix orphan doc comment
/// Strip ANSI escape sequencescommentTesting
New Tests (9 total)
src/parser/mod.rs- 6 tests forextract_json_object():src/vitest_cmd.rs- 3 tests forVitestParser:Test Results
Manual Verification
Tested with real pnpm output:
✅ JSON extracted at position 180, parsing successful
Impact
Before:
After:
Verification
🤖 Generated with Claude Code