feat: add modern JavaScript tooling support (lint, tsc, next, prettier, playwright, prisma)#9
Conversation
Add utils.rs with common utilities used across modern JavaScript tooling commands: - truncate(): Smart string truncation with ellipsis - strip_ansi(): Remove ANSI escape codes from output - execute_command(): Centralized command execution with error handling These utilities enable consistent output formatting and filtering across multiple command modules. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add comprehensive support for modern JS/TS development stack: Commands added: - rtk lint: ESLint/Biome output with grouped rule violations (84% reduction) - rtk tsc: TypeScript compiler errors grouped by file (83% reduction) - rtk next: Next.js build output with route/bundle metrics (87% reduction) - rtk prettier: Format checker showing only files needing changes (70% reduction) - rtk playwright: E2E test results showing failures only (94% reduction) - rtk prisma: Prisma CLI without ASCII art (88% reduction) Features: - Auto-detects package managers (pnpm/yarn/npm/npx) - Preserves exit codes for CI/CD compatibility - Groups errors by file and error code for quick navigation - Strips verbose output while retaining critical information Total: 6 new commands, ~2,000 LOC Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Document the 6 new commands and shared utils module in CHANGELOG.md. Focuses on token reduction metrics and CI/CD compatibility. 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 support for modern JavaScript/TypeScript development tooling to the rtk (Rust Token Killer) CLI. The PR introduces 6 new commands designed to minimize LLM token consumption by filtering verbose output from popular JavaScript tooling (ESLint, TypeScript, Next.js, Prettier, Playwright, and Prisma).
Changes:
- Added shared
utils.rsmodule with common text processing utilities (truncate, strip_ansi, execute_command) - Implemented 6 new command modules for JavaScript tooling with token reduction ranging from 70-94%
- Updated main.rs to register new commands and CLI structure
Reviewed changes
Copilot reviewed 9 out of 9 changed files in this pull request and generated 13 comments.
Show a summary per file
| File | Description |
|---|---|
| src/utils.rs | Shared utilities for ANSI stripping, text truncation, and command execution |
| src/tsc_cmd.rs | TypeScript compiler output filtering with error grouping by file and code |
| src/prisma_cmd.rs | Prisma CLI wrapper supporting generate, migrate (dev/status/deploy), and db push |
| src/prettier_cmd.rs | Prettier format checker showing only files needing changes |
| src/playwright_cmd.rs | Playwright E2E test output showing only failures and slow tests |
| src/next_cmd.rs | Next.js build output filtering with route/bundle metrics |
| src/lint_cmd.rs | ESLint/linter output with grouped rule violations |
| src/main.rs | CLI command registration and routing for new commands |
| CHANGELOG.md | Documentation of changes and token reduction metrics |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| pub fn run(args: &[String], verbose: u8) -> Result<()> { | ||
| // Try tsc directly first, fallback to npx if not found | ||
| let tsc_exists = Command::new("which") | ||
| .arg("tsc") | ||
| .output() | ||
| .map(|o| o.status.success()) | ||
| .unwrap_or(false); |
There was a problem hiding this comment.
Using the which command is not cross-platform compatible. The which command doesn't exist on Windows systems by default (it's where on Windows). Consider using std::env::var("PATH") and checking for executable existence manually, or use a cross-platform approach. This same issue exists in tsc_cmd.rs, prettier_cmd.rs, playwright_cmd.rs, prisma_cmd.rs, next_cmd.rs, and lint_cmd.rs.
| pub fn run(args: &[String], verbose: u8) -> Result<()> { | |
| // Try tsc directly first, fallback to npx if not found | |
| let tsc_exists = Command::new("which") | |
| .arg("tsc") | |
| .output() | |
| .map(|o| o.status.success()) | |
| .unwrap_or(false); | |
| fn command_exists(cmd: &str) -> bool { | |
| if let Some(paths) = std::env::var_os("PATH") { | |
| for path in std::env::split_paths(&paths) { | |
| let mut candidate = path.clone(); | |
| candidate.push(cmd); | |
| if candidate.is_file() { | |
| return true; | |
| } | |
| #[cfg(windows)] | |
| { | |
| let mut candidate_exe = path.clone(); | |
| candidate_exe.push(format!("{cmd}.exe")); | |
| if candidate_exe.is_file() { | |
| return true; | |
| } | |
| } | |
| } | |
| } | |
| false | |
| } | |
| pub fn run(args: &[String], verbose: u8) -> Result<()> { | |
| // Try tsc directly first, fallback to npx if not found | |
| let tsc_exists = command_exists("tsc"); |
| // TODO: Use duration in detailed reports (token-efficient summary doesn't need it) | ||
| #[allow(dead_code)] | ||
| duration: Option<f64>, |
There was a problem hiding this comment.
This TODO comment should be removed or converted to an actual implementation plan if the feature is needed. If the duration field is not needed for token-efficient summaries as stated, consider removing it entirely rather than keeping it with #[allow(dead_code)].
| // TODO: Use duration in detailed reports (token-efficient summary doesn't need it) | |
| #[allow(dead_code)] | |
| duration: Option<f64>, |
| cmd.arg("build"); | ||
|
|
||
| for arg in args { | ||
| cmd.arg(arg); | ||
| } | ||
|
|
||
| if verbose > 0 { | ||
| let tool = if next_exists { "next" } else { "npx next" }; | ||
| eprintln!("Running: {} build", tool); | ||
| } | ||
|
|
||
| let output = cmd.output().context("Failed to run next build (try: npm install -g next)")?; | ||
| let stdout = String::from_utf8_lossy(&output.stdout); | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| let raw = format!("{}\n{}", stdout, stderr); | ||
|
|
||
| let filtered = filter_next_build(&raw); | ||
|
|
||
| println!("{}", filtered); | ||
|
|
||
| tracking::track("next build", "rtk next build", &raw, &filtered); |
There was a problem hiding this comment.
The run function always adds "build" as the first argument to the next command. This means the command can only run next build and not other Next.js commands like next dev, next start, or next lint. The CLI in main.rs is defined as Next command but only supports build. Either rename the command to NextBuild or accept a subcommand argument to support other Next.js commands.
| cmd.arg("build"); | |
| for arg in args { | |
| cmd.arg(arg); | |
| } | |
| if verbose > 0 { | |
| let tool = if next_exists { "next" } else { "npx next" }; | |
| eprintln!("Running: {} build", tool); | |
| } | |
| let output = cmd.output().context("Failed to run next build (try: npm install -g next)")?; | |
| let stdout = String::from_utf8_lossy(&output.stdout); | |
| let stderr = String::from_utf8_lossy(&output.stderr); | |
| let raw = format!("{}\n{}", stdout, stderr); | |
| let filtered = filter_next_build(&raw); | |
| println!("{}", filtered); | |
| tracking::track("next build", "rtk next build", &raw, &filtered); | |
| // Determine subcommand (default to "build" if none is provided) | |
| let (subcommand, remaining_args) = if !args.is_empty() && !args[0].starts_with('-') { | |
| (args[0].as_str(), &args[1..]) | |
| } else { | |
| ("build", args) | |
| }; | |
| cmd.arg(subcommand); | |
| for arg in remaining_args { | |
| cmd.arg(arg); | |
| } | |
| if verbose > 0 { | |
| let tool = if next_exists { "next" } else { "npx next" }; | |
| eprintln!("Running: {} {}", tool, subcommand); | |
| } | |
| let output = cmd | |
| .output() | |
| .context("Failed to run next (try: npm install -g next)")?; | |
| let stdout = String::from_utf8_lossy(&output.stdout); | |
| let stderr = String::from_utf8_lossy(&output.stderr); | |
| let raw = format!("{}\n{}", stdout, stderr); | |
| let filtered = if subcommand == "build" { | |
| filter_next_build(&raw) | |
| } else { | |
| // For non-build subcommands, do not apply build-specific filtering. | |
| raw.clone() | |
| }; | |
| println!("{}", filtered); | |
| let event = format!("next {}", subcommand); | |
| let label = format!("rtk next {}", subcommand); | |
| tracking::track(&event, &label, &raw, &filtered); |
|
|
||
| if !output.status.success() { | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| anyhow::bail!("prisma generate failed: {}", stderr); |
There was a problem hiding this comment.
The Prisma commands use anyhow::bail! to handle failures, which means they return errors through the Result type instead of preserving the original exit codes like other commands (tsc, prettier, playwright, next). This creates inconsistent behavior - Prisma commands will exit with code 1 regardless of the actual Prisma CLI exit code, while other commands preserve exit codes for CI/CD compatibility. Consider using std::process::exit(output.status.code().unwrap_or(1)) to be consistent with other commands.
| anyhow::bail!("prisma generate failed: {}", stderr); | |
| eprintln!("prisma generate failed: {}", stderr); | |
| std::process::exit(output.status.code().unwrap_or(1)); |
|
|
||
| if !output.status.success() { | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| anyhow::bail!("prisma migrate failed: {}", stderr); |
There was a problem hiding this comment.
The Prisma migrate commands use anyhow::bail! to handle failures, which means they return errors through the Result type instead of preserving the original exit codes like other commands. This creates inconsistent behavior - Prisma commands will exit with code 1 regardless of the actual Prisma CLI exit code. Consider using std::process::exit(output.status.code().unwrap_or(1)) to be consistent with other commands.
| anyhow::bail!("prisma migrate failed: {}", stderr); | |
| let exit_code = output.status.code().unwrap_or(1); | |
| eprintln!("prisma migrate failed with exit code {}: {}", exit_code, stderr); | |
| std::process::exit(exit_code); |
| // EXCEPTION: Static regex patterns, validated at compile time | ||
| // Unwrap is safe here - panic indicates programming error caught during development |
There was a problem hiding this comment.
While the comment explains that unwrap is safe for compile-time validated regex patterns, this comment is unnecessarily verbose. The lazy_static pattern with Regex::new().unwrap() is a well-established Rust idiom. The comment can be simplified or removed since this pattern is already used elsewhere in the codebase (e.g., in utils.rs and other commands) without explanation.
| // EXCEPTION: Static regex patterns, validated at compile time | |
| // Unwrap is safe here - panic indicates programming error caught during development | |
| // Static regex patterns used with lazy_static and Regex::new().unwrap() |
| &raw, | ||
| &filtered, | ||
| ); | ||
|
|
There was a problem hiding this comment.
The lint command does not preserve the exit code from eslint, unlike other commands (tsc, prettier, playwright, next). ESLint typically returns exit code 1 when lint errors are found, and this should be propagated for CI/CD compatibility. The function should call std::process::exit(output.status.code().unwrap_or(1)) if the linter failed, similar to how other commands handle this.
| if !output.status.success() { | |
| std::process::exit(output.status.code().unwrap_or(1)); | |
| } |
| // If max_len is too small, just return "..." | ||
| "...".to_string() | ||
| } else { | ||
| format!("{}...", &s[..max_len - 3]) |
There was a problem hiding this comment.
The string slicing operation &s[..max_len - 3] can panic if s contains multi-byte UTF-8 characters and the slice boundary falls in the middle of a character. Consider using s.char_indices() to find safe boundary points or s.chars().take(max_len - 3).collect() to handle UTF-8 strings safely.
| format!("{}...", &s[..max_len - 3]) | |
| let prefix: String = s.chars().take(max_len - 3).collect(); | |
| format!("{}...", prefix) |
| /// Supprime les codes ANSI d'une chaîne (couleurs, styles). | ||
| /// | ||
| /// # Arguments | ||
| /// * `text` - Texte contenant potentiellement des codes ANSI |
There was a problem hiding this comment.
The documentation for the strip_ansi function is written in French, which is inconsistent with the rest of the codebase. All other documentation in the project appears to be in English. The documentation should be translated to English for consistency and broader accessibility.
| /// Supprime les codes ANSI d'une chaîne (couleurs, styles). | |
| /// | |
| /// # Arguments | |
| /// * `text` - Texte contenant potentiellement des codes ANSI | |
| /// Removes ANSI codes from a string (colors, styles). | |
| /// | |
| /// # Arguments | |
| /// * `text` - Text that may contain ANSI codes |
| /// Exécute une commande et retourne stdout/stderr nettoyés. | ||
| /// | ||
| /// # Arguments | ||
| /// * `cmd` - Commande à exécuter (ex: "eslint") | ||
| /// * `args` - Arguments de la commande |
There was a problem hiding this comment.
The documentation for the execute_command function is written in French, which is inconsistent with the rest of the codebase. All other documentation in the project appears to be in English. The documentation should be translated to English for consistency and broader accessibility.
| /// Exécute une commande et retourne stdout/stderr nettoyés. | |
| /// | |
| /// # Arguments | |
| /// * `cmd` - Commande à exécuter (ex: "eslint") | |
| /// * `args` - Arguments de la commande | |
| /// Executes a command and returns cleaned stdout/stderr. | |
| /// | |
| /// # Arguments | |
| /// * `cmd` - Command to execute (e.g. "eslint") | |
| /// * `args` - Arguments to pass to the command |
Add utils.rs with common utilities used by gh_cmd.rs: - truncate(): Smart string truncation with ellipsis - strip_ansi(): Remove ANSI escape codes from output - execute_command(): Centralized command execution Note: This module is shared with the Modern JS Stack commands from PR rtk-ai#9, ensuring consistent formatting across all commands. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add benchmarks for the 6 new commands in scripts/benchmark.sh: - tsc: TypeScript compiler error grouping - prettier: Format checker with file filtering - lint: ESLint/Biome grouped violations - next: Next.js build metrics extraction - playwright: E2E test failure filtering - prisma: Prisma CLI without ASCII art All benchmarks are conditional (skip if tools not available or not applicable to current project). Tests only run on projects with package.json and relevant configuration files. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add utils.rs as Key Architectural Component - Expand Module Responsibilities table (7→17 modules) - Document PR rtk-ai#9 in Fork-Specific Features section - Include token reduction metrics for all new commands
feat: add GitHub CLI integration (depends on #9)
Updates documentation to reflect all features added in recent PRs: **Version Updates** - Update installation commands to v0.3.1 (DEB/RPM packages) **New Sections** - Add Global Flags section (-u/--ultra-compact, -v/--verbose) - Add JavaScript/TypeScript Stack section (10 new commands) **New Commands Documented** - Files: `rtk smart` (heuristic code summary) - Commands: `rtk gh` (GitHub CLI), `rtk wget`, `rtk config` - Data: `rtk gain --quota` and `--tier` flags - Containers: `rtk kubectl services` - JS/TS Stack: lint, tsc, next, prettier, vitest, playwright, prisma **Features Coverage** This update documents functionality from: - PR rtk-ai#5: Git argument parsing improvements - PR rtk-ai#6: pnpm support - PR rtk-ai#9: Modern JavaScript/TypeScript stack support - PR rtk-ai#10: GitHub CLI integration - PR rtk-ai#11: Quota analysis features - PR rtk-ai#14: Additional command improvements All commands documented are available in v0.3.1. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Updates documentation to reflect all features added in recent PRs: **Version Updates** - Update installation commands to v0.3.1 (DEB/RPM packages) **New Sections** - Add Global Flags section (-u/--ultra-compact, -v/--verbose) - Add JavaScript/TypeScript Stack section (10 new commands) **New Commands Documented** - Files: `rtk smart` (heuristic code summary) - Commands: `rtk gh` (GitHub CLI), `rtk wget`, `rtk config` - Data: `rtk gain --quota` and `--tier` flags - Containers: `rtk kubectl services` - JS/TS Stack: lint, tsc, next, prettier, vitest, playwright, prisma **Features Coverage** This update documents functionality from: - PR rtk-ai#5: Git argument parsing improvements - PR rtk-ai#6: pnpm support - PR rtk-ai#9: Modern JavaScript/TypeScript stack support - PR rtk-ai#10: GitHub CLI integration - PR rtk-ai#11: Quota analysis features - PR rtk-ai#14: Additional command improvements All commands documented are available in v0.3.1. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
feat: add modern JavaScript tooling support (lint, tsc, next, prettier, playwright, prisma)
- Add utils.rs as Key Architectural Component - Expand Module Responsibilities table (7→17 modules) - Document PR rtk-ai#9 in Fork-Specific Features section - Include token reduction metrics for all new commands
feat: add GitHub CLI integration (depends on rtk-ai#9)
Updates documentation to reflect all features added in recent PRs: **Version Updates** - Update installation commands to v0.3.1 (DEB/RPM packages) **New Sections** - Add Global Flags section (-u/--ultra-compact, -v/--verbose) - Add JavaScript/TypeScript Stack section (10 new commands) **New Commands Documented** - Files: `rtk smart` (heuristic code summary) - Commands: `rtk gh` (GitHub CLI), `rtk wget`, `rtk config` - Data: `rtk gain --quota` and `--tier` flags - Containers: `rtk kubectl services` - JS/TS Stack: lint, tsc, next, prettier, vitest, playwright, prisma **Features Coverage** This update documents functionality from: - PR rtk-ai#5: Git argument parsing improvements - PR rtk-ai#6: pnpm support - PR rtk-ai#9: Modern JavaScript/TypeScript stack support - PR rtk-ai#10: GitHub CLI integration - PR rtk-ai#11: Quota analysis features - PR rtk-ai#14: Additional command improvements All commands documented are available in v0.3.1. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Change run_rails_filtered to accept impl Fn instead of fn pointer, enabling closures that capture local state (issue rtk-ai#9) - Refactor run_routes to use run_rails_filtered with closure capturing has_grep flag (~40 lines removed) - Refactor run_generate to use run_rails_filtered with closure capturing generator_type/generator_name (~30 lines removed) - Replace double MOUNTED_ENGINES iteration (.any + .find) with single .find() call (issue rtk-ai#10) - Replace BTreeMap with HashMap for namespaces in filter_rails_routes since data is re-sorted by count anyway (issue rtk-ai#12) - Remove redundant !t.is_empty() check since t.len() > 1 implies non-empty (issue rtk-ai#13) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Summary
Add comprehensive support for modern JavaScript/TypeScript development tooling with 6 new commands optimized for T3 Stack workflows:
Key Features
utils.rsmodule for consistent formatting across commandsToken Reduction Metrics
Validated on production T3 Stack project (methode-aristote/app):
Changes
3 commits with clean separation:
9 files changed, 2,203 insertions
All new modules follow existing RTK patterns
Zero breaking changes to existing commands
Clippy-clean for new modules (upstream warnings not touched)
Test Plan
🤖 Generated with Claude Code