From ade40d812da963ba61c7a9211e1286d3f4fd5d69 Mon Sep 17 00:00:00 2001 From: "David Andrews (LexGenius.ai)" Date: Tue, 17 Feb 2026 14:34:54 -0500 Subject: [PATCH 1/5] Add first-class uv command dispatch --- hooks/rtk-rewrite.sh | 4 +- hooks/test-rtk-rewrite.sh | 16 ++++ src/main.rs | 12 +++ src/uv_cmd.rs | 197 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 227 insertions(+), 2 deletions(-) create mode 100644 src/uv_cmd.rs diff --git a/hooks/rtk-rewrite.sh b/hooks/rtk-rewrite.sh index 59e02ca..7e3320a 100644 --- a/hooks/rtk-rewrite.sh +++ b/hooks/rtk-rewrite.sh @@ -165,6 +165,8 @@ elif echo "$MATCH_CMD" | grep -qE '^pnpm[[:space:]]+(list|ls|outdated)([[:space: REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^pnpm /rtk pnpm /')" # --- Python tooling --- +elif echo "$MATCH_CMD" | grep -qE '^uv[[:space:]]+'; then + REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^uv /rtk uv /')" elif echo "$MATCH_CMD" | grep -qE '^pytest([[:space:]]|$)'; then REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^pytest/rtk pytest/')" elif echo "$MATCH_CMD" | grep -qE '^python[[:space:]]+-m[[:space:]]+pytest([[:space:]]|$)'; then @@ -173,8 +175,6 @@ elif echo "$MATCH_CMD" | grep -qE '^ruff[[:space:]]+(check|format)([[:space:]]|$ REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^ruff /rtk ruff /')" elif echo "$MATCH_CMD" | grep -qE '^pip[[:space:]]+(list|outdated|install|show)([[:space:]]|$)'; then REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^pip /rtk pip /')" -elif echo "$MATCH_CMD" | grep -qE '^uv[[:space:]]+pip[[:space:]]+(list|outdated|install|show)([[:space:]]|$)'; then - REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^uv pip /rtk pip /')" # --- Go tooling --- elif echo "$MATCH_CMD" | grep -qE '^go[[:space:]]+test([[:space:]]|$)'; then diff --git a/hooks/test-rtk-rewrite.sh b/hooks/test-rtk-rewrite.sh index 2c5535b..9f2f12a 100755 --- a/hooks/test-rtk-rewrite.sh +++ b/hooks/test-rtk-rewrite.sh @@ -117,6 +117,18 @@ test_rewrite "npx prisma migrate" \ "npx prisma migrate" \ "rtk prisma migrate" +test_rewrite "uv pip list" \ + "uv pip list" \ + "rtk uv pip list" + +test_rewrite "uv run python -m pytest -q" \ + "uv run python -m pytest -q" \ + "rtk uv run python -m pytest -q" + +test_rewrite "uv sync --frozen" \ + "uv sync --frozen" \ + "rtk uv sync --frozen" + echo "" # ---- SECTION 2: Env var prefix handling (THE BIG FIX) ---- @@ -149,6 +161,10 @@ test_rewrite "env + docker compose" \ "COMPOSE_PROJECT_NAME=test docker compose up -d" \ "COMPOSE_PROJECT_NAME=test rtk docker compose up -d" +test_rewrite "env + uv run pytest" \ + "PYTHONPATH=. uv run python -m pytest tests/" \ + "PYTHONPATH=. rtk uv run python -m pytest tests/" + echo "" # ---- SECTION 3: New patterns ---- diff --git a/src/main.rs b/src/main.rs index 5bec4da..1415b2a 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,6 +44,7 @@ mod tracking; mod tree; mod tsc_cmd; mod utils; +mod uv_cmd; mod vitest_cmd; mod wget_cmd; @@ -505,6 +506,13 @@ enum Commands { args: Vec, }, + /// uv package manager/runner with first-class subcommand dispatch + Uv { + /// uv arguments (e.g., run, pip, sync) + #[arg(trailing_var_arg = true, allow_hyphen_values = true)] + args: Vec, + }, + /// Go commands with compact output Go { #[command(subcommand)] @@ -1349,6 +1357,10 @@ fn main() -> Result<()> { pip_cmd::run(&args, cli.verbose)?; } + Commands::Uv { args } => { + uv_cmd::run(&args, cli.verbose)?; + } + Commands::Go { command } => match command { GoCommands::Test { args } => { go_cmd::run_test(&args, cli.verbose)?; diff --git a/src/uv_cmd.rs b/src/uv_cmd.rs new file mode 100644 index 0000000..1785bce --- /dev/null +++ b/src/uv_cmd.rs @@ -0,0 +1,197 @@ +use crate::pip_cmd; +use crate::pytest_cmd; +use crate::tracking; +use anyhow::{Context, Result}; +use std::process::Command; + +pub fn run(args: &[String], verbose: u8) -> Result<()> { + if args.is_empty() { + anyhow::bail!("uv: no subcommand specified"); + } + + match args[0].as_str() { + "run" => run_run(&args[1..], verbose), + "pip" => pip_cmd::run(&args[1..], verbose), + "sync" => run_sync(&args[1..], verbose), + _ => run_passthrough(args, verbose), + } +} + +fn run_run(args: &[String], verbose: u8) -> Result<()> { + if args.len() >= 3 && args[0] == "python" && args[1] == "-m" && args[2] == "pytest" { + return pytest_cmd::run(&args[3..], verbose); + } + + if !args.is_empty() && args[0] == "pytest" { + return pytest_cmd::run(&args[1..], verbose); + } + + run_passthrough_with_prefix("run", args, verbose) +} + +fn run_sync(args: &[String], verbose: u8) -> Result<()> { + let timer = tracking::TimedExecution::start(); + + let mut cmd = Command::new("uv"); + cmd.arg("sync"); + for arg in args { + cmd.arg(arg); + } + + if verbose > 0 { + eprintln!("Running: uv sync {}", args.join(" ")); + } + + let output = cmd.output().context("Failed to run uv sync")?; + 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_uv_sync_output(&raw); + + let exit_code = output + .status + .code() + .unwrap_or(if output.status.success() { 0 } else { 1 }); + + if let Some(hint) = crate::tee::tee_and_hint(&raw, "uv_sync", exit_code) { + println!("{}\n{}", filtered, hint); + } else { + println!("{}", filtered); + } + + timer.track( + &format!("uv sync {}", args.join(" ")), + &format!("rtk uv sync {}", args.join(" ")), + &raw, + &filtered, + ); + + if !output.status.success() { + std::process::exit(exit_code); + } + + Ok(()) +} + +fn run_passthrough_with_prefix(prefix: &str, args: &[String], verbose: u8) -> Result<()> { + let mut all_args = Vec::with_capacity(args.len() + 1); + all_args.push(prefix.to_string()); + all_args.extend(args.iter().cloned()); + run_passthrough(&all_args, verbose) +} + +fn run_passthrough(args: &[String], verbose: u8) -> Result<()> { + let timer = tracking::TimedExecution::start(); + + let mut cmd = Command::new("uv"); + for arg in args { + cmd.arg(arg); + } + + if verbose > 0 { + eprintln!("Running: uv {}", args.join(" ")); + } + + let output = cmd + .output() + .with_context(|| format!("Failed to run uv {}", args.join(" ")))?; + + let stdout = String::from_utf8_lossy(&output.stdout); + let stderr = String::from_utf8_lossy(&output.stderr); + let raw = format!("{}\n{}", stdout, stderr); + + print!("{}", stdout); + eprint!("{}", stderr); + + timer.track( + &format!("uv {}", args.join(" ")), + &format!("rtk uv {}", args.join(" ")), + &raw, + &raw, + ); + + if !output.status.success() { + std::process::exit(output.status.code().unwrap_or(1)); + } + + Ok(()) +} + +fn filter_uv_sync_output(output: &str) -> String { + let mut summary_lines = Vec::new(); + let mut package_lines = Vec::new(); + + for raw_line in output.lines() { + let line = raw_line.trim(); + if line.is_empty() { + continue; + } + + let lower = line.to_ascii_lowercase(); + let is_summary = line.starts_with("Resolved") + || line.starts_with("Prepared") + || line.starts_with("Installed") + || line.starts_with("Uninstalled") + || line.starts_with("Added") + || line.starts_with("Removed") + || line.starts_with("Updated") + || line.starts_with("Audited"); + let is_error = lower.contains("error") || lower.contains("failed"); + let is_package_delta = line.starts_with('+') || line.starts_with('-'); + + if is_summary || is_error { + summary_lines.push(line.to_string()); + } else if is_package_delta { + package_lines.push(line.to_string()); + } + } + + if summary_lines.is_empty() && package_lines.is_empty() { + return "✓ uv sync completed".to_string(); + } + + let mut out = String::from("uv sync\n═══════════════════════════════════════\n"); + for line in summary_lines { + out.push_str(&line); + out.push('\n'); + } + + if !package_lines.is_empty() { + out.push_str("\nPackage changes:\n"); + for line in package_lines.iter().take(15) { + out.push_str(line); + out.push('\n'); + } + if package_lines.len() > 15 { + out.push_str(&format!("... +{} more\n", package_lines.len() - 15)); + } + } + + out.trim().to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_filter_uv_sync_output_summary() { + let output = r#" +Resolved 145 packages in 4ms +Installed 2 packages in 21ms ++ pytest==8.3.2 +- pytest==8.2.0 +"#; + let filtered = filter_uv_sync_output(output); + assert!(filtered.contains("uv sync")); + assert!(filtered.contains("Resolved 145 packages")); + assert!(filtered.contains("Installed 2 packages")); + assert!(filtered.contains("pytest==8.3.2")); + } + + #[test] + fn test_filter_uv_sync_output_fallback() { + let filtered = filter_uv_sync_output("done"); + assert_eq!(filtered, "✓ uv sync completed"); + } +} From 9deec0c4320e01c74b4e35872ec9ad0885a58493 Mon Sep 17 00:00:00 2001 From: "David Andrews (LexGenius.ai)" Date: Tue, 17 Feb 2026 15:52:20 -0500 Subject: [PATCH 2/5] Handle .venv python pytest rewrite and uv dispatch --- hooks/rtk-rewrite.sh | 2 ++ hooks/test-rtk-rewrite.sh | 8 ++++++++ src/uv_cmd.rs | 25 ++++++++++++++++++++++++- 3 files changed, 34 insertions(+), 1 deletion(-) diff --git a/hooks/rtk-rewrite.sh b/hooks/rtk-rewrite.sh index 7e3320a..8c36577 100644 --- a/hooks/rtk-rewrite.sh +++ b/hooks/rtk-rewrite.sh @@ -171,6 +171,8 @@ elif echo "$MATCH_CMD" | grep -qE '^pytest([[:space:]]|$)'; then REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^pytest/rtk pytest/')" elif echo "$MATCH_CMD" | grep -qE '^python[[:space:]]+-m[[:space:]]+pytest([[:space:]]|$)'; then REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^python -m pytest/rtk pytest/')" +elif echo "$MATCH_CMD" | grep -qE '^([^[:space:]]+/)?python(3)?[[:space:]]+-m[[:space:]]+pytest([[:space:]]|$)'; then + REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed -E 's@^([^[:space:]]+/)?python(3)? -m pytest@rtk pytest@')" elif echo "$MATCH_CMD" | grep -qE '^ruff[[:space:]]+(check|format)([[:space:]]|$)'; then REWRITTEN="${ENV_PREFIX}$(echo "$CMD_BODY" | sed 's/^ruff /rtk ruff /')" elif echo "$MATCH_CMD" | grep -qE '^pip[[:space:]]+(list|outdated|install|show)([[:space:]]|$)'; then diff --git a/hooks/test-rtk-rewrite.sh b/hooks/test-rtk-rewrite.sh index 9f2f12a..0f14157 100755 --- a/hooks/test-rtk-rewrite.sh +++ b/hooks/test-rtk-rewrite.sh @@ -165,6 +165,14 @@ test_rewrite "env + uv run pytest" \ "PYTHONPATH=. uv run python -m pytest tests/" \ "PYTHONPATH=. rtk uv run python -m pytest tests/" +test_rewrite "venv python pytest" \ + ".venv/bin/python -m pytest tests/unit/" \ + "rtk pytest tests/unit/" + +test_rewrite "env + venv python pytest" \ + "PYTHONPATH=. .venv/bin/python -m pytest tests/unit/" \ + "PYTHONPATH=. rtk pytest tests/unit/" + echo "" # ---- SECTION 3: New patterns ---- diff --git a/src/uv_cmd.rs b/src/uv_cmd.rs index 1785bce..46f8c3c 100644 --- a/src/uv_cmd.rs +++ b/src/uv_cmd.rs @@ -2,6 +2,7 @@ use crate::pip_cmd; use crate::pytest_cmd; use crate::tracking; use anyhow::{Context, Result}; +use std::path::Path; use std::process::Command; pub fn run(args: &[String], verbose: u8) -> Result<()> { @@ -18,7 +19,7 @@ pub fn run(args: &[String], verbose: u8) -> Result<()> { } fn run_run(args: &[String], verbose: u8) -> Result<()> { - if args.len() >= 3 && args[0] == "python" && args[1] == "-m" && args[2] == "pytest" { + if args.len() >= 3 && is_python_executable(&args[0]) && args[1] == "-m" && args[2] == "pytest" { return pytest_cmd::run(&args[3..], verbose); } @@ -29,6 +30,18 @@ fn run_run(args: &[String], verbose: u8) -> Result<()> { run_passthrough_with_prefix("run", args, verbose) } +fn is_python_executable(candidate: &str) -> bool { + if candidate == "python" || candidate == "python3" { + return true; + } + + let Some(file_name) = Path::new(candidate).file_name().and_then(|s| s.to_str()) else { + return false; + }; + + matches!(file_name, "python" | "python3") +} + fn run_sync(args: &[String], verbose: u8) -> Result<()> { let timer = tracking::TimedExecution::start(); @@ -194,4 +207,14 @@ Installed 2 packages in 21ms let filtered = filter_uv_sync_output("done"); assert_eq!(filtered, "✓ uv sync completed"); } + + #[test] + fn test_is_python_executable() { + assert!(is_python_executable("python")); + assert!(is_python_executable("python3")); + assert!(is_python_executable(".venv/bin/python")); + assert!(is_python_executable("/tmp/venv/bin/python3")); + assert!(!is_python_executable("pytest")); + assert!(!is_python_executable(".venv/bin/pypy")); + } } From 660ca3ff96c0749f2b1f225875721d18266e94a4 Mon Sep 17 00:00:00 2001 From: "David Andrews (LexGenius.ai)" Date: Tue, 17 Feb 2026 15:52:58 -0500 Subject: [PATCH 3/5] Add explicit .ven python pytest coverage --- hooks/test-rtk-rewrite.sh | 8 ++++++++ src/uv_cmd.rs | 1 + 2 files changed, 9 insertions(+) diff --git a/hooks/test-rtk-rewrite.sh b/hooks/test-rtk-rewrite.sh index 0f14157..f43051f 100755 --- a/hooks/test-rtk-rewrite.sh +++ b/hooks/test-rtk-rewrite.sh @@ -173,6 +173,14 @@ test_rewrite "env + venv python pytest" \ "PYTHONPATH=. .venv/bin/python -m pytest tests/unit/" \ "PYTHONPATH=. rtk pytest tests/unit/" +test_rewrite "ven python pytest" \ + ".ven/bin/python -m pytest tests/unit/" \ + "rtk pytest tests/unit/" + +test_rewrite "env + ven python pytest" \ + "PYTHONPATH=. .ven/bin/python -m pytest tests/unit/" \ + "PYTHONPATH=. rtk pytest tests/unit/" + echo "" # ---- SECTION 3: New patterns ---- diff --git a/src/uv_cmd.rs b/src/uv_cmd.rs index 46f8c3c..9aab188 100644 --- a/src/uv_cmd.rs +++ b/src/uv_cmd.rs @@ -212,6 +212,7 @@ Installed 2 packages in 21ms fn test_is_python_executable() { assert!(is_python_executable("python")); assert!(is_python_executable("python3")); + assert!(is_python_executable(".ven/bin/python")); assert!(is_python_executable(".venv/bin/python")); assert!(is_python_executable("/tmp/venv/bin/python3")); assert!(!is_python_executable("pytest")); From 90a5d48bd97d69d1c964fd7ef660d71da5dd5e4d Mon Sep 17 00:00:00 2001 From: "David Andrews (LexGenius.ai)" Date: Tue, 17 Feb 2026 15:53:43 -0500 Subject: [PATCH 4/5] Route uv run mypy through lint filter --- hooks/test-rtk-rewrite.sh | 4 ++++ src/uv_cmd.rs | 11 +++++++++++ 2 files changed, 15 insertions(+) diff --git a/hooks/test-rtk-rewrite.sh b/hooks/test-rtk-rewrite.sh index f43051f..c9249aa 100755 --- a/hooks/test-rtk-rewrite.sh +++ b/hooks/test-rtk-rewrite.sh @@ -125,6 +125,10 @@ test_rewrite "uv run python -m pytest -q" \ "uv run python -m pytest -q" \ "rtk uv run python -m pytest -q" +test_rewrite "uv run mypy" \ + "uv run mypy src/" \ + "rtk uv run mypy src/" + test_rewrite "uv sync --frozen" \ "uv sync --frozen" \ "rtk uv sync --frozen" diff --git a/src/uv_cmd.rs b/src/uv_cmd.rs index 9aab188..130ad06 100644 --- a/src/uv_cmd.rs +++ b/src/uv_cmd.rs @@ -1,3 +1,4 @@ +use crate::lint_cmd; use crate::pip_cmd; use crate::pytest_cmd; use crate::tracking; @@ -23,10 +24,20 @@ fn run_run(args: &[String], verbose: u8) -> Result<()> { return pytest_cmd::run(&args[3..], verbose); } + if args.len() >= 3 && is_python_executable(&args[0]) && args[1] == "-m" && args[2] == "mypy" { + let mut lint_args = vec!["mypy".to_string()]; + lint_args.extend(args[3..].iter().cloned()); + return lint_cmd::run(&lint_args, verbose); + } + if !args.is_empty() && args[0] == "pytest" { return pytest_cmd::run(&args[1..], verbose); } + if !args.is_empty() && args[0] == "mypy" { + return lint_cmd::run(args, verbose); + } + run_passthrough_with_prefix("run", args, verbose) } From ab87b9c693e60263ced654da2885c5f073571057 Mon Sep 17 00:00:00 2001 From: "David Andrews (LexGenius.ai)" Date: Tue, 17 Feb 2026 15:55:49 -0500 Subject: [PATCH 5/5] Route uv run Python linters through RTK filters --- hooks/test-rtk-rewrite.sh | 12 +++++ src/uv_cmd.rs | 102 ++++++++++++++++++++++++++++++++++---- 2 files changed, 103 insertions(+), 11 deletions(-) diff --git a/hooks/test-rtk-rewrite.sh b/hooks/test-rtk-rewrite.sh index c9249aa..ad47f03 100755 --- a/hooks/test-rtk-rewrite.sh +++ b/hooks/test-rtk-rewrite.sh @@ -129,6 +129,18 @@ test_rewrite "uv run mypy" \ "uv run mypy src/" \ "rtk uv run mypy src/" +test_rewrite "uv run ruff" \ + "uv run ruff check src/" \ + "rtk uv run ruff check src/" + +test_rewrite "uv run pylint" \ + "uv run pylint src/" \ + "rtk uv run pylint src/" + +test_rewrite "uv run flake8" \ + "uv run flake8 src/" \ + "rtk uv run flake8 src/" + test_rewrite "uv sync --frozen" \ "uv sync --frozen" \ "rtk uv sync --frozen" diff --git a/src/uv_cmd.rs b/src/uv_cmd.rs index 130ad06..2e2d5b3 100644 --- a/src/uv_cmd.rs +++ b/src/uv_cmd.rs @@ -1,6 +1,7 @@ use crate::lint_cmd; use crate::pip_cmd; use crate::pytest_cmd; +use crate::ruff_cmd; use crate::tracking; use anyhow::{Context, Result}; use std::path::Path; @@ -20,25 +21,45 @@ pub fn run(args: &[String], verbose: u8) -> Result<()> { } fn run_run(args: &[String], verbose: u8) -> Result<()> { - if args.len() >= 3 && is_python_executable(&args[0]) && args[1] == "-m" && args[2] == "pytest" { - return pytest_cmd::run(&args[3..], verbose); + if let Some((tool, start_idx)) = routed_uv_run_tool(args) { + let rest = &args[start_idx..]; + return run_routed_uv_tool(tool, rest, verbose); } - if args.len() >= 3 && is_python_executable(&args[0]) && args[1] == "-m" && args[2] == "mypy" { - let mut lint_args = vec!["mypy".to_string()]; - lint_args.extend(args[3..].iter().cloned()); - return lint_cmd::run(&lint_args, verbose); + run_passthrough_with_prefix("run", args, verbose) +} + +fn run_routed_uv_tool(tool: &str, args: &[String], verbose: u8) -> Result<()> { + match tool { + "pytest" => pytest_cmd::run(args, verbose), + "ruff" => ruff_cmd::run(args, verbose), + "mypy" | "pylint" | "flake8" => { + let mut lint_args = vec![tool.to_string()]; + lint_args.extend(args.iter().cloned()); + lint_cmd::run(&lint_args, verbose) + } + _ => run_passthrough_with_prefix("run", args, verbose), + } +} + +fn routed_uv_run_tool(args: &[String]) -> Option<(&str, usize)> { + if args.is_empty() { + return None; } - if !args.is_empty() && args[0] == "pytest" { - return pytest_cmd::run(&args[1..], verbose); + if args.len() >= 3 && is_python_executable(&args[0]) && args[1] == "-m" { + let tool = args[2].as_str(); + if matches!(tool, "pytest" | "ruff" | "mypy" | "pylint" | "flake8") { + return Some((tool, 3)); + } } - if !args.is_empty() && args[0] == "mypy" { - return lint_cmd::run(args, verbose); + let tool = args[0].as_str(); + if matches!(tool, "pytest" | "ruff" | "mypy" | "pylint" | "flake8") { + return Some((tool, 1)); } - run_passthrough_with_prefix("run", args, verbose) + None } fn is_python_executable(candidate: &str) -> bool { @@ -229,4 +250,63 @@ Installed 2 packages in 21ms assert!(!is_python_executable("pytest")); assert!(!is_python_executable(".venv/bin/pypy")); } + + #[test] + fn test_routed_uv_run_tool_direct() { + assert_eq!( + routed_uv_run_tool(&["mypy".to_string(), "src/".to_string()]), + Some(("mypy", 1)) + ); + assert_eq!( + routed_uv_run_tool(&["ruff".to_string(), "check".to_string()]), + Some(("ruff", 1)) + ); + assert_eq!( + routed_uv_run_tool(&["pytest".to_string(), "-q".to_string()]), + Some(("pytest", 1)) + ); + } + + #[test] + fn test_routed_uv_run_tool_python_module() { + assert_eq!( + routed_uv_run_tool(&[ + ".venv/bin/python".to_string(), + "-m".to_string(), + "pylint".to_string(), + ".".to_string() + ]), + Some(("pylint", 3)) + ); + assert_eq!( + routed_uv_run_tool(&[ + ".ven/bin/python".to_string(), + "-m".to_string(), + "flake8".to_string(), + ".".to_string() + ]), + Some(("flake8", 3)) + ); + assert_eq!( + routed_uv_run_tool(&[ + "python3".to_string(), + "-m".to_string(), + "mypy".to_string(), + "src".to_string() + ]), + Some(("mypy", 3)) + ); + } + + #[test] + fn test_routed_uv_run_tool_passthrough_cases() { + assert_eq!( + routed_uv_run_tool(&["python".to_string(), "-m".to_string(), "black".to_string()]), + None + ); + assert_eq!( + routed_uv_run_tool(&["alembic".to_string(), "upgrade".to_string()]), + None + ); + } }