You'll need the following tools installed on your system:
- Node.js (version specified in
.node-version) - pnpm (version specified in the
packageManagerfield ofpackage.json) - Just
- CMake
- Rust and Cargo
- cargo-binstall
If you haven't installed Node.js and pnpm, we recommend using Vite+ to manage them. See environment management for details.
If you haven't installed Node.js and pnpm, we recommend installing them with Vite+:
curl -fsSL https://vite.plus | VP_NODE_MANAGER=yes VP_PM_MANAGER=yes bashAfter installing Vite+, restart your terminal to activate the node and pnpm shims.
You'll also need Just and CMake:
brew install just cmakeInstall Rust & Cargo using rustup:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
cargo install cargo-binstallInitial setup to install dependencies for Vite+:
just initIf you use Nix with flakes enabled, you can use the dev shell defined in flake.nix instead of installing the tools above manually:
nix develop
just initKnown limitation: on NixOS, the browser-mode cases of just snapshot-test do not run because the Chromium binary downloaded by playwright install cannot start without its system libraries. These cases are covered by CI; every other check works in the dev shell.
If you haven't installed Node.js and pnpm, we recommend installing them with Vite+:
$env:VP_NODE_MANAGER = "yes"
$env:VP_PM_MANAGER = "yes"
irm https://viteplus.dev/install.ps1 | iexAfter installing Vite+, restart your terminal to activate the node and pnpm shims.
You'll also need Just and CMake. You can install them using winget:
winget install Casey.Just Kitware.CMakeInstall Rust & Cargo from rustup.rs, then install cargo-binstall:
cargo install cargo-binstallInitial setup to install dependencies for Vite+:
just initNote: Run commands in PowerShell or Windows Terminal. Some commands may require elevated permissions.
To create a release build of Vite+ and all upstream dependencies, run:
just buildPre-commit checks are opt-in. Run vp config after a build to install the hook dispatcher; each commit then runs vp staged on staged files. vp staged loads its config from vite.config.ts and needs the built JS packages, so build first. Opt out with vp hooks disable, or skip one run with VP_GIT_HOOKS=0. If husky was active in your clone before, run git config --unset core.hooksPath once first.
pnpm bootstrap-cli
vp --versionThis builds all packages, compiles the Rust vp binary, and installs the CLI to ~/.vite-plus.
To switch back to a release version, use vp upgrade --force (current points to local-dev-* but the binary version may still match the release, so --force is needed)
vp upgrade --forceAutomated tests don't cover everything. Complex flows such as prompts, pickers, and scaffolding are also worth validating by running your work-in-progress CLI inside a real Vite+ project.
First, understand how vp picks which vite-plus to run: for JS-backed commands (such as vp create), the global vp binary resolves vite-plus from the project's node_modules first and only falls back to the global installation in ~/.vite-plus. If your test project has vite-plus installed from npm, pnpm bootstrap-cli alone will not make it run your local code.
Build the local CLI package after each change:
pnpm -F vite-plus build # TypeScript + native NAPI binding
pnpm -F vite-plus build-ts # faster, when only TypeScript changedLink your checkout into the test project. The global vp then delegates to the project-local CLI, and re-entrant vp sub-commands (for example vp create running vp install and vp fmt after scaffolding) resolve back to the same linked checkout:
cd /path/to/test-project
pnpm link /path/to/vite-plus/packages/cli
vp create # now runs your local checkoutVerify the link with ls -l node_modules/vite-plus (it should be a symlink into your checkout). Notes:
- pnpm records the link as a
vite-plus: link:...override (inpnpm-workspace.yamlfor workspace projects, otherwise underpnpm.overridesinpackage.json), so it survives later installs. Don't commit that override in the test project. pnpm linkmay also add apackageManagerfield to the test project'spackage.json; revert it if unwanted.- Undo with
pnpm unlink vite-plus, or remove the override and runpnpm install.
pnpm link swaps the code inside an existing project, but vp migrate and vp create pin the exact CLI version and then install it. Use the local npm registry to make your built checkout available to these commands.
pnpm build # the served packages are built artifacts; rebuild after JS changes
# One-shot: wrap any command (from the project you want to migrate)
cd /path/to/test-project
node /path/to/vite-plus/packages/tools/src/local-npm-registry.ts --pack -- vp migrate --no-interactive
node /path/to/vite-plus/packages/tools/src/local-npm-registry.ts --pack -- vp create vite:application --no-interactive
# Or keep a server running for repeated commands (from the vite-plus checkout)
pnpm local-registry --pack --serve
# copy the printed `export ...` lines into the shell where you run vppnpm local-registry:ps lists any registry processes still running; pnpm local-registry:kill stops them all and removes their leftover temp caches. See the tool documentation for implementation details and test integration.
pnpm link only swaps the JS side; the vp binary on PATH (and the Rust-backed commands it handles directly, such as package-manager commands) is still the installed binary. For changes to the Rust global CLI (crates/), follow the source installation steps, and combine with pnpm link when the change spans both layers.
You can run this command to build, test and check if there are any snapshot changes:
pnpm bootstrap-cli && pnpm test && git statusCLI output and interactive flows (prompts, pickers, keystrokes, ctrl-c) are tested with the PTY snapshot suite in crates/vp_cli_snapshots/. Every step runs in a real pseudo-terminal; snapshots are Markdown files compared with real pass/fail semantics. Write new CLI tests here, one fixture directory per scenario with a snapshots.toml declaring the cases.
# Build vp and run the whole suite
just snapshot-test
# Filter by trial name substring
just snapshot-test create
# Record or accept snapshot changes, then review the .md diffs like code
UPDATE_SNAPSHOTS=1 just snapshot-test createThe full case/step/interaction reference (including the vpt helper tool and milestone conventions for interactive tests) lives in crates/vp_cli_snapshots/tests/cli_snapshots/README.md; the design rationale is in rfcs/interactive-snapshot-tests.md.
Keep pull requests small and focused. Split changes that can be reviewed and merged independently into separate PRs. If one change depends on another, submit the dependent PR after its prerequisite has merged.
Note
Upstream dependencies only need to be updated when an "upgrade upstream dependencies" pull request is merged.
To sync the latest upstream dependencies such as Rolldown and Vite, run:
pnpm tool sync-remote
just buildIf you are using macOS, add your terminal app (Ghostty, iTerm2, Terminal, …) to the approved "Developer Tools" apps in the Privacy panel of System Settings and restart your terminal app. Your Rust builds will be about ~30% faster.