feat: add Python and Go support#88
Conversation
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>
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>
There was a problem hiding this comment.
Pull request overview
This pull request adds comprehensive Python and Go language support to the RTK token reduction toolkit, introducing 7 new commands (3 for Python, 4 for Go) with 70-90% token reduction across all operations. The implementation follows established patterns from the codebase and includes extensive testing with 47 new test cases.
Changes:
- Python commands:
rtk ruff,rtk pytest,rtk pipfor linter/formatter, test runner, and package management - Go commands:
rtk go test/build/vetandrtk golangci-lintfor testing, building, and linting - Enhanced cargo test aggregation for multi-suite test runs with compact single-line summaries
- Hook integration in rtk-rewrite.sh for transparent command rewrites
- Comprehensive test coverage and documentation updates
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/ruff_cmd.rs | Ruff linter/formatter integration with JSON parsing for check mode and text filtering for format mode |
| src/pytest_cmd.rs | Pytest test runner with state machine parser for test output filtering |
| src/pip_cmd.rs | pip/uv package manager with JSON output parsing and auto-detection of uv |
| src/go_cmd.rs | Go test/build/vet commands with NDJSON streaming parser and text filters |
| src/golangci_cmd.rs | golangci-lint integration with JSON parsing and rule grouping |
| src/main.rs | Command enum extensions for new Python/Go commands and registration in main dispatcher |
| src/cargo_cmd.rs | Enhanced test result aggregation using regex to provide compact multi-suite summaries |
| hooks/rtk-rewrite.sh | Hook patterns for transparent pytest, ruff, pip, go, and golangci-lint command rewrites |
| scripts/test-all.sh | Conditional smoke tests for Python and Go tools with appropriate skipping when not installed |
| CLAUDE.md | Documentation updates describing new Python/Go support and architectural patterns |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| fi | ||
|
|
||
| if command -v pip &>/dev/null; then | ||
| assert_help "rtk pip" rtk pip --help |
There was a problem hiding this comment.
The pip help test will fail because the implementation requires a subcommand. Calling rtk pip --help will hit the match arm at line 35-40 in pip_cmd.rs and bail with an error about unsupported subcommand. Consider changing this test to rtk pip list --help or update the pip_cmd.rs implementation to handle the case where the first argument is --help without a subcommand.
| assert_help "rtk pip" rtk pip --help | |
| assert_help "rtk pip list" rtk pip list --help |
| // golangci-lint returns exit code 1 when issues found (expected behavior) | ||
| // Don't exit with error code in that case | ||
| Ok(()) |
There was a problem hiding this comment.
The golangci-lint command doesn't preserve exit codes, which breaks CI/CD integration. While the comment on line 82-83 mentions that golangci-lint returns exit code 1 when issues are found, CI/CD systems often rely on these exit codes to determine build success or failure. This is inconsistent with other commands in the codebase (e.g., ruff_cmd.rs:107-108, go_cmd.rs:78-79) which preserve exit codes. Consider preserving the exit code to maintain CI/CD compatibility, especially since the PR description emphasizes "Exit code preservation" as a key feature.
| // Detect subcommand: check, format, or version | ||
| let is_check = args.is_empty() | ||
| || args[0] == "check" | ||
| || (!args[0].starts_with('-') && args[0] != "format" && args[0] != "version"); |
There was a problem hiding this comment.
The detection logic for the is_check case has a subtle issue. When args[0] is a non-flag argument that's not "format" or "version" (line 37), it's assumed to be a path for the check command. However, if a user passes an unsupported ruff subcommand (like "clean"), it will be incorrectly treated as a check command rather than being passed through to ruff to show an appropriate error. Consider checking if the first arg is a known ruff subcommand and falling through to the passthrough case for unknown subcommands, or at minimum document this limitation.
| // Detect subcommand: check, format, or version | |
| let is_check = args.is_empty() | |
| || args[0] == "check" | |
| || (!args[0].starts_with('-') && args[0] != "format" && args[0] != "version"); | |
| // Detect subcommand: check, format, version, and other known Ruff subcommands | |
| let first_arg = args.get(0).map(|s| s.as_str()); | |
| let is_check = match first_arg { | |
| // No arguments: default to `ruff check .` | |
| None => true, | |
| // Explicit `check` subcommand | |
| Some("check") => true, | |
| // Leading flag: don't force `check` | |
| Some(arg) if arg.starts_with('-') => false, | |
| // Non-flag argument: treat as `check` path unless it's a known Ruff subcommand | |
| Some(arg) => { | |
| !matches!( | |
| arg, | |
| "format" | |
| | "version" | |
| | "rule" | |
| | "config" | |
| | "lsp" | |
| | "server" | |
| | "help" | |
| | "clean" | |
| ) | |
| } | |
| }; |
| // Force JSON output | ||
| let has_format = args | ||
| .iter() | ||
| .any(|a| a == "--out-format" || a.starts_with("--out-format=")); | ||
|
|
||
| if !has_format { | ||
| cmd.arg("run").arg("--out-format=json"); | ||
| } else { | ||
| cmd.arg("run"); | ||
| } | ||
|
|
||
| for arg in args { | ||
| cmd.arg(arg); | ||
| } | ||
|
|
||
| if verbose > 0 { | ||
| eprintln!("Running: golangci-lint run --out-format=json"); |
There was a problem hiding this comment.
The command unconditionally injects the "run" subcommand (line 45, 47). This means if a user calls rtk golangci-lint help, it becomes golangci-lint run help, which is incorrect. Similarly, rtk golangci-lint version would become golangci-lint run version. Consider checking if the first argument is a known golangci-lint subcommand (like "help", "version", "linters", "config") and only inject "run" when there's no subcommand present or when it's "run" itself.
| // Force JSON output | |
| let has_format = args | |
| .iter() | |
| .any(|a| a == "--out-format" || a.starts_with("--out-format=")); | |
| if !has_format { | |
| cmd.arg("run").arg("--out-format=json"); | |
| } else { | |
| cmd.arg("run"); | |
| } | |
| for arg in args { | |
| cmd.arg(arg); | |
| } | |
| if verbose > 0 { | |
| eprintln!("Running: golangci-lint run --out-format=json"); | |
| // Force JSON output for the `run` subcommand | |
| let has_format = args | |
| .iter() | |
| .any(|a| a == "--out-format" || a.starts_with("--out-format=")); | |
| // Determine how to handle subcommands: | |
| // - If the first argument is "run", don't inject another "run", but still enforce JSON output if needed. | |
| // - If the first argument is a known non-"run" subcommand (e.g., "help", "version"), don't inject "run". | |
| // - Otherwise, inject "run" before the user arguments. | |
| let first = args.first().map(String::as_str); | |
| let is_known_non_run_subcommand = matches!( | |
| first, | |
| Some("help") | Some("version") | Some("linters") | Some("config") | |
| ); | |
| let is_run = matches!(first, Some("run")); | |
| let should_inject_run = !is_known_non_run_subcommand && !is_run; | |
| let mut final_args: Vec<String> = Vec::new(); | |
| if should_inject_run { | |
| final_args.push("run".to_string()); | |
| } | |
| // Only enforce JSON output when we're effectively running the `run` subcommand. | |
| let needs_json_format = !has_format && (should_inject_run || is_run); | |
| if needs_json_format { | |
| final_args.push("--out-format=json".to_string()); | |
| } | |
| for arg in args { | |
| final_args.push(arg.clone()); | |
| } | |
| for arg in &final_args { | |
| cmd.arg(arg); | |
| } | |
| if verbose > 0 { | |
| eprintln!("Running: golangci-lint {}", final_args.join(" ")); |
| fn which_command(cmd: &str) -> Option<String> { | ||
| Command::new("which") | ||
| .arg(cmd) | ||
| .output() | ||
| .ok() | ||
| .filter(|o| o.status.success()) | ||
| .and_then(|o| String::from_utf8(o.stdout).ok()) | ||
| .map(|s| s.trim().to_string()) | ||
| .filter(|s| !s.is_empty()) | ||
| } |
There was a problem hiding this comment.
The which_command function is duplicated between pytest_cmd.rs and pip_cmd.rs. This creates maintenance overhead as any bug fix or improvement would need to be applied in multiple places. Consider extracting this function to utils.rs and importing it from there, similar to how truncate is shared across modules.
| fn compact_path(path: &str) -> String { | ||
| let path = path.replace('\\', "/"); | ||
|
|
||
| if let Some(pos) = path.rfind("/src/") { | ||
| format!("src/{}", &path[pos + 5..]) | ||
| } else if let Some(pos) = path.rfind("/lib/") { | ||
| format!("lib/{}", &path[pos + 5..]) | ||
| } else if let Some(pos) = path.rfind("/tests/") { | ||
| format!("tests/{}", &path[pos + 7..]) | ||
| } else if let Some(pos) = path.rfind('/') { | ||
| path[pos + 1..].to_string() | ||
| } else { | ||
| path | ||
| } | ||
| } |
There was a problem hiding this comment.
The compact_path function is duplicated across multiple files (ruff_cmd.rs, golangci_cmd.rs, grep_cmd.rs, lint_cmd.rs) with slightly different implementations. This creates maintenance overhead and inconsistent behavior. Consider extracting a unified version to utils.rs that can handle both Python-specific paths (src/, lib/, tests/) and Go-specific paths (pkg/, cmd/, internal/), similar to how truncate is shared across modules.
| fn compact_path(path: &str) -> String { | ||
| let path = path.replace('\\', "/"); | ||
|
|
||
| if let Some(pos) = path.rfind("/pkg/") { | ||
| format!("pkg/{}", &path[pos + 5..]) | ||
| } else if let Some(pos) = path.rfind("/cmd/") { | ||
| format!("cmd/{}", &path[pos + 5..]) | ||
| } else if let Some(pos) = path.rfind("/internal/") { | ||
| format!("internal/{}", &path[pos + 10..]) | ||
| } else if let Some(pos) = path.rfind('/') { | ||
| path[pos + 1..].to_string() | ||
| } else { | ||
| path | ||
| } | ||
| } |
There was a problem hiding this comment.
The compact_path function is duplicated from other files (ruff_cmd.rs, grep_cmd.rs, lint_cmd.rs) with different implementations. This creates maintenance overhead and inconsistent behavior. Consider extracting a unified version to utils.rs that can handle both Python-specific paths (src/, lib/, tests/) and Go-specific paths (pkg/, cmd/, internal/), similar to how truncate is shared across modules.
| fn which_command(cmd: &str) -> Option<String> { | ||
| Command::new("which") | ||
| .arg(cmd) | ||
| .output() | ||
| .ok() | ||
| .filter(|o| o.status.success()) | ||
| .and_then(|o| String::from_utf8(o.stdout).ok()) | ||
| .map(|s| s.trim().to_string()) | ||
| .filter(|s| !s.is_empty()) | ||
| } |
There was a problem hiding this comment.
The which command used here is Unix-specific and will not work on Windows systems. This limits cross-platform compatibility. Consider using the which crate (commonly used in Rust) or implementing platform-specific logic to detect commands in PATH on Windows (using where command or checking PATH environment variable directly). This issue exists in other similar functions throughout the codebase, but should be addressed here to prevent spreading the pattern to new code.
| fn which_command(cmd: &str) -> Option<String> { | ||
| Command::new("which") | ||
| .arg(cmd) | ||
| .output() | ||
| .ok() | ||
| .filter(|o| o.status.success()) | ||
| .and_then(|o| String::from_utf8(o.stdout).ok()) | ||
| .map(|s| s.trim().to_string()) | ||
| .filter(|s| !s.is_empty()) | ||
| } |
There was a problem hiding this comment.
The which command used here is Unix-specific and will not work on Windows systems. This limits cross-platform compatibility. Consider using the which crate (commonly used in Rust) or implementing platform-specific logic to detect commands in PATH on Windows (using where command or checking PATH environment variable directly). This issue exists in other similar functions throughout the codebase, but should be addressed here to prevent spreading the pattern to new code.
| /// Parse a test result summary line | ||
| /// Format: "test result: ok. 15 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out; finished in 0.01s" | ||
| fn parse_line(line: &str) -> Option<Self> { | ||
| static RE: OnceLock<regex::Regex> = OnceLock::new(); |
There was a problem hiding this comment.
The regex usage here doesn't follow the codebase convention. According to the stored memory and examining multiple files (deps.rs:3, filter.rs:2, grep_cmd.rs:3, etc.), the convention is to import Regex explicitly with use regex::Regex; rather than using the fully qualified path regex::Regex. Add the import at the top of the file.
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>
* 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(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>
* 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(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(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
Adds comprehensive Python and Go language support with 70-90% token reduction across all commands.
Closes #82
Python commands (3):
rtk ruff check/format- Linter/formatter with JSON (check) and text (format) parsing (80%+ reduction)rtk pytest- Test runner with state machine text parser (90%+ reduction)rtk pip list/outdated/install- Package manager with auto-detect uv (70-85% reduction)Go commands (4):
rtk go test- NDJSON streaming parser for interleaved test events (90%+ reduction)rtk go build- Text filter showing errors only (80% reduction)rtk go vet- Text filter for issues (75% reduction)rtk golangci-lint- JSON parser grouped by rule (85% reduction)Architecture:
lint/prettierpattern)git/cargopattern)ruff_cmd,pytest_cmd,pip_cmd,go_cmd,golangci_cmdrtk-rewrite.shfor transparent command rewritesFiles changed:
Test plan
cargo test)cargo fmt --all)scripts/test-all.sh(conditional on tool availability)rtk-rewrite.sh🤖 Generated with Claude Code