Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Bug Fixes

* **npx:** route unknown tools through npx instead of npm ([#815](https://github.com/rtk-ai/rtk/issues/815))
* **hook:** respect Claude Code deny/ask permission rules on rewrite — hook now checks settings.json before rewriting commands, preventing bypass of user-configured deny/ask permissions
* **git:** replace symbol prefixes (`* branch`, `+ Staged:`, `~ Modified:`, `? Untracked:`) with plain lowercase labels (`branch:`, `staged:`, `modified:`, `untracked:`) in git status output
* **ruby:** use `rails test` instead of `rake test` when positional file args are passed — `rake test` ignores positional files and only supports `TEST=path`
Expand Down
2 changes: 1 addition & 1 deletion src/cmds/js/npm_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ pub fn run(args: &[String], verbose: u8, skip_env: bool) -> Result<()> {
}

/// Filter npm run output - strip boilerplate, progress bars, npm WARN
fn filter_npm_output(output: &str) -> String {
pub fn filter_npm_output(output: &str) -> String {
let mut result = Vec::new();

for line in output.lines() {
Expand Down
62 changes: 60 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1972,8 +1972,31 @@ fn main() -> Result<()> {
playwright_cmd::run(&args[1..], cli.verbose)?;
}
_ => {
// Generic passthrough with npm boilerplate filter
npm_cmd::run(&args, cli.verbose, cli.skip_env)?;
let timer = core::tracking::TimedExecution::start();
let mut cmd = core::utils::resolved_command("npx");
cmd.args(&args);
if cli.skip_env {
cmd.env("SKIP_ENV_VALIDATION", "1");
}
if cli.verbose > 0 {
eprintln!("Running: npx {}", args.join(" "));
}
let output = cmd.output().context("Failed to run npx")?;
let stdout = String::from_utf8_lossy(&output.stdout);
let stderr = String::from_utf8_lossy(&output.stderr);
let raw = format!("{}\n{}", stdout, stderr);
let filtered = npm_cmd::filter_npm_output(&raw);
println!("{}", filtered);
let args_str = args.join(" ");
timer.track(
&format!("npx {}", args_str),
&format!("rtk npx {}", args_str),
&raw,
&filtered,
);
if !output.status.success() {
std::process::exit(output.status.code().unwrap_or(1));
}
}
}
}
Expand Down Expand Up @@ -2598,4 +2621,39 @@ mod tests {
}
}
}

#[test]
fn test_npx_parses_simple_command() {
let cli = Cli::try_parse_from(["rtk", "npx", "cowsay", "hello"]).unwrap();
match cli.command {
Commands::Npx { args } => {
assert_eq!(args, vec!["cowsay", "hello"]);
}
_ => panic!("Expected Npx command"),
}
}

#[test]
fn test_npx_parses_flags_before_command() {
let cli = Cli::try_parse_from(["rtk", "npx", "--no-install", "cowsay", "hello"]).unwrap();
match cli.command {
Commands::Npx { args } => {
assert_eq!(args, vec!["--no-install", "cowsay", "hello"]);
}
_ => panic!("Expected Npx command"),
}
}

#[test]
fn test_npx_parses_known_tool_names() {
for tool in &["tsc", "eslint", "prisma", "next", "prettier", "playwright"] {
let cli = Cli::try_parse_from(["rtk", "npx", tool, "--help"]).unwrap();
match cli.command {
Commands::Npx { ref args } => {
assert_eq!(args[0], *tool);
}
_ => panic!("Expected Npx command for {}", tool),
}
}
}
}