From 47145a142cdb477f0af6c9b971dad2a7d2d0019e Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 10 Mar 2026 21:11:09 +0800 Subject: [PATCH 1/2] fix: let global subcommands in task scripts fall through as Verbatim `vp run` crashed when a task script contained global-only commands like `vp config` because CLIArgs parsing failed on unrecognized subcommands. Now InvalidSubcommand errors return HandledCommand::Verbatim, letting the global CLI binary handle them as regular subprocesses. --- packages/cli/binding/src/cli.rs | 27 +++++++++++++++++-- .../command-run-with-vp-config/package.json | 9 +++++++ .../command-run-with-vp-config/snap.txt | 26 ++++++++++++++++++ .../command-run-with-vp-config/steps.json | 3 +++ 4 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 packages/cli/snap-tests/command-run-with-vp-config/package.json create mode 100644 packages/cli/snap-tests/command-run-with-vp-config/snap.txt create mode 100644 packages/cli/snap-tests/command-run-with-vp-config/steps.json diff --git a/packages/cli/binding/src/cli.rs b/packages/cli/binding/src/cli.rs index 6e2ac6ade5..b4e189e15f 100644 --- a/packages/cli/binding/src/cli.rs +++ b/packages/cli/binding/src/cli.rs @@ -671,8 +671,15 @@ impl CommandHandler for VitePlusCommandHandler { } // Parse "vp " using CLIArgs — always use "vp" as the program name // so clap shows "Usage: vp ..." even if the original command was "vite ..." - let cli_args = - CLIArgs::try_parse_from(iter::once("vp").chain(command.args.iter().map(Str::as_str)))?; + let cli_args = match CLIArgs::try_parse_from( + iter::once("vp").chain(command.args.iter().map(Str::as_str)), + ) { + Ok(args) => args, + Err(err) if err.kind() == ErrorKind::InvalidSubcommand => { + return Ok(HandledCommand::Verbatim); + } + Err(err) => return Err(err.into()), + }; match cli_args { CLIArgs::Synthesizable(SynthesizableSubcommand::Check { .. }) => { // Check is a composite command — run as a subprocess in task scripts @@ -1660,4 +1667,20 @@ mod tests { let subcommand = SynthesizableSubcommand::Lint { args: vec!["src/index.ts".to_string()] }; assert!(!should_suppress_subcommand_stdout(&subcommand)); } + + #[test] + fn global_subcommands_produce_invalid_subcommand_error() { + use clap::error::ErrorKind; + + for subcommand in ["config", "create", "env", "migrate"] { + let error = CLIArgs::try_parse_from(["vp", subcommand]) + .expect_err(&format!("expected error for global subcommand '{subcommand}'")); + assert_eq!( + error.kind(), + ErrorKind::InvalidSubcommand, + "expected InvalidSubcommand for '{subcommand}', got {:?}", + error.kind() + ); + } + } } diff --git a/packages/cli/snap-tests/command-run-with-vp-config/package.json b/packages/cli/snap-tests/command-run-with-vp-config/package.json new file mode 100644 index 0000000000..25118402c5 --- /dev/null +++ b/packages/cli/snap-tests/command-run-with-vp-config/package.json @@ -0,0 +1,9 @@ +{ + "name": "command-run-with-vp-config", + "version": "1.0.0", + "scripts": { + "foo": "vp config", + "bar": "vp not-exist-command" + }, + "packageManager": "pnpm@10.19.0" +} diff --git a/packages/cli/snap-tests/command-run-with-vp-config/snap.txt b/packages/cli/snap-tests/command-run-with-vp-config/snap.txt new file mode 100644 index 0000000000..a7d8c33a45 --- /dev/null +++ b/packages/cli/snap-tests/command-run-with-vp-config/snap.txt @@ -0,0 +1,26 @@ +> vp run foo # should run vp config command +$ vp config ⊘ cache disabled +.git can't be found + +Created AGENTS.md with Vite+ instructions +◇ Add this MCP server config to your agent ─╮ + + { + "vite-plus": { + "command": "npx", + "args": [ + "vp", + "mcp" + ] + } + } + +╰────────────────────────────────────────────╯ + + +[2]> vp run bar # should throw error +$ vp not-exist-command ⊘ cache disabled +error: Command 'not-exist-command' not found + +Did you mean `vp test`? + diff --git a/packages/cli/snap-tests/command-run-with-vp-config/steps.json b/packages/cli/snap-tests/command-run-with-vp-config/steps.json new file mode 100644 index 0000000000..d6b0e1631e --- /dev/null +++ b/packages/cli/snap-tests/command-run-with-vp-config/steps.json @@ -0,0 +1,3 @@ +{ + "commands": ["vp run foo # should run vp config command", "vp run bar # should throw error"] +} From df46871993de80c4ca6814193e08f4f8215c65f0 Mon Sep 17 00:00:00 2001 From: MK Date: Tue, 10 Mar 2026 21:41:58 +0800 Subject: [PATCH 2/2] fix: disable cache for unknown subcommands, exec, and check in task scripts Per review feedback, use Synthesized with UserCacheConfig::disabled() instead of Verbatim for commands that should not be cached: global subcommands (vp config, etc.), vp exec, and vp check. --- packages/cli/binding/src/cli.rs | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/packages/cli/binding/src/cli.rs b/packages/cli/binding/src/cli.rs index b4e189e15f..f21b426974 100644 --- a/packages/cli/binding/src/cli.rs +++ b/packages/cli/binding/src/cli.rs @@ -676,14 +676,18 @@ impl CommandHandler for VitePlusCommandHandler { ) { Ok(args) => args, Err(err) if err.kind() == ErrorKind::InvalidSubcommand => { - return Ok(HandledCommand::Verbatim); + return Ok(HandledCommand::Synthesized( + command.to_synthetic_plan_request(UserCacheConfig::disabled()), + )); } Err(err) => return Err(err.into()), }; match cli_args { CLIArgs::Synthesizable(SynthesizableSubcommand::Check { .. }) => { // Check is a composite command — run as a subprocess in task scripts - Ok(HandledCommand::Verbatim) + Ok(HandledCommand::Synthesized( + command.to_synthetic_plan_request(UserCacheConfig::disabled()), + )) } CLIArgs::Synthesizable(subcmd) => { let resolved = self.resolver.resolve(subcmd, &command.envs, &command.cwd).await?; @@ -692,7 +696,9 @@ impl CommandHandler for VitePlusCommandHandler { CLIArgs::ViteTask(cmd) => Ok(HandledCommand::ViteTaskCommand(cmd)), CLIArgs::Exec(_) => { // exec in task scripts should run as a subprocess - Ok(HandledCommand::Verbatim) + Ok(HandledCommand::Synthesized( + command.to_synthetic_plan_request(UserCacheConfig::disabled()), + )) } } }