+
+
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/apps/web/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/apps/web/package.json
new file mode 100644
index 0000000000..64630869c1
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/apps/web/package.json
@@ -0,0 +1 @@
+{ "name": "web", "private": true }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/package.json
new file mode 100644
index 0000000000..05c1c2ef02
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/package.json
@@ -0,0 +1 @@
+{ "name": "app-root-auto-select", "private": true, "workspaces": ["apps/*", "packages/*"] }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/packages/ui/package.json
new file mode 100644
index 0000000000..70bb6aab17
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/packages/ui/package.json
@@ -0,0 +1 @@
+{ "name": "ui", "private": true, "type": "module", "main": "src/index.ts" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/packages/ui/src/index.ts
new file mode 100644
index 0000000000..905c7a9bbb
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/packages/ui/src/index.ts
@@ -0,0 +1,3 @@
+export function hello(name: string): string {
+ return `hello ${name}`;
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots.toml
new file mode 100644
index 0000000000..0d74f9f251
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots.toml
@@ -0,0 +1,10 @@
+[[case]]
+name = "auto_select"
+vp = ["local", "global"]
+comment = """
+With exactly one likely-runnable package, a bare app command in an interactive
+terminal auto-selects it, prints the Selected/Tip teaching lines, and runs
+there (rfcs/cwd-flag.md). This TTY-only branch was untestable in the old
+harness.
+"""
+steps = [["vp", "build"]]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots/auto_select.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots/auto_select.global.md
new file mode 100644
index 0000000000..46cc92d7a2
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots/auto_select.global.md
@@ -0,0 +1,21 @@
+# auto_select
+
+With exactly one likely-runnable package, a bare app command in an interactive
+terminal auto-selects it, prints the Selected/Tip teaching lines, and runs
+there (rfcs/cwd-flag.md). This TTY-only branch was untestable in the old
+harness.
+
+## `vp build`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+Selected package: web (apps/web)
+Tip: run this directly with `vp -C apps/web build`
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots/auto_select.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots/auto_select.local.md
new file mode 100644
index 0000000000..ceca69331c
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_auto_select/snapshots/auto_select.local.md
@@ -0,0 +1,19 @@
+# auto_select
+
+With exactly one likely-runnable package, a bare app command in an interactive
+terminal auto-selects it, prints the Selected/Tip teaching lines, and runs
+there (rfcs/cwd-flag.md). This TTY-only branch was untestable in the old
+harness.
+
+## `vp build`
+
+```
+Selected package: web (apps/web)
+Tip: run this directly with `vp -C apps/web build`
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/missing/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/missing/package.json
new file mode 100644
index 0000000000..f6a8d9de96
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/missing/package.json
@@ -0,0 +1 @@
+{ "name": "missing-default", "private": true }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/missing/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/missing/vite.config.ts
new file mode 100644
index 0000000000..8204c22024
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/missing/vite.config.ts
@@ -0,0 +1,3 @@
+export default {
+ defaultPackage: './packages/nope',
+};
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/package.json
new file mode 100644
index 0000000000..b9fabc032d
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/package.json
@@ -0,0 +1 @@
+{ "name": "app-root-default-package", "private": true }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/packages/ui/package.json
new file mode 100644
index 0000000000..70bb6aab17
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/packages/ui/package.json
@@ -0,0 +1 @@
+{ "name": "ui", "private": true, "type": "module", "main": "src/index.ts" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/packages/ui/src/index.ts
new file mode 100644
index 0000000000..905c7a9bbb
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/packages/ui/src/index.ts
@@ -0,0 +1,3 @@
+export function hello(name: string): string {
+ return `hello ${name}`;
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml
new file mode 100644
index 0000000000..e85cd246d9
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots.toml
@@ -0,0 +1,31 @@
+[[case]]
+name = "default_package"
+vp = ["local", "global"]
+comment = """
+defaultPackage in the root config acts as an implicit -C for bare app
+commands, including at a root that is not a JS workspace; vp prints a note
+line and runs in the configured directory (rfcs/cwd-flag.md).
+"""
+steps = [
+ ["vp", "pack"],
+ { argv = ["vpt", "list-dir", "packages/ui/dist"], comment = "output lands in the configured package" },
+]
+
+[[case]]
+name = "default_package_missing"
+vp = ["local", "global"]
+comment = "defaultPackage pointing at a missing directory errors before any workspace lookup."
+steps = [{ argv = ["vp", "build"], cwd = "missing" }]
+
+[[case]]
+name = "unanalyzable_config_ignored"
+vp = ["local", "global"]
+comment = """
+Regression guard for spread/unanalyzable configs: a config that only parses
+as an open map might hide defaultPackage behind the spread, but that must
+not fail the command. Bare vp build falls through and runs in place.
+"""
+# PTY-rendered (not tty = false): non-TTY steps record raw bytes, and the
+# raw Windows byte stream of a vite build differs invisibly from macOS; the
+# grid rendering normalizes that and is what the assertion needs anyway.
+steps = [{ argv = ["vp", "build"], cwd = "spread" }]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package.global.md
new file mode 100644
index 0000000000..3e187a1d9b
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package.global.md
@@ -0,0 +1,26 @@
+# default_package
+
+defaultPackage in the root config acts as an implicit -C for bare app
+commands, including at a root that is not a JS workspace; vp prints a note
+line and runs in the configured directory (rfcs/cwd-flag.md).
+
+## `vp pack`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+note: vp pack: using ./packages/ui (defaultPackage)
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir packages/ui/dist`
+
+output lands in the configured package
+
+```
+index.mjs
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package.local.md
new file mode 100644
index 0000000000..622f391b6c
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package.local.md
@@ -0,0 +1,24 @@
+# default_package
+
+defaultPackage in the root config acts as an implicit -C for bare app
+commands, including at a root that is not a JS workspace; vp prints a note
+line and runs in the configured directory (rfcs/cwd-flag.md).
+
+## `vp pack`
+
+```
+note: vp pack: using ./packages/ui (defaultPackage)
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir packages/ui/dist`
+
+output lands in the configured package
+
+```
+index.mjs
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_missing.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_missing.global.md
new file mode 100644
index 0000000000..b05221c778
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_missing.global.md
@@ -0,0 +1,13 @@
+# default_package_missing
+
+defaultPackage pointing at a missing directory errors before any workspace lookup.
+
+## `cd missing && vp build`
+
+**Exit code:** 1
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+error: defaultPackage points to a missing directory: ./packages/nope
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_missing.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_missing.local.md
new file mode 100644
index 0000000000..768869fad9
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/default_package_missing.local.md
@@ -0,0 +1,11 @@
+# default_package_missing
+
+defaultPackage pointing at a missing directory errors before any workspace lookup.
+
+## `cd missing && vp build`
+
+**Exit code:** 1
+
+```
+error: defaultPackage points to a missing directory: ./packages/nope
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/unanalyzable_config_ignored.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/unanalyzable_config_ignored.global.md
new file mode 100644
index 0000000000..ea92615f0c
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/unanalyzable_config_ignored.global.md
@@ -0,0 +1,18 @@
+# unanalyzable_config_ignored
+
+Regression guard for spread/unanalyzable configs: a config that only parses
+as an open map might hide defaultPackage behind the spread, but that must
+not fail the command. Bare vp build falls through and runs in place.
+
+## `cd spread && vp build`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/unanalyzable_config_ignored.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/unanalyzable_config_ignored.local.md
new file mode 100644
index 0000000000..1446589b7d
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/snapshots/unanalyzable_config_ignored.local.md
@@ -0,0 +1,16 @@
+# unanalyzable_config_ignored
+
+Regression guard for spread/unanalyzable configs: a config that only parses
+as an open map might hide defaultPackage behind the spread, but that must
+not fail the command. Bare vp build falls through and runs in place.
+
+## `cd spread && vp build`
+
+```
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/spread/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/spread/index.html
new file mode 100644
index 0000000000..32b40097ee
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_default_package/spread/index.html
@@ -0,0 +1,6 @@
+
+
+
+
+
+
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/apps/web/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/apps/web/package.json
new file mode 100644
index 0000000000..64630869c1
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/apps/web/package.json
@@ -0,0 +1 @@
+{ "name": "web", "private": true }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/package.json
new file mode 100644
index 0000000000..b2d7158d1d
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/package.json
@@ -0,0 +1,11 @@
+{
+ "name": "app-root-listing",
+ "private": true,
+ "workspaces": [
+ "apps/*",
+ "packages/*"
+ ],
+ "scripts": {
+ "build": "vp build"
+ }
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/packages/ui/package.json
new file mode 100644
index 0000000000..70bb6aab17
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/packages/ui/package.json
@@ -0,0 +1 @@
+{ "name": "ui", "private": true, "type": "module", "main": "src/index.ts" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/packages/ui/src/index.ts
new file mode 100644
index 0000000000..905c7a9bbb
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/packages/ui/src/index.ts
@@ -0,0 +1,3 @@
+export function hello(name: string): string {
+ return `hello ${name}`;
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots.toml
new file mode 100644
index 0000000000..953b61ac07
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots.toml
@@ -0,0 +1,50 @@
+[[case]]
+name = "listing"
+vp = ["local", "global"]
+comment = """
+A bare app command at a workspace root without an interactive terminal prints
+the ranked package listing with -C hints and exits 1 instead of building the
+root (rfcs/cwd-flag.md).
+"""
+steps = [
+ { argv = ["vp", "build"], tty = false, continue-on-failure = true },
+ { argv = ["vp", "dev"], tty = false, comment = "dev at the root no longer starts a server against the root" },
+]
+
+[[case]]
+name = "picker_select"
+vp = ["local", "global"]
+comment = """
+A bare app command at a workspace root with several candidates opens the
+fuzzy package picker (the vp run selector component); typing filters, Enter
+runs the selection as an implicit -C (rfcs/cwd-flag.md).
+"""
+steps = [
+ { argv = ["vp", "build"], interactions = [
+ { "expect-milestone" = "package-select::0" },
+ { "write" = "web" },
+ { "expect-milestone" = "package-select:web:0" },
+ { "write-key" = "enter" },
+ ] },
+]
+
+[[case]]
+name = "picker_cancel"
+vp = ["local", "global"]
+comment = "Ctrl+C in the package picker cancels with exit 130 and runs nothing."
+steps = [
+ { argv = ["vp", "build"], interactions = [
+ { "expect-milestone" = "package-select::0" },
+ { "write-key" = "ctrl-c" },
+ ] },
+]
+
+[[case]]
+name = "script_at_root_elicits"
+vp = ["local", "global"]
+comment = """
+A root package script "build": "vp build" run through vp run gets the same
+target elicitation as a direct bare invocation: the spawned vp prints the
+listing and the task fails instead of silently building the root.
+"""
+steps = [{ argv = ["vp", "run", "build"], tty = false }]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/listing.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/listing.global.md
new file mode 100644
index 0000000000..daf3c000d6
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/listing.global.md
@@ -0,0 +1,39 @@
+# listing
+
+A bare app command at a workspace root without an interactive terminal prints
+the ranked package listing with -C hints and exits 1 instead of building the
+root (rfcs/cwd-flag.md).
+
+## `vp build`
+
+**Exit code:** 1
+
+```
+[1m[31merror:[39m[0m `vp build` at the workspace root needs a target package.
+
+ Packages in this workspace:
+ admin apps/admin
+ web apps/web
+ ui packages/ui
+
+ Pass a directory: vp -C apps/admin build
+ Or run every package's build script: vp run -r build
+```
+
+## `vp dev`
+
+dev at the root no longer starts a server against the root
+
+**Exit code:** 1
+
+```
+[1m[31merror:[39m[0m `vp dev` at the workspace root needs a target package.
+
+ Packages in this workspace:
+ admin apps/admin
+ web apps/web
+ ui packages/ui
+
+ Pass a directory: vp -C apps/admin dev
+ Or run every package's dev script: vp run -r dev
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/listing.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/listing.local.md
new file mode 100644
index 0000000000..daf3c000d6
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/listing.local.md
@@ -0,0 +1,39 @@
+# listing
+
+A bare app command at a workspace root without an interactive terminal prints
+the ranked package listing with -C hints and exits 1 instead of building the
+root (rfcs/cwd-flag.md).
+
+## `vp build`
+
+**Exit code:** 1
+
+```
+[1m[31merror:[39m[0m `vp build` at the workspace root needs a target package.
+
+ Packages in this workspace:
+ admin apps/admin
+ web apps/web
+ ui packages/ui
+
+ Pass a directory: vp -C apps/admin build
+ Or run every package's build script: vp run -r build
+```
+
+## `vp dev`
+
+dev at the root no longer starts a server against the root
+
+**Exit code:** 1
+
+```
+[1m[31merror:[39m[0m `vp dev` at the workspace root needs a target package.
+
+ Packages in this workspace:
+ admin apps/admin
+ web apps/web
+ ui packages/ui
+
+ Pass a directory: vp -C apps/admin dev
+ Or run every package's dev script: vp run -r dev
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_cancel.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_cancel.global.md
new file mode 100644
index 0000000000..1731cd3bcf
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_cancel.global.md
@@ -0,0 +1,25 @@
+# picker_cancel
+
+Ctrl+C in the package picker cancels with exit 130 and runs nothing.
+
+## `vp build`
+
+**Exit code:** 130
+
+**→ expect-milestone:** `package-select::0`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+Select a package to build (↑/↓, Enter to run, type to search):
+
+ › admin apps/admin
+ web apps/web
+ ui packages/ui
+```
+
+**← write-key:** `ctrl-c`
+
+```
+VITE+ - The Unified Toolchain for the Web
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_cancel.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_cancel.local.md
new file mode 100644
index 0000000000..0855d0a694
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_cancel.local.md
@@ -0,0 +1,22 @@
+# picker_cancel
+
+Ctrl+C in the package picker cancels with exit 130 and runs nothing.
+
+## `vp build`
+
+**Exit code:** 130
+
+**→ expect-milestone:** `package-select::0`
+
+```
+Select a package to build (↑/↓, Enter to run, type to search):
+
+ › admin apps/admin
+ web apps/web
+ ui packages/ui
+```
+
+**← write-key:** `ctrl-c`
+
+```
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_select.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_select.global.md
new file mode 100644
index 0000000000..a5f8c1fe5e
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_select.global.md
@@ -0,0 +1,46 @@
+# picker_select
+
+A bare app command at a workspace root with several candidates opens the
+fuzzy package picker (the vp run selector component); typing filters, Enter
+runs the selection as an implicit -C (rfcs/cwd-flag.md).
+
+## `vp build`
+
+**→ expect-milestone:** `package-select::0`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+Select a package to build (↑/↓, Enter to run, type to search):
+
+ › admin apps/admin
+ web apps/web
+ ui packages/ui
+```
+
+**← write:** `web`
+
+**→ expect-milestone:** `package-select:web:0`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+Select a package to build (↑/↓, Enter to run, type to search): web
+
+ › web apps/web
+```
+
+**← write-key:** `enter`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+Selected package: web (apps/web)
+Tip: run this directly with `vp -C apps/web build`
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_select.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_select.local.md
new file mode 100644
index 0000000000..c2fd913b4c
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/picker_select.local.md
@@ -0,0 +1,40 @@
+# picker_select
+
+A bare app command at a workspace root with several candidates opens the
+fuzzy package picker (the vp run selector component); typing filters, Enter
+runs the selection as an implicit -C (rfcs/cwd-flag.md).
+
+## `vp build`
+
+**→ expect-milestone:** `package-select::0`
+
+```
+Select a package to build (↑/↓, Enter to run, type to search):
+
+ › admin apps/admin
+ web apps/web
+ ui packages/ui
+```
+
+**← write:** `web`
+
+**→ expect-milestone:** `package-select:web:0`
+
+```
+Select a package to build (↑/↓, Enter to run, type to search): web
+
+ › web apps/web
+```
+
+**← write-key:** `enter`
+
+```
+Selected package: web (apps/web)
+Tip: run this directly with `vp -C apps/web build`
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/script_at_root_elicits.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/script_at_root_elicits.global.md
new file mode 100644
index 0000000000..9b5daa0fa8
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/script_at_root_elicits.global.md
@@ -0,0 +1,23 @@
+# script_at_root_elicits
+
+A root package script "build": "vp build" run through vp run gets the same
+target elicitation as a direct bare invocation: the spawned vp prints the
+listing and the task fails instead of silently building the root.
+
+## `vp run build`
+
+**Exit code:** 1
+
+```
+$ vp build ⊘ cache disabled
+
+[1m[31merror:[39m[0m `vp build` at the workspace root needs a target package.
+
+ Packages in this workspace:
+ admin apps/admin
+ web apps/web
+ ui packages/ui
+
+ Pass a directory: vp -C apps/admin build
+ Or run every package's build script: vp run -r build
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/script_at_root_elicits.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/script_at_root_elicits.local.md
new file mode 100644
index 0000000000..9b5daa0fa8
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/snapshots/script_at_root_elicits.local.md
@@ -0,0 +1,23 @@
+# script_at_root_elicits
+
+A root package script "build": "vp build" run through vp run gets the same
+target elicitation as a direct bare invocation: the spawned vp prints the
+listing and the task fails instead of silently building the root.
+
+## `vp run build`
+
+**Exit code:** 1
+
+```
+$ vp build ⊘ cache disabled
+
+[1m[31merror:[39m[0m `vp build` at the workspace root needs a target package.
+
+ Packages in this workspace:
+ admin apps/admin
+ web apps/web
+ ui packages/ui
+
+ Pass a directory: vp -C apps/admin build
+ Or run every package's build script: vp run -r build
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/vite.config.ts
new file mode 100644
index 0000000000..9b4b8b8cd3
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_listing/vite.config.ts
@@ -0,0 +1,4 @@
+// Shared workspace config (lint/fmt style): must NOT make the root an app target.
+export default {
+ lint: {},
+};
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/index.html
new file mode 100644
index 0000000000..31acf91c54
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/index.html
@@ -0,0 +1,6 @@
+
+
+
+
root app
+
+
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/package.json
new file mode 100644
index 0000000000..78cbc31491
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/package.json
@@ -0,0 +1 @@
+{ "name": "runnable-root", "private": true, "workspaces": ["packages/*"] }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/packages/ui/package.json
new file mode 100644
index 0000000000..70bb6aab17
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/packages/ui/package.json
@@ -0,0 +1 @@
+{ "name": "ui", "private": true, "type": "module", "main": "src/index.ts" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/packages/ui/src/index.ts
new file mode 100644
index 0000000000..62c987f2f1
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/packages/ui/src/index.ts
@@ -0,0 +1 @@
+export const ui = true;
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots.toml
new file mode 100644
index 0000000000..7629fe5ebf
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots.toml
@@ -0,0 +1,10 @@
+[[case]]
+name = "runnable_root_runs_in_place"
+vp = ["local", "global"]
+comment = """
+A workspace root that is itself a runnable app never elicits: bare vp build
+runs in place (pre-elicitation behavior), without picker output. The
+non-TTY shape of the same rule is pinned by settings_only_workspace, whose
+tsdown output is byte-stable on Windows (a piped vite build is not).
+"""
+steps = [["vp", "build"]]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots/runnable_root_runs_in_place.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots/runnable_root_runs_in_place.global.md
new file mode 100644
index 0000000000..d4f59d0950
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots/runnable_root_runs_in_place.global.md
@@ -0,0 +1,19 @@
+# runnable_root_runs_in_place
+
+A workspace root that is itself a runnable app never elicits: bare vp build
+runs in place (pre-elicitation behavior), without picker output. The
+non-TTY shape of the same rule is pinned by settings_only_workspace, whose
+tsdown output is byte-stable on Windows (a piped vite build is not).
+
+## `vp build`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots/runnable_root_runs_in_place.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots/runnable_root_runs_in_place.local.md
new file mode 100644
index 0000000000..a9a953de62
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/app_root_runnable_root/snapshots/runnable_root_runs_in_place.local.md
@@ -0,0 +1,17 @@
+# runnable_root_runs_in_place
+
+A workspace root that is itself a runnable app never elicits: bare vp build
+runs in place (pre-elicitation behavior), without picker output. The
+non-TTY shape of the same rule is pinned by settings_only_workspace, whose
+tsdown output is byte-stable on Windows (a piped vite build is not).
+
+## `vp build`
+
+```
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md
index 7a51254a8e..33e4236631 100644
--- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message.md
@@ -58,6 +58,7 @@ Documentation: https://viteplus.dev/guide/
Options:
-V, --version Print version
+ -C Run as if vp was started in instead of the current working directory
-h, --help Print help
```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message_local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message_local.md
index b0c2afef0e..074c6346c6 100644
--- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message_local.md
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cli_helper_message/snapshots/cli_helper_message_local.md
@@ -30,6 +30,7 @@ Package Manager Commands:
install Install all dependencies, or add packages if package names are provided
Options:
+ -C Run as if vp was started in instead of the current working directory
-h, --help Print help
```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/package.json
new file mode 100644
index 0000000000..dea0bba909
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/package.json
@@ -0,0 +1,10 @@
+{
+ "name": "cwd-flag",
+ "private": true,
+ "workspaces": [
+ "packages/*"
+ ],
+ "scripts": {
+ "where:hello": "vp -C packages/hello run where"
+ }
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/packages/hello/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/packages/hello/package.json
new file mode 100644
index 0000000000..c765a5d476
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/packages/hello/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "hello",
+ "version": "1.0.0",
+ "type": "module",
+ "main": "src/index.ts",
+ "scripts": {
+ "where": "node -e \"console.log('cwd base: ' + require('node:path').basename(process.cwd()))\""
+ }
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/packages/hello/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/packages/hello/src/index.ts
new file mode 100644
index 0000000000..905c7a9bbb
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/packages/hello/src/index.ts
@@ -0,0 +1,3 @@
+export function hello(name: string): string {
+ return `hello ${name}`;
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots.toml
new file mode 100644
index 0000000000..1be84e1398
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots.toml
@@ -0,0 +1,21 @@
+[[case]]
+name = "cwd_flag"
+vp = ["local", "global"]
+comment = """
+The global -C flag runs any command as if vp was started in the directory:
+pack and run behave byte-identically to the cd forms, a missing directory
+errors, and the positional keeps upstream tsdown entry semantics
+(rfcs/cwd-flag.md).
+"""
+steps = [
+ { argv = ["vp", "-C", "packages/hello", "pack"], comment = "-C packs the package from the workspace root" },
+ { argv = ["vpt", "list-dir", "packages/hello/dist"], comment = "output lands in the target package" },
+ { argv = ["vpt", "rm", "-rf", "packages/hello/dist"], comment = "reset so both forms produce identical output" },
+ { argv = ["vp", "pack"], cwd = "packages/hello", comment = "the cd form is equivalent" },
+ { argv = ["vp", "-C", "packages/hello", "run", "where"], comment = "-C applies to vp run as well" },
+ { argv = ["vp", "run", "where"], cwd = "packages/hello", comment = "equivalent cd form for run" },
+ { argv = ["vp", "-C", "packages/missing", "build"], comment = "missing directory errors", continue-on-failure = true },
+ { argv = ["vp", "pack", "packages/hello"], comment = "positional stays a tsdown entry resolved from the invocation directory" },
+ { argv = ["vpt", "list-dir", "dist"], comment = "upstream semantics: output lands at the invocation directory" },
+ { argv = ["vp", "run", "where:hello"], comment = "a script whose command starts with vp -C runs verbatim and honors the directory" },
+]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots/cwd_flag.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots/cwd_flag.global.md
new file mode 100644
index 0000000000..0059c25184
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots/cwd_flag.global.md
@@ -0,0 +1,117 @@
+# cwd_flag
+
+The global -C flag runs any command as if vp was started in the directory:
+pack and run behave byte-identically to the cd forms, a missing directory
+errors, and the positional keeps upstream tsdown entry semantics
+(rfcs/cwd-flag.md).
+
+## `vp -C packages/hello pack`
+
+-C packs the package from the workspace root
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir packages/hello/dist`
+
+output lands in the target package
+
+```
+index.mjs
+```
+
+## `vpt rm -rf packages/hello/dist`
+
+reset so both forms produce identical output
+
+```
+```
+
+## `cd packages/hello && vp pack`
+
+the cd form is equivalent
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vp -C packages/hello run where`
+
+-C applies to vp run as well
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+~/packages/hello$ node -e "console.log('cwd base: ' + require('node:path').basename(process.cwd()))" ⊘ cache disabled
+cwd base: hello
+```
+
+## `cd packages/hello && vp run where`
+
+equivalent cd form for run
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+~/packages/hello$ node -e "console.log('cwd base: ' + require('node:path').basename(process.cwd()))" ⊘ cache disabled
+cwd base: hello
+```
+
+## `vp -C packages/missing build`
+
+missing directory errors
+
+**Exit code:** 1
+
+```
+directory not found: packages/missing
+```
+
+## `vp pack packages/hello`
+
+positional stays a tsdown entry resolved from the invocation directory
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+ℹ entry: packages/hello
+ℹ Build start
+ℹ dist/hello.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir dist`
+
+upstream semantics: output lands at the invocation directory
+
+```
+hello.mjs
+```
+
+## `vp run where:hello`
+
+a script whose command starts with vp -C runs verbatim and honors the directory
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+$ vp -C packages/hello run where ⊘ cache disabled
+VITE+ - The Unified Toolchain for the Web
+
+~/packages/hello$ node -e "console.log('cwd base: ' + require('node:path').basename(process.cwd()))" ⊘ cache disabled
+cwd base: hello
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots/cwd_flag.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots/cwd_flag.local.md
new file mode 100644
index 0000000000..e58f7923a2
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/cwd_flag/snapshots/cwd_flag.local.md
@@ -0,0 +1,103 @@
+# cwd_flag
+
+The global -C flag runs any command as if vp was started in the directory:
+pack and run behave byte-identically to the cd forms, a missing directory
+errors, and the positional keeps upstream tsdown entry semantics
+(rfcs/cwd-flag.md).
+
+## `vp -C packages/hello pack`
+
+-C packs the package from the workspace root
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir packages/hello/dist`
+
+output lands in the target package
+
+```
+index.mjs
+```
+
+## `vpt rm -rf packages/hello/dist`
+
+reset so both forms produce identical output
+
+```
+```
+
+## `cd packages/hello && vp pack`
+
+the cd form is equivalent
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vp -C packages/hello run where`
+
+-C applies to vp run as well
+
+```
+~/packages/hello$ node -e "console.log('cwd base: ' + require('node:path').basename(process.cwd()))" ⊘ cache disabled
+cwd base: hello
+```
+
+## `cd packages/hello && vp run where`
+
+equivalent cd form for run
+
+```
+~/packages/hello$ node -e "console.log('cwd base: ' + require('node:path').basename(process.cwd()))" ⊘ cache disabled
+cwd base: hello
+```
+
+## `vp -C packages/missing build`
+
+missing directory errors
+
+**Exit code:** 1
+
+```
+error: directory not found: packages/missing
+```
+
+## `vp pack packages/hello`
+
+positional stays a tsdown entry resolved from the invocation directory
+
+```
+ℹ entry: packages/hello
+ℹ Build start
+ℹ dist/hello.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir dist`
+
+upstream semantics: output lands at the invocation directory
+
+```
+hello.mjs
+```
+
+## `vp run where:hello`
+
+a script whose command starts with vp -C runs verbatim and honors the directory
+
+```
+$ vp -C packages/hello run where ⊘ cache disabled
+~/packages/hello$ node -e "console.log('cwd base: ' + require('node:path').basename(process.cwd()))" ⊘ cache disabled
+cwd base: hello
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/package.json
new file mode 100644
index 0000000000..ed5c75cb8f
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/package.json
@@ -0,0 +1 @@
+{ "name": "member-default-package", "private": true, "workspaces": ["packages/*"] }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/package.json
new file mode 100644
index 0000000000..70bb6aab17
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/package.json
@@ -0,0 +1 @@
+{ "name": "ui", "private": true, "type": "module", "main": "src/index.ts" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/src/index.ts
new file mode 100644
index 0000000000..62c987f2f1
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/src/index.ts
@@ -0,0 +1 @@
+export const ui = true;
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/vite.config.ts
new file mode 100644
index 0000000000..6021f747da
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/packages/ui/vite.config.ts
@@ -0,0 +1,5 @@
+// A workspace member declaring defaultPackage: ignored below the workspace
+// root, where the current directory already identifies the target.
+export default {
+ defaultPackage: "./does-not-exist",
+};
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots.toml
new file mode 100644
index 0000000000..f96ff0faf0
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots.toml
@@ -0,0 +1,9 @@
+[[case]]
+name = "member_config_ignored"
+vp = ["local", "global"]
+comment = """
+defaultPackage is a root-pointer concept: a workspace member's own config
+declaring it (here pointing at a missing directory) must not redirect or
+fail a command already running in that member; the pack runs in place.
+"""
+steps = [{ argv = ["vp", "pack"], cwd = "packages/ui", tty = false }]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots/member_config_ignored.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots/member_config_ignored.global.md
new file mode 100644
index 0000000000..da45246d53
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots/member_config_ignored.global.md
@@ -0,0 +1,15 @@
+# member_config_ignored
+
+defaultPackage is a root-pointer concept: a workspace member's own config
+declaring it (here pointing at a missing directory) must not redirect or
+fail a command already running in that member; the pack runs in place.
+
+## `cd packages/ui && vp pack`
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots/member_config_ignored.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots/member_config_ignored.local.md
new file mode 100644
index 0000000000..da45246d53
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/member_default_package/snapshots/member_config_ignored.local.md
@@ -0,0 +1,15 @@
+# member_config_ignored
+
+defaultPackage is a root-pointer concept: a workspace member's own config
+declaring it (here pointing at a missing directory) must not redirect or
+fail a command already running in that member; the pack runs in place.
+
+## `cd packages/ui && vp pack`
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/index.html
new file mode 100644
index 0000000000..ce6d6280c2
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/index.html
@@ -0,0 +1,6 @@
+
+
+
+
web
+
+
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/package.json
new file mode 100644
index 0000000000..64630869c1
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/package.json
@@ -0,0 +1 @@
+{ "name": "web", "private": true }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/vite.config.ts
new file mode 100644
index 0000000000..ff8b4c5632
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/apps/web/vite.config.ts
@@ -0,0 +1 @@
+export default {};
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/package.json
new file mode 100644
index 0000000000..b6e5ea319d
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/package.json
@@ -0,0 +1 @@
+{ "name": "pack-default-entry", "private": true, "workspaces": ["apps/*", "packages/*"] }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/lib/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/lib/package.json
new file mode 100644
index 0000000000..20968dbb63
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/lib/package.json
@@ -0,0 +1 @@
+{ "name": "lib", "private": true, "type": "module" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/lib/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/lib/src/index.ts
new file mode 100644
index 0000000000..a1398e8d76
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/lib/src/index.ts
@@ -0,0 +1,3 @@
+export function lib(name: string): string {
+ return `lib ${name}`;
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/spread-only/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/spread-only/package.json
new file mode 100644
index 0000000000..318af1703a
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/spread-only/package.json
@@ -0,0 +1 @@
+{ "name": "spread-only", "private": true }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/spread-only/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/spread-only/vite.config.ts
new file mode 100644
index 0000000000..aaf0089da5
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/packages/spread-only/vite.config.ts
@@ -0,0 +1,5 @@
+const base = {};
+
+export default {
+ ...base,
+};
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots.toml
new file mode 100644
index 0000000000..ff6e7598fb
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots.toml
@@ -0,0 +1,14 @@
+[[case]]
+name = "pack_default_entry"
+vp = ["local", "global"]
+comment = """
+Bare vp pack at the root auto-selects the only pack-runnable package: the
+library whose sole signal is tsdown's default src/index.ts entry. The app's
+vite.config.ts has no pack block, so it does not count as pack-runnable
+(rfcs/cwd-flag.md, "The likely-runnable heuristic"); tsdown then packs via
+its default entry with no pack config at all.
+"""
+steps = [
+ ["vp", "pack"],
+ { argv = ["vpt", "list-dir", "packages/lib/dist"], comment = "output lands in the auto-selected library" },
+]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots/pack_default_entry.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots/pack_default_entry.global.md
new file mode 100644
index 0000000000..b52a25aca4
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots/pack_default_entry.global.md
@@ -0,0 +1,29 @@
+# pack_default_entry
+
+Bare vp pack at the root auto-selects the only pack-runnable package: the
+library whose sole signal is tsdown's default src/index.ts entry. The app's
+vite.config.ts has no pack block, so it does not count as pack-runnable
+(rfcs/cwd-flag.md, "The likely-runnable heuristic"); tsdown then packs via
+its default entry with no pack config at all.
+
+## `vp pack`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+Selected package: lib (packages/lib)
+Tip: run this directly with `vp -C packages/lib pack`
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir packages/lib/dist`
+
+output lands in the auto-selected library
+
+```
+index.mjs
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots/pack_default_entry.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots/pack_default_entry.local.md
new file mode 100644
index 0000000000..27c8175b05
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/pack_default_entry/snapshots/pack_default_entry.local.md
@@ -0,0 +1,27 @@
+# pack_default_entry
+
+Bare vp pack at the root auto-selects the only pack-runnable package: the
+library whose sole signal is tsdown's default src/index.ts entry. The app's
+vite.config.ts has no pack block, so it does not count as pack-runnable
+(rfcs/cwd-flag.md, "The likely-runnable heuristic"); tsdown then packs via
+its default entry with no pack config at all.
+
+## `vp pack`
+
+```
+Selected package: lib (packages/lib)
+Tip: run this directly with `vp -C packages/lib pack`
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir packages/lib/dist`
+
+output lands in the auto-selected library
+
+```
+index.mjs
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/package.json
new file mode 100644
index 0000000000..af33ecea8a
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/package.json
@@ -0,0 +1 @@
+{ "name": "settings-only", "private": true, "type": "module" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/pnpm-workspace.yaml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/pnpm-workspace.yaml
new file mode 100644
index 0000000000..4ff9cc2cbd
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/pnpm-workspace.yaml
@@ -0,0 +1 @@
+minimumReleaseAge: 4320
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots.toml
new file mode 100644
index 0000000000..2a97170a7f
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots.toml
@@ -0,0 +1,10 @@
+[[case]]
+name = "pack_in_place"
+vp = ["local", "global"]
+comment = """
+Regression guard for the vp-config e2e shape: a single package whose
+pnpm-workspace.yaml only carries settings (catalogs, minimumReleaseAge) is
+a workspace root whose only runnable candidate is itself. Bare vp pack must
+run in place, TTY or not, never print the target listing.
+"""
+steps = [{ argv = ["vp", "pack"], tty = false }]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots/pack_in_place.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots/pack_in_place.global.md
new file mode 100644
index 0000000000..6f10cdbefc
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots/pack_in_place.global.md
@@ -0,0 +1,16 @@
+# pack_in_place
+
+Regression guard for the vp-config e2e shape: a single package whose
+pnpm-workspace.yaml only carries settings (catalogs, minimumReleaseAge) is
+a workspace root whose only runnable candidate is itself. Bare vp pack must
+run in place, TTY or not, never print the target listing.
+
+## `vp pack`
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots/pack_in_place.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots/pack_in_place.local.md
new file mode 100644
index 0000000000..6f10cdbefc
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/snapshots/pack_in_place.local.md
@@ -0,0 +1,16 @@
+# pack_in_place
+
+Regression guard for the vp-config e2e shape: a single package whose
+pnpm-workspace.yaml only carries settings (catalogs, minimumReleaseAge) is
+a workspace root whose only runnable candidate is itself. Bare vp pack must
+run in place, TTY or not, never print the target listing.
+
+## `vp pack`
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/src/index.ts
new file mode 100644
index 0000000000..7b8c293a75
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/src/index.ts
@@ -0,0 +1 @@
+export const lib = true;
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/vite.config.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/vite.config.ts
new file mode 100644
index 0000000000..580a281fd7
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/settings_only_workspace/vite.config.ts
@@ -0,0 +1,3 @@
+export default {
+ pack: {},
+};
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/index.html b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/index.html
new file mode 100644
index 0000000000..d6845ec898
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/index.html
@@ -0,0 +1,6 @@
+
+
+
+
single package
+
+
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/package.json b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/package.json
new file mode 100644
index 0000000000..10db26f25c
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/package.json
@@ -0,0 +1 @@
+{ "name": "single-package", "private": true, "type": "module" }
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots.toml b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots.toml
new file mode 100644
index 0000000000..dfcf4a5f86
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots.toml
@@ -0,0 +1,19 @@
+[[case]]
+name = "build_in_place"
+vp = ["local", "global"]
+comment = """
+Regression guard: a single-package repo (no workspaces field) never goes
+through target elicitation. Bare vp build runs in place even in an
+interactive terminal: no picker, no Selected line, no listing
+(rfcs/cwd-flag.md, resolution order step "anywhere else").
+"""
+steps = [["vp", "build"]]
+
+[[case]]
+name = "pack_in_place"
+vp = ["local", "global"]
+comment = "Same guard for vp pack: the standalone library packs in place via tsdown's default entry."
+steps = [
+ ["vp", "pack"],
+ { argv = ["vpt", "list-dir", "dist"], comment = "output lands in the repo itself" },
+]
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/build_in_place.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/build_in_place.global.md
new file mode 100644
index 0000000000..d4a89a25be
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/build_in_place.global.md
@@ -0,0 +1,19 @@
+# build_in_place
+
+Regression guard: a single-package repo (no workspaces field) never goes
+through target elicitation. Bare vp build runs in place even in an
+interactive terminal: no picker, no Selected line, no listing
+(rfcs/cwd-flag.md, resolution order step "anywhere else").
+
+## `vp build`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/build_in_place.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/build_in_place.local.md
new file mode 100644
index 0000000000..acb09f2cf9
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/build_in_place.local.md
@@ -0,0 +1,17 @@
+# build_in_place
+
+Regression guard: a single-package repo (no workspaces field) never goes
+through target elicitation. Bare vp build runs in place even in an
+interactive terminal: no picker, no Selected line, no listing
+(rfcs/cwd-flag.md, resolution order step "anywhere else").
+
+## `vp build`
+
+```
+vite building client environment for production...
+✓ 2 modules transformed.
+computing gzip size...
+dist/index.html kB │ gzip: kB
+
+✓ built in
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/pack_in_place.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/pack_in_place.global.md
new file mode 100644
index 0000000000..9eb6e8bdd1
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/pack_in_place.global.md
@@ -0,0 +1,23 @@
+# pack_in_place
+
+Same guard for vp pack: the standalone library packs in place via tsdown's default entry.
+
+## `vp pack`
+
+```
+VITE+ - The Unified Toolchain for the Web
+
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir dist`
+
+output lands in the repo itself
+
+```
+index.mjs
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/pack_in_place.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/pack_in_place.local.md
new file mode 100644
index 0000000000..d8ce6886ae
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/snapshots/pack_in_place.local.md
@@ -0,0 +1,21 @@
+# pack_in_place
+
+Same guard for vp pack: the standalone library packs in place via tsdown's default entry.
+
+## `vp pack`
+
+```
+ℹ entry: src/index.ts
+ℹ Build start
+ℹ dist/index.mjs kB │ gzip: kB
+ℹ 1 files, total: kB
+✔ Build complete in
+```
+
+## `vpt list-dir dist`
+
+output lands in the repo itself
+
+```
+index.mjs
+```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/src/index.ts b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/src/index.ts
new file mode 100644
index 0000000000..56e894462d
--- /dev/null
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/single_package/src/index.ts
@@ -0,0 +1,3 @@
+export function single(name: string): string {
+ return `single ${name}`;
+}
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.global.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.global.md
index c2f0d2f46e..fc51e24490 100644
--- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.global.md
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.global.md
@@ -58,5 +58,6 @@ Documentation: https://viteplus.dev/guide/
Options:
-V, --version Print version
+ -C Run as if vp was started in instead of the current working directory
-h, --help Print help
```
diff --git a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.local.md b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.local.md
index 398c5d0f4d..166ac74621 100644
--- a/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.local.md
+++ b/crates/vite_cli_snapshots/tests/cli_snapshots/fixtures/vp_help/snapshots/help.local.md
@@ -30,5 +30,6 @@ Package Manager Commands:
install Install all dependencies, or add packages if package names are provided
Options:
+ -C Run as if vp was started in instead of the current working directory
-h, --help Print help
```
diff --git a/crates/vite_global_cli/src/cli.rs b/crates/vite_global_cli/src/cli.rs
index 2d91427f40..437ee17d33 100644
--- a/crates/vite_global_cli/src/cli.rs
+++ b/crates/vite_global_cli/src/cli.rs
@@ -3,7 +3,7 @@
//! This module defines the CLI structure using clap and routes commands
//! to their appropriate handlers.
-use std::{collections::HashSet, ffi::OsStr, io::IsTerminal, process::ExitStatus};
+use std::{collections::HashSet, ffi::OsStr, process::ExitStatus};
use clap::{CommandFactory, FromArgMatches, Parser, Subcommand};
use clap_complete::ArgValueCompleter;
@@ -53,6 +53,10 @@ pub struct Args {
#[arg(short = 'V', long = "version")]
pub version: bool,
+ /// Run as if vp was started in instead of the current working directory
+ #[arg(short = 'C', value_name = "DIR")]
+ pub chdir: Option,
+
#[clap(subcommand)]
pub command: Option,
}
@@ -805,7 +809,7 @@ fn should_reinstall_node_mismatches(
return true;
}
- if !std::io::stdin().is_terminal() || std::env::var_os("CI").is_some() {
+ if !vite_shared::is_stdin_terminal() || std::env::var_os("CI").is_some() {
let package_names =
packages.iter().map(|package| package.name.as_str()).collect::>().join(", ");
output::warn(&format!(
@@ -848,10 +852,19 @@ pub async fn run_command(cwd: AbsolutePathBuf, args: Args) -> Result Result {
+ // Apply the global `-C ` flag before anything reads cwd. main
+ // normally consumes a leading `-C` pre-parse; this covers orderings that
+ // reach clap (e.g. a second `-C`), with identical semantics — including
+ // the process-cwd change and PWD sync the shared helper performs.
+ if let Some(dir) = &args.chdir {
+ cwd =
+ crate::apply_chdir(&cwd, dir).map_err(|message| Error::UserMessage(message.into()))?;
+ }
+
// Handle --version flag (Category B: delegates to JS)
if args.version {
return commands::version::execute(cwd).await;
diff --git a/crates/vite_global_cli/src/command_picker.rs b/crates/vite_global_cli/src/command_picker.rs
index dc31289b9c..590f36c941 100644
--- a/crates/vite_global_cli/src/command_picker.rs
+++ b/crates/vite_global_cli/src/command_picker.rs
@@ -1,7 +1,7 @@
//! Interactive top-level command picker for `vp`.
use std::{
- io::{self, IsTerminal, Write},
+ io::{self, Write},
ops::ControlFlow,
};
@@ -114,24 +114,10 @@ const COMMANDS: &[CommandEntry] = &[
},
];
-const CI_ENV_VARS: &[&str] = &[
- "CI",
- "CONTINUOUS_INTEGRATION",
- "GITHUB_ACTIONS",
- "GITLAB_CI",
- "CIRCLECI",
- "TRAVIS",
- "JENKINS_URL",
- "BUILDKITE",
- "DRONE",
- "CODEBUILD_BUILD_ID",
- "TF_BUILD",
-];
-
pub fn pick_top_level_command_if_interactive(
cwd: &AbsolutePath,
) -> io::Result {
- if !should_enable_picker() {
+ if !vite_shared::is_interactive_terminal() {
return Ok(TopLevelCommandPick::Skipped);
}
@@ -143,17 +129,6 @@ pub fn pick_top_level_command_if_interactive(
})
}
-fn should_enable_picker() -> bool {
- std::io::stdin().is_terminal()
- && std::io::stdout().is_terminal()
- && std::env::var("TERM").map_or(true, |term| term != "dumb")
- && !is_ci_environment()
-}
-
-fn is_ci_environment() -> bool {
- CI_ENV_VARS.iter().any(|key| std::env::var_os(key).is_some())
-}
-
fn run_picker(command_order: &[usize]) -> io::Result