{"owner":"voidzero-dev","repo":"vite-plus","hasSkills":true,"totalSkillsCount":21,"totalTokensCount":28179,"categories":["subagent-persona","anthropic-skill","root-instruction","claude-rule","plugin-manifest"],"hasMcp":false,"mcpConfig":null,"found":[".claude/agents/cargo-workspace-merger.md",".claude/agents/monorepo-architect.md",".claude/skills/add-ecosystem-ci/SKILL.md",".claude/skills/bump-vite-task/SKILL.md",".claude/skills/release-manager/SKILL.md",".claude/skills/spawn-process/SKILL.md",".claude/skills/sync-tsdown-cli/SKILL.md",".claude/skills/sync-upstream-cli-help/SKILL.md",".claude/skills/sync-upstream-dependency-docs/SKILL.md",".claude/skills/test-pkg-pr-new-migrate/SKILL.md",".claude/skills/verify-interactive-cli/SKILL.md","AGENTS.md","CLAUDE.md","packages/cli/AGENTS.md","packages/cli/README.md","packages/cli/src/migration/migrator/README.md","packages/cli/templates/generator/README.md","packages/cli/templates/monorepo/README.md","packages/create-vite-plus/README.md","packages/prompts/README.md","packages/tools/README.md"],"skills":{".claude/agents/cargo-workspace-merger.md":"---\nname: cargo-workspace-merger\ndescription: \"Use this agent when you need to merge one Cargo workspace into another, specifically when integrating a subproject's crates and dependencies into a root workspace. This includes tasks like: adding crate path references to workspace members, merging workspace dependency definitions while avoiding duplicates, and ensuring only production dependencies (not unnecessary dev dependencies) are included.\\\\n\\\\n<example>\\\\nContext: The user wants to integrate the rolldown project into their existing Cargo workspace.\\\\nuser: \\\"I need to merge the rolldown Cargo workspace into our root workspace\\\"\\\\nassistant: \\\"I'll use the cargo-workspace-merger agent to handle this integration. This involves analyzing both Cargo.toml files, identifying the crates to add, and merging the necessary dependencies.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\\\\n\\\\n<example>\\\\nContext: The user has cloned a Rust project as a subdirectory and wants to integrate it.\\\\nuser: \\\"Can you add all the crates from ./external-lib into our workspace?\\\"\\\\nassistant: \\\"I'll launch the cargo-workspace-merger agent to analyze the external library's workspace structure and merge it into your root Cargo.toml.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\"\nmodel: opus\ncolor: yellow\n---\n\nYou are an expert Rust build system engineer specializing in Cargo workspace management and dependency resolution. You have deep knowledge of Cargo.toml structure, workspace inheritance, and dependency deduplication strategies.\n\n## Your Primary Mission\n\nMerge a child Cargo workspace (located in a subdirectory) into a parent root Cargo workspace. This involves two main tasks:\n\n1. **Adding crate references**: Add all crates from the child workspace to the root workspace's `[workspace.dependencies]` section with proper path references.\n\n2. **Merging workspace dependencies**: Combine the child workspace's `[workspace.dependencies]` with the root's dependencies, ensuring no duplicates and only including dependencies actually used by the crates being merged.\n\n## Step-by-Step Process\n\n### Step 1: Analyze the Child Workspace\n\n- Read the child workspace's `Cargo.toml` (e.g., `./rolldown/Cargo.toml`)\n- Identify all workspace members from the `[workspace.members]` section\n- Extract all `[workspace.dependencies]` definitions\n\n### Step 2: Identify Crates to Add\n\n- For each workspace member, locate its `Cargo.toml`\n- Extract the crate name from `[package].name`\n- Build a list of path references in the format: `crate_name = { path = \"./child/crates/crate_name\" }`\n\n### Step 3: Analyze Dependency Usage\n\n- For each crate in the child workspace, read its `Cargo.toml`\n- Collect all dependencies from `[dependencies]`, `[dev-dependencies]`, and `[build-dependencies]`\n- Focus on dependencies that reference `workspace = true` - these need the workspace-level definition\n- Create a set of actually-used workspace dependencies\n\n### Step 4: Filter and Merge Dependencies\n\n- From the child's `[workspace.dependencies]`, only include those that are actually used by the crates\n- Check for conflicts with existing root workspace dependencies:\n  - Same dependency, same version: Skip (already exists)\n  - Same dependency, different version: Flag for manual resolution and suggest keeping the newer version\n- Exclude dev-only dependencies that aren't needed for the merged crates\n\n### Step 5: Update Root Cargo.toml\n\n- Add all crate path references to `[workspace.dependencies]`\n- Add filtered workspace dependencies to `[workspace.dependencies]`\n- Maintain alphabetical ordering within sections for cleanliness\n- Preserve any existing comments and formatting\n\n## Output Format\n\nProvide:\n\n1. A summary of crates being added\n2. A summary of dependencies being merged\n3. Any conflicts or issues requiring manual attention\n4. The exact additions to make to the root `Cargo.toml`\n\n## Quality Checks\n\n- Verify all paths exist before adding references\n- Ensure no duplicate entries are created\n- Validate that merged dependencies don't break existing crates\n- After modifications, suggest running `cargo check --workspace` to verify the merge\n- Use highest compatible semver versions (if not pinned) and merge features in crates\n\n## Important Considerations\n\n- Use `vt_path` types for path operations as per project conventions\n- Dependencies with `path` references in the child workspace may need path adjustments\n- Feature flags on dependencies must be preserved\n- Optional dependencies must maintain their optional status\n- If a dependency exists in both workspaces with different features, merge the feature lists\n\n### Workspace Package Inheritance\n\nChild crates may inherit fields from `[workspace.package]` using `field.workspace = true`. Common inherited fields include:\n\n- `homepage`\n- `repository`\n- `license`\n- `edition`\n- `authors`\n- `rust-version`\n\n**Important**: If the child workspace's `[workspace.package]` defines fields that the root workspace does not, you must add those fields to the root workspace's `[workspace.package]` section. Otherwise, crates that inherit these fields will fail to build with errors like:\n\n```\nerror inheriting `homepage` from workspace root manifest's `workspace.package.homepage`\nCaused by: `workspace.package.homepage` was not defined\n```\n\n**Steps to handle this**:\n\n1. Read the child workspace's `[workspace.package]` section\n2. Compare with the root workspace's `[workspace.package]` section\n3. Add any missing fields to the root workspace (use the root project's own values, not the child's)\n\n## Error Handling\n\n- If a crate path doesn't exist, report it clearly and skip\n- If Cargo.toml parsing fails, provide the specific error\n- If version conflicts exist, list all conflicts before proceeding and ask for guidance\n\n### Crates with Compile-Time Environment Variables\n\nSome crates use `env!()` macros that require compile-time environment variables set via `.cargo/config.toml`. These crates often have `relative = true` paths that only work when building from their original workspace root.\n\n**Example**: `rolldown_workspace` uses `env!(\"WORKSPACE_DIR\")` which is set in `rolldown/.cargo/config.toml`.\n\n**How to handle**:\n\n1. Check child workspace's `.cargo/config.toml` for `[env]` section\n2. If crates use these env vars with `relative = true`, copy those env vars to root `.cargo/config.toml` with paths adjusted to point to the child workspace directory\n3. Example: If child has `WORKSPACE_DIR = { value = \"\", relative = true }`, root should have `WORKSPACE_DIR = { value = \"child-dir\", relative = true }`\n",".claude/agents/monorepo-architect.md":"---\nname: monorepo-architect\ndescription: Use this agent when you need architectural guidance for monorepo tooling, particularly for reviewing code organization, module boundaries, and ensuring proper separation of concerns in Rust/Node.js projects. This agent should be invoked after implementing new features or refactoring existing code to validate architectural decisions and placement of functionality.\\n\\nExamples:\\n- <example>\\n  Context: The user has just implemented a new caching mechanism for the monorepo task runner.\\n  user: \"I've added a new caching system to handle task outputs\"\\n  assistant: \"I'll use the monorepo-architect agent to review the architectural decisions and ensure the caching logic is properly placed within the module structure.\"\\n  <commentary>\\n  Since new functionality was added, use the monorepo-architect agent to review the code architecture and module boundaries.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is refactoring the task dependency resolution system.\\n  user: \"I've refactored how we resolve task dependencies across packages\"\\n  assistant: \"Let me invoke the monorepo-architect agent to review the refactored code and ensure proper separation of concerns.\"\\n  <commentary>\\n  After refactoring core functionality, use the monorepo-architect agent to validate architectural decisions.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is adding cross-package communication features.\\n  user: \"I've implemented a new IPC mechanism for packages to communicate during builds\"\\n  assistant: \"I'll use the monorepo-architect agent to review where this IPC logic lives and ensure it doesn't create inappropriate cross-module dependencies.\"\\n  <commentary>\\n  When adding features that span multiple modules, use the monorepo-architect agent to prevent architectural violations.\\n  </commentary>\\n</example>\nmodel: opus\ncolor: purple\n---\n\nYou are a senior software architect with deep expertise in Rust and Node.js ecosystems, specializing in monorepo tooling and build systems. You have extensively studied and analyzed the architectures of nx, Turborepo, Rush, and Lage, understanding their design decisions, trade-offs, and implementation patterns.\n\nYour primary responsibility is to review code architecture and ensure that functionality is properly organized within the codebase. You focus on:\n\n**Core Architectural Principles:**\n\n- Single Responsibility: Each module, file, and function should have one clear purpose\n- Separation of Concerns: Business logic, I/O operations, and configuration should be clearly separated\n- Module Boundaries: Enforce clean interfaces between modules, preventing tight coupling\n- Dependency Direction: Dependencies should flow in one direction, typically from high-level to low-level modules\n\n**When reviewing code, you will:**\n\n1. **Analyze Module Structure**: Examine where new functionality has been placed and determine if it belongs there based on the module's responsibility. Look for code that crosses logical boundaries or mixes concerns.\n\n2. **Identify Architectural Violations**:\n   - Cross-module responsibilities where one module is doing work that belongs to another\n   - Circular dependencies or bidirectional coupling\n   - Business logic mixed with I/O operations\n   - Configuration logic scattered across multiple modules\n   - Violation of the dependency inversion principle\n\n3. **Suggest Proper Placement**: When you identify misplaced functionality, provide specific recommendations:\n   - Identify the correct module/file where the code should reside\n   - Explain why the current placement violates architectural principles\n   - Suggest how to refactor without breaking existing functionality\n   - Consider the impact on testing and maintainability\n\n4. **Reference Industry Standards**: Draw from your knowledge of nx, Turborepo, Rush, and Lage to:\n   - Compare architectural decisions with proven patterns from these tools\n   - Highlight when a different approach might be more scalable or maintainable\n   - Suggest battle-tested patterns for common monorepo challenges\n\n5. **Focus on Rust/Node.js Best Practices**:\n   - In Rust: Ensure proper use of ownership, traits for abstraction, and module organization\n   - In Node.js: Validate CommonJS/ESM module patterns, async patterns, and package boundaries\n   - For interop: Review FFI boundaries and data serialization approaches\n\n**Review Methodology:**\n\n1. Start by understanding the intent of the recent changes\n2. Map out the affected modules and their responsibilities\n3. Identify any code that seems out of place or creates inappropriate coupling\n4. Provide a prioritized list of architectural concerns (critical, important, minor)\n5. For each concern, explain the principle being violated and suggest a concrete fix\n\n**Output Format:**\n\nStructure your review as:\n\n- **Summary**: Brief overview of architectural health\n- **Critical Issues**: Must-fix architectural violations that will cause problems\n- **Recommendations**: Suggested improvements with rationale\n- **Positive Patterns**: Acknowledge well-architected decisions\n- **Comparison Notes**: When relevant, note how similar problems are solved in nx/Turborepo/Rush/Lage\n\nYou are pragmatic and understand that perfect architecture must be balanced with delivery speed. Focus on issues that will genuinely impact maintainability, testability, or scalability. Avoid nitpicking and recognize when 'good enough' is appropriate for the current stage of the project.\n\nWhen you lack context about the broader system, ask clarifying questions rather than making assumptions. Your goal is to ensure the codebase remains maintainable and follows established architectural patterns while evolving to meet new requirements.\n",".claude/skills/add-ecosystem-ci/SKILL.md":"---\nname: add-ecosystem-ci\ndescription: Add a new ecosystem-ci test case for testing real-world projects against vite-plus\nallowed-tools: Bash, Read, Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Add Ecosystem-CI Test Case\n\nAdd a new ecosystem-ci test case following this process:\n\n## Step 1: Get Repository Information\n\nAsk the user for the GitHub repository URL if not provided as argument: $ARGUMENTS\n\nUse GitHub CLI to get repository info:\n\n```bash\ngh api repos/OWNER/REPO --jq '.default_branch'\ngh api repos/OWNER/REPO/commits/BRANCH --jq '.sha'\n```\n\n## Step 2: Auto-detect Project Configuration\n\n### 2.1 Check for Subdirectory\n\nFetch the repository's root to check if the main package.json is in a subdirectory (like `web/`, `app/`, `frontend/`).\n\n### 2.2 Check if Project Already Uses Vite-Plus\n\nCheck the project's root `package.json` for `vite-plus` in `dependencies` or `devDependencies`. If the project already uses vite-plus, set `forceFreshMigration: true` in `repo.json`. This tells `patch-project.ts` to set `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping with \"already using Vite+\".\n\n### 2.3 Auto-detect Commands from GitHub Workflows\n\nFetch the project's GitHub workflow files to detect available commands:\n\n```bash\n# List workflow files\ngh api repos/OWNER/REPO/contents/.github/workflows --jq '.[].name'\n\n# Fetch workflow content (for each .yml/.yaml file)\ngh api repos/OWNER/REPO/contents/.github/workflows/ci.yml --jq '.content' | base64 -d\n```\n\nLook for common patterns in workflow files:\n\n- `pnpm run <command>` / `npm run <command>` / `yarn <command>`\n- Commands like: `lint`, `build`, `test`, `type-check`, `typecheck`, `format`, `format:check`\n- Map detected commands to `vp` equivalents: `vp run lint`, `vp run build`, etc.\n\n### 2.4 Ask User to Confirm\n\nPresent the auto-detected configuration and ask user to confirm or modify:\n\n- Which directory contains the main package.json? (auto-detected or manual)\n- What Node.js version to use? (22 or 24, try to detect from workflow)\n- Which commands to run? (show detected commands as multi-select options)\n- Which OS to run on? (both, ubuntu-only, windows-only) - default: both\n\n## Step 3: Update Files\n\n1. **Add to `ecosystem-ci/repo.json`**:\n\n   ```json\n   {\n     \"project-name\": {\n       \"repository\": \"https://github.com/owner/repo.git\",\n       \"branch\": \"main\",\n       \"hash\": \"full-commit-sha\",\n       \"forceFreshMigration\": true // only if project already uses vite-plus\n     }\n   }\n   ```\n\n2. **Add to `.github/workflows/e2e-test.yml`** matrix:\n   ```yaml\n   - name: project-name\n     node-version: 24\n     directory: web # only if subdirectory is needed\n     command: |\n       vp run lint\n       vp run build\n   ```\n\n## Step 4: Verify\n\n### 4.1 Build fresh tgz packages\n\nAlways rebuild tgz packages from latest source to avoid using stale cached versions:\n\n```bash\n# Rebuild the global CLI first (includes Rust binary + NAPI binding)\npnpm bootstrap-cli\n\n# Pack fresh tgz files into tmp/tgz/\nrm -rf tmp/tgz && mkdir -p tmp/tgz\ncd packages/core && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/test && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/cli && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\nls -la tmp/tgz\n```\n\n### 4.2 Clone and test locally\n\n```bash\nnode ecosystem-ci/clone.ts project-name\n```\n\n### 4.3 Patch and run commands\n\n```bash\n# Run from the ecosystem-ci temp directory\ncd $(node -e \"const os=require('os'); console.log(os.tmpdir() + '/vite-plus-ecosystem-ci')\")\n\n# Migrate the project (uses tgz files from tmp/tgz/)\nnode /path/to/vite-plus/ecosystem-ci/patch-project.ts project-name\n\n# Run the configured commands\ncd project-name\nvp run build\n```\n\n3. **Add OS exclusion to `.github/workflows/e2e-test.yml`** (if not running on both):\n\n   For ubuntu-only:\n\n   ```yaml\n   exclude:\n     - os: windows-latest\n       project:\n         name: project-name\n   ```\n\n   For windows-only:\n\n   ```yaml\n   exclude:\n     - os: ubuntu-latest\n       project:\n         name: project-name\n   ```\n\n## Important Notes\n\n- The workflow matrix `directory` field is optional - only add it if the project commands must run in a subdirectory (like a workspace member)\n- `patch-project.ts` always runs `vp migrate` and `vp install` at the clone root; `vp migrate` rejects workspace member targets\n- `forceFreshMigration` is required for projects that already have `vite-plus` in their package.json — it sets `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping\n- OS exclusions are added to the existing `exclude` section in the workflow matrix\n",".claude/skills/bump-vite-task/SKILL.md":"---\nname: bump-vite-task\ndescription: Bump vite-task git dependency to the latest main commit. Use when you need to update the vite-task git-dependency crates (vt, fspy, vt_pty_terminal_test, and friends; the authoritative set lives in Cargo.toml) in vite-plus.\nallowed-tools: Read, Grep, Glob, Edit, Bash, Agent, WebFetch\n---\n\n# Bump vite-task to Latest Main\n\nUpdate the vite-task git dependency in `Cargo.toml` to the latest commit on the vite-task main branch, fix any breaking changes, and create a PR.\n\n## Steps\n\n### 1. Get current and target commits\n\n- Read `Cargo.toml` and find the current `rev = \"...\"` for any vite-task git dependency; enumerate the full set with `grep 'voidzero-dev/vite-task' Cargo.toml` instead of trusting a hardcoded list. They all share the same revision.\n- Get the latest commit hash on vite-task's main branch:\n  ```bash\n  git ls-remote https://github.com/voidzero-dev/vite-task.git refs/heads/main\n  ```\n\n### 2. Update Cargo.toml\n\n- Replace **all** occurrences of the old commit hash with the new one in `Cargo.toml`. The set of crates sharing the vite-task revision changes over time; the grep from step 1 is the authoritative list, so update every entry it returns.\n- The commented `[patch.\"https://github.com/voidzero-dev/vite-task.git\"]` section near the bottom of `Cargo.toml` mirrors the same crates for local vite-task development; keep it in sync when crates are added or removed (a plain rev bump does not touch it).\n\n### 3. Ensure upstream dependencies are cloned\n\n- `cargo check` requires the `./rolldown` and `./vite` directories to exist (many workspace path dependencies point to `./rolldown/crates/...`).\n- Locally, clone them using the commit hashes from `packages/tools/.upstream-versions.json`.\n- CI handles this automatically via the `.github/actions/clone` action.\n\n### 4. Verify compilation\n\n- Run `cargo check` to ensure the new vite-task compiles without errors.\n- If there are compilation errors, these are **breaking changes** from vite-task. Fix them in the vite-plus codebase (the consuming side), not in vite-task.\n- Common breaking changes include: renamed functions/methods, changed function signatures, new required fields in structs, removed public APIs.\n\n### 5. Run tests\n\n- Run `cargo test -p vp_command -p vp_error -p vp_pm_cli -p vp_js_runtime -p vp_migration -p vp_shared -p vp_static_config -p vite-plus-cli -p vp_global_cli` to run the vite-plus crate tests.\n- Note: Some tests require network access (e.g., `vp_pm_cli::package_manager` tests, `vp_global_cli::commands::env` tests). These may fail in sandboxed environments. Verify they also fail on the main branch before dismissing them.\n- Note: `cargo test -p vt` will NOT work because vt is a git dependency, not a workspace member.\n- The PTY snapshot suite (`crates/vp_cli_snapshots`) is excluded from `just test`; it is covered in step 6.\n\n### 6. Update snapshot tests\n\nvite-task changes often affect CLI output, which means snapshot tests need updating. Common output changes:\n\n- **Status icons**: e.g., cache hit/miss indicators may change\n- **New CLI options**: e.g., new flags added to `vp run` that show up in help output\n- **Cache behavior messages**: e.g., new summary lines about cache status\n- **Task output formatting**: e.g., step numbering, separator lines\n\n**PTY snapshot suite (`crates/vp_cli_snapshots`):**\n\n- A bump can break it two ways: runner compilation (it consumes vite-task's `vt_pty_terminal_test`, `vt_pty_terminal_test_client`, and `vt_snapshot_test` crates directly, so their API changes surface here; fix in the runner) and recorded CLI output.\n- Update output locally with real assertions: `UPDATE_SNAPSHOTS=1 just snapshot-test`, then review the `.md` diffs like code. Without a built `packages/cli/dist`, run the global flavor only: `VP_SNAP_SKIP_FLAVORS=local UPDATE_SNAPSHOTS=1 just snapshot-test`.\n- Windows runs in the `CLI snapshot test (Windows)` CI job via a cross-compiled nextest archive; snapshots are OS-shared, so a Windows-only diff there means a redaction gap, not a re-record.\n- Reference: `crates/vp_cli_snapshots/tests/cli_snapshots/README.md`.\n\n### 7. Review changelog and update docs\n\n- Fetch the vite-task `CHANGELOG.md` diff between old and new commits to see what changed:\n  ```\n  https://raw.githubusercontent.com/voidzero-dev/vite-task/<new-hash>/CHANGELOG.md\n  ```\n- Review each changelog entry and determine if it affects user-facing behavior: new CLI options, changed defaults, new config fields, removed features, etc.\n- The changelog contains links to the corresponding vite-task PRs. For complex changes, check the PR description and code diff (especially any docs changes in the PR) to understand the full scope of the change.\n- If user-facing changes are found, update the relevant docs in `docs/` (e.g., `docs/guide/`, `docs/config/`).\n- Common doc updates include:\n  - **New CLI flags/options**: Update the relevant config doc (e.g., `docs/config/run.md`, `docs/config/build.md`)\n  - **New features or commands**: Add or update the relevant guide page (e.g., `docs/guide/cache.md`)\n  - **Changed defaults or behavior**: Update any docs that describe the old behavior\n  - **Removed/deprecated options**: Remove or mark as deprecated in the relevant docs\n- If no user-facing changes are found, skip this step.\n\n### 8. Create the PR\n\n- Commit message: `chore: bump vite-task to <short-hash>`\n- PR title: `chore: bump vite-task to <short-hash>`\n- PR body: Link to vite-task CHANGELOG.md diff between old and new commits:\n  ```\n  https://github.com/voidzero-dev/vite-task/compare/<old-hash>...<new-hash>#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed\n  ```\n\n### 9. Verify CI\n\nAfter creating the PR, automatically watch CI without asking the user first. Ensure the `done` check passes. Key checks to monitor:\n\n- **Lint**: Clippy and format checks\n- **Test** (Linux, Mac, Windows): Rust unit tests\n- **CLI snapshot test** (Linux, Mac, Windows): the PTY snapshot suite - most likely to fail on a vite-task bump (runner compiles against vite-task crates AND asserts CLI output)\n- **Run task**: Task runner integration tests\n- **Security Analysis**: Fix any `cargo deny` advisories. It runs only when `Cargo.lock` changes, so a green `main` does not mean the lockfile is clean.\n\nThe only **required** status check for merging is `done`, which aggregates the other checks (excluding Security Analysis).\n\n## Notes\n\n- Building the full CLI locally (`pnpm bootstrap-cli`) requires the rolldown Node.js package to be built first, which is complex. Prefer the global-only snapshot command above or CI when no local CLI build is available.\n- `Cargo.lock` is automatically updated by cargo when you change the revision in `Cargo.toml`.\n",".claude/skills/release-manager/SKILL.md":"---\nname: release-manager\ndescription: Run the standard vite-plus release process end-to-end as the release manager. Covers preparing the release PR, syncing the NAPI binding version, writing the categorized changelog, smoke-testing via a preview build, handling release-branch CI failures, merging, and post-release verification and announcements. Use when asked to cut, prepare, or manage a vite-plus release (e.g. \"release v0.2.3\", \"act as release manager\").\nallowed-tools: Bash, Read, Edit, Write, Grep, Glob, WebFetch\n---\n\n# Vite+ Release Manager\n\nRun a standard vite-plus release from version bump to published announcement. Any maintainer with repo write access can follow this; the only extra privilege needed is approval rights on the `release` GitHub environment (step 7).\n\n## Usage\n\n```\n/release-manager                    # start a new release: ask for the target version, begin at step 1\n/release-manager X.Y.Z              # start a new release for that version\n/release-manager <PR URL or #N>     # take over an in-flight release\n```\n\nWhen given a release PR (URL or number), do not start from step 1. First audit the release's current state, then continue from the earliest unfinished step:\n\n- Is the binding version synced? (step 2: `grep -c \"'<prev>'\" packages/cli/binding/index.cjs` on the release branch)\n- Do the release version examples in the migration guide and the setup, migration, and upgrade prompts match `packages/cli/package.json`? (step 2)\n- Is the PR description still the `prepare_release` boilerplate, or already a categorized changelog? (step 3)\n- Is a preview build present and for the current head? (step 4)\n- Does `main` have commits the release branch lacks? (`git log origin/release/vX.Y.Z..origin/main`, step 5)\n- What is CI status? (`gh pr checks <PR#>`, step 5)\n- Already merged? Check the Release workflow (`gh run list --workflow Release --repo voidzero-dev/vite-plus`) and whether the GitHub release body is still the generated stub, then continue at step 7 or 8.\n\nReport the detected state before making changes, so the previous release manager's work is not redone or overwritten.\n\nBefore post-release work, fetch `origin/main` and read its copy of this skill (`git show origin/main:.claude/skills/release-manager/SKILL.md`). A checkout left behind after the merge can contain superseded release or announcement instructions.\n\n## Pipeline overview\n\n1. `Prepare Release` workflow bumps versions and opens the release PR (`release/vX.Y.Z` -> `main`).\n2. Release manager: sync `binding/index.cjs` and the release versions in the documentation and prompts, write the changelog PR description, offer the preview-build smoke test (recommend it when the release has more than 10 commits since the previous tag), get CI green.\n3. Merging the PR pushes a `packages/cli/package.json` change to `main`, which triggers `release.yml`: build, manual approval gate, npm publish, GitHub release, Docker image, Discord notification.\n4. Release manager: polish the GitHub release notes, verify installs, announce.\n\nCanonical sources: `.github/workflows/prepare_release.yml`, `.github/workflows/release.yml`, `.github/workflows/publish-preview.yml`.\n\n## 1. Start the release\n\n```bash\ngh workflow run prepare_release.yml --repo voidzero-dev/vite-plus -f version=X.Y.Z\n```\n\nThe workflow bumps `packages/cli/package.json`, `packages/core/package.json`, `packages/cli/binding/Cargo.toml`, and `crates/vp_global_cli/Cargo.toml`, refreshes `Cargo.lock`, and opens a PR titled `release: vX.Y.Z` from branch `release/vX.Y.Z`. The PR body ends with `Merging this PR will trigger the release workflow.` and that line must survive every later edit.\n\n## 2. Sync release versions (required every release)\n\n### NAPI binding\n\nNAPI bakes the package version into version checks in `packages/cli/binding/index.cjs` (26+ sites). `prepare_release` bumps `package.json` but does not regenerate this file, so CI's `Ensure no unexpected file changes after build` step in the `CLI E2E test` job fails until it is synced. Do this immediately; do not wait for CI to fail.\n\n```bash\ngit fetch origin release/vX.Y.Z && git checkout release/vX.Y.Z\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs          # non-zero means sync needed\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs\n```\n\nApply two whole-file text replacements (`<prev>` is the previous release version, e.g. `0.2.1`):\n\n1. `'<prev>'` -> `'<curr>'`\n2. `expected <prev> but got` -> `expected <curr> but got`\n\nThen confirm the replacement took; both counts must now be zero:\n\n```bash\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs                 # 0\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs  # 0\n```\n\nDo not regenerate via a full build; the text replace is deterministic and byte-identical to what `napi build` would produce for a version-only bump. Commit with this exact message shape (it makes `git log --grep` find the sync across releases) and push:\n\n```\nchore(release): sync binding/index.cjs version to <curr>\n\nNAPI bakes the package.json version into binding/index.cjs version\nchecks. The prepare_release workflow bumps package.json but does not\nregenerate this file, so the CI build's regeneration step produces a\ndiff that the post-build no-unexpected-changes guard rejects.\n```\n\n### Documentation and prompts\n\nUse the version in the release branch's `packages/cli/package.json` as the target release version in these files:\n\n- `docs/guide/migrate.md`: pnpm and npm migration command examples and matching release prose.\n- `docs/.vitepress/theme/data/migration-prompts.ts`: `setupPrompt`, `migrationPrompt`, `upgradePrompt`, and their shared instructions, including command examples and matching release prose. `CopyPrompt` uses `setupPrompt` on both the homepage and Getting Started guide.\n\nUpdate every `--package=vite-plus@<curr>` pin and the corresponding `For the <curr> release` and `Replace <curr>` text. Keep an exact version; do not replace it with a placeholder, a major range, or `latest`.\n\nPreserve historical versions such as the migration's source version and the release that introduced a breaking change. Leave Node.js requirements, bundled tool versions, and preview-registry instructions unchanged unless their requirements change.\n\nCommit these updates on the release branch with the binding sync or in a separate release-version sync commit. Recheck both files and the binding after a target-version change or a merge from `main`. Before merging, confirm that the guide and all three prompts use the target release in both package-manager commands and their matching prose, then run `git diff --check`.\n\nOnly these release-version sync commits go directly on the release branch. Everything else goes through `main` (see step 5).\n\n## 3. Write the release PR description\n\nThe release tag does not exist yet, so read release files from the PR head branch and generate notes against `main`:\n\n```bash\ngit fetch --tags && git tag --sort=-version:refname | head -5   # find <prev>\ngit log --oneline v<prev>..origin/main\ngh api repos/voidzero-dev/vite-plus/releases/generate-notes \\\n  -f tag_name=v<curr> -f previous_tag_name=v<prev> -f target_commitish=main\n\ngit show origin/release/v<curr>:packages/core/package.json          # bundledVersions: vite, rolldown, tsdown\ngit show origin/release/v<curr>:packages/tools/.upstream-versions.json  # vite/rolldown commit hashes\ngit show origin/release/v<curr>:pnpm-workspace.yaml                 # vitest/oxlint/oxlint-tsgolint/oxfmt catalog pins\n```\n\n### Structure\n\n```markdown\n<One or two sentences on the release theme. Do not repeat the PR title as an opener line; GitHub renders the title directly above the body, and step 8 would only strip it again. When a blog post accompanies the release, read it first (via its preview URL if not yet deployed), align the theme with it, and link the final URL here even if that URL is not live yet.>\n\n### Breaking Changes\n\n### Features\n\n### Fixes & Enhancements\n\n### Refactor\n\n### Docs\n\n### Chore\n\n### Bundled Versions\n\n### Upgrade\n\n### New Contributors\n\n**Full Changelog**: https://github.com/voidzero-dev/vite-plus/compare/v<prev>...v<curr>\n\n---\n\nMerging this PR will trigger the release workflow.\n```\n\n### Categorization rules\n\n- Every PR from `generate-notes` appears exactly once, except fully reverted changes described below and bot-authored PRs that carry nothing for a user to read or act on (a docs stats refresh, a badge update). Keep bot PRs that do change what users get, such as the upstream dependency upgrades. Record each omitted PR and its reason when reporting validation counts.\n- **Breaking Changes goes first, above Features, and only when the release has one.** A rename is breaking only when the old name stops working; if a deprecated alias is retained it is not breaking, so keep the two in different sections rather than merging them into one entry. Give each breaking entry an old -> new table when several names change, plus one line telling readers where to update (shell profile, CI job, Dockerfile). Do not editorialize about the version number.\n- When several breaking changes affect different workflows, group them under short `####` headings. Explain the changed behavior and required action before each table; keep automatic migration steps separate from changes users must make manually.\n- **Describe the net change between the two released versions, not intra-cycle churn.** When several PRs touch the same area within one release (one narrows a behavior, a later one broadens it back), the reader only sees the delta from `v<prev>` to `v<curr>`; describe that once, listing every PR number, and do not narrate a regression that was introduced and then fixed inside the cycle. Apply this to the intro/theme sentence too.\n- If a change and its complete revert are both unreleased, omit both when they leave no net change. Remove sections with no remaining entries. A revert of behavior in the previous release still needs an entry.\n- `feat` -> Features, `fix` -> Fixes & Enhancements, `refactor` and `revert` -> Refactor (never Chore), `docs` -> Docs, `test` / `ci` / `chore` -> Chore.\n- **Place an entry by its user impact when the prefix disagrees.** A `fix` that gives an existing command new observable behavior (for example, starting to set environment variables for child processes) belongs in Features. A fix that makes a command reject input it used to drop silently stays in Fixes & Enhancements, not Breaking Changes.\n- `feat(docs)` goes in Docs when the user-facing surface is the docs site.\n- **Docs means the published docs site, not contributor files.** A `docs` commit that changes an RFC, `AGENTS.md`, the repo map, or a skill under `.claude/` belongs in Chore: a vite-plus user never reads those. Docs should hold only entries a reader could go and look at on the site or in the README.\n- **Describe behaviour, not resolution logic.** An entry states what a user now observes. Rules the implementation follows internally (target-selection signals, config precedence, detection order) belong in the RFC or the PR, not the changelog. If an entry needs a nested list to explain how a decision is reached, cut it down to the outcome.\n- **A breaking change needs its migration path.** State what existing installs or projects do by default, then how to move to the new behaviour deliberately, then what that costs. Link the guide rather than restating it, and say plainly when doing nothing is a valid choice.\n- There is no separate Highlights section. Lead Features with up to 5 new capabilities a vite-plus user will notice, plus security fixes, then the remaining features; the upstream upgrade entry stays last. Bug fixes go in Fixes & Enhancements even when they are prominent. Skip developer-tooling-only conveniences.\n- Entry format: `Description ([#N](https://github.com/voidzero-dev/vite-plus/pull/N)), by @author`. Describe the user-visible behavior, not the implementation. Group supporting implementation PRs under the user-visible change they enable instead of giving them separate entries. Never include defensive edge cases or internal mechanics unless users need them to use or understand the feature; use concrete behavior instead of internal UI taxonomy that needs extra context. For a fix to an intermittent failure, say that it was rare and when it happened, so the entry does not read as if it always failed. When a PR carries over a superseded PR from another author (its body says so, or `gh pr view N --json commits` lists their commits), credit both authors.\n- **Upstream dependency upgrade PRs** (`feat(deps): upgrade upstream dependencies`): consolidate all of them into one Features entry with net oldest-to-latest version changes (e.g. `vite 8.0.16 -> 8.1.2`), listing every PR number. Check the upgraded range for security fixes (search the upstream changelog for CVE/GHSA); if present, add a dedicated security entry quoting severity and linking the advisory. Cite only published advisories: a draft advisory that maintainer access lets you see is still embargoed, so keep it and its fix's security framing out of the notes and the announcement. When oxfmt or oxlint changed version, add one clause telling users the new versions can flag code that passed before, so they should run `vp fmt` after upgrading if their CI runs `vp check`; in ecosystem testing this is reliably the largest single class of post-upgrade CI failures. Put this entry last in Features as a parent bullet carrying the PR links and credits, with one sub-item per upgraded tool and one per user-facing consequence (new formatting or lint behavior, migration changes, compatibility notes); a single long line is hard to read.\n- **vite-task bumps** (`bump vite-task to <commit>`): expand the full rev range (compare `Cargo.toml` at `v<prev>` vs the release branch), run `git log <old>..<new>` in the local vite-task checkout, and read vite-task's `CHANGELOG.md` at the new commit for wording. Promote user-visible upstream changes into Features / Fixes with `[vite-task#N](https://github.com/voidzero-dev/vite-task/pull/N)` links, crediting the upstream PR author (`gh pr view N --repo voidzero-dev/vite-task --json author`). Cross-repo link format is `[vite-task#N]` / `[vite#N]`, not `[owner/repo#N]`.\n- New Contributors: copy from `generate-notes`, exclude bots (`renovate[bot]`, `voidzero-guard[bot]`, `github-actions[bot]`), list as inline `@mentions`. `generate-notes` only covers vite-plus, so also add first-time contributors from the expanded vite-task range: an author is new when `gh api -X GET search/issues -f q='repo:voidzero-dev/vite-task is:pr is:merged author:<login> merged:<<date of the range start>' -q .total_count` is 0. List them with the others, without a repository label, in the release notes and the Discord thanks line.\n\n### Bundled Versions table\n\n| Tool            | Version | Source                                                                  | Changelog                                                              |\n| --------------- | ------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| vite            | `X.Y.Z` | [`<short-sha>`](https://github.com/vitejs/vite/commit/<full-sha>)       | [X.Y.Z](https://github.com/vitejs/vite/releases/tag/vX.Y.Z)            |\n| rolldown        | `X.Y.Z` | [`<short-sha>`](https://github.com/rolldown/rolldown/commit/<full-sha>) | [X.Y.Z](https://github.com/rolldown/rolldown/releases/tag/vX.Y.Z)      |\n| tsdown          | `X.Y.Z` | [npm](https://npmx.dev/package/tsdown/v/X.Y.Z)                          | [X.Y.Z](https://github.com/rolldown/tsdown/releases/tag/vX.Y.Z)        |\n| vitest          | `X.Y.Z` | [npm](https://npmx.dev/package/vitest/v/X.Y.Z)                          | [X.Y.Z](https://github.com/vitest-dev/vitest/releases/tag/vX.Y.Z)      |\n| oxlint          | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint/v/X.Y.Z)                          | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxlint_vX.Y.Z) |\n| oxlint-tsgolint | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint-tsgolint/v/X.Y.Z)                 | [X.Y.Z](https://github.com/oxc-project/tsgolint/releases/tag/vX.Y.Z)   |\n| oxfmt           | `X.Y.Z` | [npm](https://npmx.dev/package/oxfmt/v/X.Y.Z)                           | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxfmt_vX.Y.Z)  |\n\nvite and rolldown are built from pinned commits, so link the commit. The npm-installed tools link to npmx.dev. The Changelog column links the upstream release notes for every version in the upgraded range, comma-separated (for example the two patch releases between the previous and new pin); write `unchanged` when the version did not move.\n\n### Style rules\n\n- No em dashes or en dashes anywhere in the title or body. Use commas, colons, or parentheses.\n- Keep the PR title plain (`release: vX.Y.Z`) and the GitHub release title plain (`vite-plus vX.Y.Z`); the theme goes in the opening sentence. Lead the opening theme with the most important user-visible behavior. Avoid vague benefit-only wording that the body must explain.\n- When naming a package version in prose or a heading, use one inline literal, `package@version`. Keep separate version columns in tables.\n- Link unfamiliar technical abbreviations to the relevant documentation section; keep the short abbreviation as the link text.\n- The Upgrade section is a `vp upgrade` code block.\n- Apply via a temp file, never a heredoc (heredoc quoting can escape backticks inside the table and break rendering):\n\n```bash\ngh pr edit <PR#> --repo voidzero-dev/vite-plus --title \"release: vX.Y.Z\" --body-file /tmp/pr-body.md\n```\n\n### Validate before finishing\n\n```bash\nBODY=$(gh pr view <PR#> --repo voidzero-dev/vite-plus --json body -q '.body')\n# every generate-notes PR present (minus documented omissions), none duplicated:\necho \"$BODY\" | grep -oE 'voidzero-dev/vite-plus/pull/[0-9]+' | sort -u | wc -l\necho \"$BODY\" | grep -oE '(vite-plus|vite-task)/pull/[0-9]+' | sort | uniq -d   # must be empty\necho \"$BODY\" | grep -nE '[—–]'                                                  # must be empty\necho \"$BODY\" | grep -c '\\\\`'                                                    # must be 0 (escaped backticks)\necho \"$BODY\" | tail -1                                                          # boilerplate closing line intact\n```\n\nDiff the body's PR numbers against `generate-notes` rather than only counting them: a count alone hides one missing entry offsetting one extra. Every number in the missing list must have a documented reason for omission.\n\n## 4. Preview build smoke test (before merging)\n\nThis step runs **after the changelog (step 3) is complete and before merging (step 6)**. **Always ask the release manager whether to run it, and ask about both levels explicitly** (the local `vp migrate` sweep, and the fork-PR CI validation below); never silently skip either, and do not add the label on your own.\n\nDecide what to recommend before you ask, by counting the commits the release actually contains:\n\n```bash\ngit rev-list --count v<prev>..origin/main\n```\n\n**Recommend running the smoke test when that count is above 10**, or when the release touches migrate/create behavior, package-manager or install-path handling, or the native bindings, whatever the count. Recommend skipping only for a release that is both small (10 commits or fewer) and clear of those areas. State the count and your recommendation in the question so the release manager can overrule it, and say roughly what it costs, since the full catalog runs for hours.\n\nIf the release manager approves, **read and follow [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md) first**, then validate against the **full ecosystem-ci catalog** (every runnable fork), not a single project.\n\nIf the release manager says yes:\n\n1. Add the `preview-build` label to the release PR to publish installable `0.0.0-commit.<head-sha>` builds through the registry bridge:\n\n   ```bash\n   gh pr edit <PR#> --repo voidzero-dev/vite-plus --add-label \"preview-build\"\n   ```\n\n2. Wait for the `Publish preview build` workflow run on the release branch to succeed (it packs the built package directories and registers the commit with the registry bridge, then comments the build info on the PR).\n3. Verify the build against every runnable project in the catalog with the `test-pkg-pr-new-migrate` skill: it runs `vp migrate` from the preview commit against each local checkout, with dependencies resolved through the registry bridge. Report the outcome to the release manager before moving on.\n\n**The catalog.** The smoke-test catalog and the local-setup rules live in the ecosystem-ci org: [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md), with the machine-readable list in [`ecosystem.json`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/ecosystem.json) (each fork's upstream, tracked branch, and package manager). Run every runnable fork; filter `ecosystem.json` with `jq` to skip non-JS `other` repos and any the release manager says to ignore. Forks pinned to the immediately previous release exercise a real upgrade rather than a no-op.\n\n> **Mandatory: open any test PR against the `vite-plus-ecosystem-ci` fork, never the upstream repo.** `gh pr create` inside a fork defaults its base repo to the parent (upstream), so pass `--repo vite-plus-ecosystem-ci/<repo>` (or run `gh repo set-default vite-plus-ecosystem-ci/<repo>` first). See TESTING.md.\n\n`test-pkg-pr-new-migrate` needs a **local** checkout on the fork's **tracked branch** (often not the default branch, e.g. `vue-core` tracks `minor`). Clone under one directory so the whole test environment cleans up in one step:\n\n```bash\nrepo=<repo>; branch=<tracked-branch>   # from ecosystem.json\ngit clone git@github.com:vite-plus-ecosystem-ci/$repo.git ~/git/github.com/vite-plus-ecosystem-ci/$repo\ngit -C ~/git/github.com/vite-plus-ecosystem-ci/$repo checkout \"$branch\"\n# ... run the harness against ~/git/github.com/vite-plus-ecosystem-ci/$repo ...\n# cleanup after the release: rm -rf ~/git/github.com/vite-plus-ecosystem-ci\n```\n\nThe `.github` repo also ships `scripts/setup-local.sh <repo>` (or `--all`), which does the clone, tracked-branch checkout, remotes, and fork base-repo pinning from the manifest in one step.\n\n**Sync every fork to upstream before you test anything.** The forks drift, often by hundreds of commits, so a checkout straight from `origin` validates stale code and any PR you open against it carries all that drift instead of just the upgrade. From the `.github` checkout, run the safe sync command before the local sweep:\n\n```bash\nscripts/sync-forks.sh --all\n```\n\nThe command fast-forwards only forks with no fork-only commits. Exit code `2` means that at least one fork needs a sync or manual work. Resolve or explicitly skip each reported fork. Never overwrite a divergent tracked branch.\n\nRun `scripts/sync-forks.sh \"$repo\"` again immediately before you open each fork PR. If it prints `REOPEN`, close and reopen the current release PR so GitHub calculates a new merge base. Close superseded PRs and delete their branches. TESTING.md carries the full procedure.\n\n**Validate in the project's own CI.** Beyond the local `vp migrate`, exercise the prerelease in the fork's real CI by opening a draft PR on the fork, following \"Smoke-test via a fork PR\" in TESTING.md: branch `update-vite-plus-prerelease-test-<version>` synced from `source`, apply the upgrade, open a **draft** PR on the fork (never upstream) **assigned to the release manager**, then watch its checks for upgrade-related failures. Offer this alongside the local sweep rather than treating it as an afterthought; it is the only level that exercises each project's own build and tests. Some projects' CIs install with a non-standard tool that cannot resolve preview builds through the bridge `.npmrc` (e.g. cnpmcore's `utoo`), so check the install step before trusting fork-CI results.\n\nThe workflow triggers only on the `labeled` event, not on new pushes. To rebuild after the head moves (e.g. after a step 5 merge from `main`), remove and re-add the label (this cancels an in-flight build for the branch). A stale build whose diff to the new head is test-only is still valid for smoke testing; ask before re-triggering.\n\nRecord each project's base commit, starting version, resolved preview commit, fork PR head, and CI run. After a new preview build, identify which projects ran again and which retain earlier evidence. Do not report a partial repeat as a full-catalog run on the new preview.\n\n### Example (v0.2.2, PR #2016)\n\nChangelog complete, CI green, release manager approved the smoke test. A build existed for head `06708538`; the head had since moved by a test-only merge from `main`, so that build was still valid and was not re-triggered.\n\nHere the target was vibe-dashboard `main` (a `vite-plus-ecosystem-ci` fork, pnpm monorepo on `vite-plus 0.2.1`, i.e. the previous release), so the run exercised the common upgrade path. Pass the release PR number; the harness resolves it through the bridge to the latest published immutable commit and prints the resolved SHA (confirm it matches the build you expect). Pass a full commit SHA instead only to pin a specific build when several have been published:\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh 2016 ~/git/github.com/vite-plus-ecosystem-ci/vibe-dashboard --no-interactive\n```\n\nA passing run looks like:\n\n```\n◇ Updated . to Vite+ 0.0.0-commit.06708538...\n• Dependencies:\n    vite-plus  0.2.1 → 0.0.0-commit.06708538...\n    vite             → 8.1.2\n✓ Dependencies installed in 5.7s\n\nMigration worktree changes (.npmrc force-staged so it survives .gitignore):\nA  .npmrc                      # bridge registry written by vp migrate\n M package.json / pnpm-workspace.yaml / pnpm-lock.yaml\n\nFound 1 version of @voidzero-dev/vite-plus-core\nFound 1 version of vite-plus\nFound 1 version of vitest\n```\n\nPass criteria: the upgrade lands on the `0.0.0-commit.<sha>` build, the install succeeds through the bridge registry, and each of `@voidzero-dev/vite-plus-core`, `vite-plus`, and `vitest` resolves to exactly ONE version (`vitest` at the bundled upstream version). Multiple or stale versions mean the migration or install is broken: stop and treat it as a release blocker. Report the outcome to the release manager either way.\n\nOne exception to rule out first: `0.0.0-commit.<sha>` is a prerelease, and a `*` range does not match prereleases. A package that declares an optional `vite-plus: '*'` peer (oxlint and oxfmt do) can therefore keep a stale `vite-plus` from the old lockfile under pnpm, so the check reports two versions. If the previous-release control resolves to one version and the extra copy hangs only off such a peer, the duplicate is a preview-build artifact.\n\n### Triaging failures across the catalog\n\nAcross the full catalog most failures are not regressions, and reporting them as \"N failed\" without triage is useless to the release manager. Sort every failure into one of these before drawing any conclusion:\n\n- **Registry-bridge fetch flakes.** Check these first, because they are common and they masquerade as something far worse. The bridge drops tarball requests under load: pnpm logs `error (23). Will retry`, or the install dies with `ECONNRESET  aborted`. The dangerous case is a platform binding, because `@voidzero-dev/vite-plus-<platform>` is an **optional** dependency: when its download exhausts the retries, the installer skips it and still reports success, and the job then fails much later at the first command that loads the binding, with `Cannot find native binding` / `Cannot find module '@voidzero-dev/vite-plus-linux-x64-gnu'`. That is a local `node_modules` resolution failure and says nothing about the registry, but NAPI's loader appends generic \"npm has a bug related to optional dependencies\" boilerplate that reads like a publishing problem. Do not conclude the addon was unpublished; the bridge publishes every platform package for every commit build. Confirm, then re-run:\n\n  ```bash\n  curl -s \"https://registry-bridge.viteplus.dev/@voidzero-dev%2fvite-plus-linux-x64-gnu\" \\\n    | python3 -c \"import json,sys; print('0.0.0-commit.<sha>' in json.load(sys.stdin)['versions'])\"\n  gh run rerun <run-id> --failed --repo <owner>/<repo>\n  ```\n\n  Grep every failing log for `error (23)` and `ECONNRESET` before classifying it as anything else. In one release this single cause accounted for 8 fork failures, all of which passed on re-run.\n\n- **Local TLS inspection.** If every package fails locally with `ERR_PNPM_META_FETCH_FAIL ... fetch failed` while `curl` to the bridge succeeds, a TLS-inspecting client (such as a corporate zero-trust agent) may be re-signing `registry-bridge.viteplus.dev` with a root that the OS trusts but Node does not. `node -e \"fetch('https://registry-bridge.viteplus.dev/vite-plus')\"` then fails with `SELF_SIGNED_CERT_IN_CHAIN`. Export that root certificate and set `NODE_EXTRA_CA_CERTS` for the harness and every control run. A follow-up \"latest release is ...\" message comes from pnpm's stale metadata cache and is a symptom, not the cause.\n\n- **Preview-build artifacts.** These are caused by the `0.0.0-commit.<sha>` version string itself and cannot happen for a real npm release, so they are never blockers. The recurring ones: pnpm `ERR_PNPM_TRUST_DOWNGRADE` (\"possible package takeover\"), npm `ETARGET` from a `before`/min-release-age policy, bun `minimum release age`, `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` when a project declares `vite` as a peer (migrate writes the `npm:@voidzero-dev/vite-plus-core@...` alias there), `ERR_PNPM_TARBALL_URL_MISMATCH` or a failed supply-chain policy check against the bridge tarball URLs, Docker builds whose context does not carry the bridge `.npmrc`, project scripts that query `vite@*` or `vite-plus@*` and find nothing (the prerelease does not match `*`), and npm 10 failing with `Unable to resolve reference $vite-plus` on a nested `overrides` entry. Confirm the last two by swapping the commit version for a real release in a scratch copy.\n- **Pre-existing failures.** Prove it rather than asserting it, with whichever control is cheaper: install the previous release into an isolated home and re-run the same command, or check whether the fork's base branch CI already fails. The isolated-home control is the highest-value technique in this step, since it converts a scary-looking failure into a one-line fact:\n\n  ```bash\n  VP_HOME=$HOME/.cache/vp-control-<prev> VP_VERSION=<prev> VP_NODE_MANAGER=no \\\n    VP_SELF_SETUP_NO_MODIFY_PATH=1 bash packages/cli/install.sh\n  cd <project> && VP_HOME=$HOME/.cache/vp-control-<prev> VP_NODE_MANAGER=no \\\n    PATH=\"$HOME/.cache/vp-control-<prev>/bin:$PATH\" vp migrate <project> --no-interactive\n  ```\n\n  `VP_SELF_SETUP_NO_MODIFY_PATH=1` keeps the control installation out of the user's shell profiles; without it, every new shell sets `VP_HOME` to the control.\n\n  **Run the control from inside the project directory.** Launching it from the vite-plus checkout makes `vp` delegate to that checkout's `packages/cli/dist` instead of the pinned release, which silently invalidates the comparison (it fails with an unrelated error such as `Fail to parse yaml as RuleConfig`).\n\n  Run candidate and control in isolated checkouts of the same project base, starting with the same lockfile and no `node_modules`. Compare any lockfile changes made by migration so unrelated dependency versions do not invalidate the control. When the control reproduces the failure, report the evidence (same exit code and error class).\n\n- **Stale pins: weight the forks that were actually on the previous release.** A fork pinned several releases back does not test the release under review at all; `vp migrate` performs a multi-release jump, and any resulting type errors are evidence about that jump. Before drawing a conclusion, work out what each fork was on and judge the release primarily on the forks upgrading from the immediately previous release. Derive the pin from the upgrade commit itself, not from `HEAD`, since later commits on the test branch hide it:\n\n  ```bash\n  sha=$(git -C <dir> log --format=%H --grep='^test: upgrade vite-plus to prerelease' -n 1)\n  git -C <dir> show \"$sha\" | grep -E '^-.*vite-plus'\n  ```\n\n  Parse every YAML document when auditing pnpm lockfiles. Importers and package resolutions can be in separate documents; a single-document parser can miss the resolved versions.\n\n  Report that subset separately; \"2 of the 7 forks on the previous release pass, the other 5 fail on fork infrastructure\" is a far stronger statement than a headline pass rate over the whole catalog.\n\n- **Project-side and infra failures.** Dependency conflicts between the project's own packages, missing fork secrets, third-party GitHub Apps not installed on the fork, network timeouts. Retry once before classifying anything as a network failure; they pass on retry. Two recurring shapes worth naming: a package that imports a dependency it never declared and only ever resolved through hoisting (`Cannot find package 'oxfmt'`) breaks as soon as the harness regenerates the lockfile; and a project whose own dependency has no `main`/`module`/`exports` cannot load its config under any vite-plus version.\n- **Dependency drift during migration.** Regenerating a lockfile can move unrelated floating or nightly dependencies to incompatible versions. Compare with the base lockfile before blaming the candidate. On the test branch, retain the original versions and their dependency graph, then verify a frozen install and rerun the failing command.\n- **Custom quality checks.** Check that project wrappers still load their plugins and recognize migrated test imports. Preserve existing lint diagnostic coverage when repairing migration issues; a smaller baseline can mean that checks stopped running.\n- **Release-age gates on fresh dependencies.** A project's `minimumReleaseAge` can reject packages that Vite+ itself pins and that were published shortly before the release. That is the project's policy, not a vite-plus bug: add the package to `minimumReleaseAgeExclude` on the test branch, and do not propose extending migrate's exemption list. Real installs of the release hit the same gate until those packages age past the project's window.\n- **Harness artifacts.** Failures your own test setup caused, such as a lockfile the harness deleted and the install never regenerated. Fix these and re-run rather than reporting them.\n\nReport the tally by cause, not just pass/fail, and state plainly which failures you controlled for and which you classified from the error text alone. Only a failure that reproduces on the candidate but not on the previous release is a regression.\n\nBefore filing an upstream issue for a finding, search every repository that could own it, including closed issues and open PRs. For a type-aware oxlint diagnostic that means both `oxc-project/oxc` and `oxc-project/tsgolint`.\n\nWhen repairing timing-sensitive smoke tests, keep their assertions and make readiness or timing deterministic. Use a negative control when changing how a test observes behavior: temporarily remove or break that behavior, confirm the test fails, and restore it before committing.\n\nTwo long-run mechanics worth knowing: `vp migrate` installs Vite+ git hooks in the project, so any later `git commit`/`git push` there needs `--no-verify`; and macOS has no GNU `timeout`, so a driver script that time-boxes runs needs its own watchdog. If that driver runs projects in parallel, kill the whole process tree on timeout, not just the wrapper: an orphaned `pnpm install` holds the store lock and the next project then hangs at 0% CPU with no output, which reads like a vite-plus hang and is not one. Concurrent installs that share one store can stall the same way without any orphan, with one process idle while holding the store's `index.db`; rerun the stuck project alone before treating it as a hang.\n\nTwo fork-CI blockers are worth fixing rather than reporting, both on the **test branch only** so the tracked branch stays clean against upstream. A fork whose workflows never trigger on `pull_request` reports \"no checks\" and proves nothing: add a minimal workflow that runs `vp run build` through whatever setup the project already uses. A fork whose workflows target third-party runners (self-hosted labels such as `blacksmith-*`) queues every job forever, because those labels only resolve for the upstream org: map them to GitHub-hosted equivalents, replacing the longest label first so an `-arm` suffix is not left half-rewritten. Runner-specific _actions_ need more than a label swap and are usually not worth fixing. The same applies to `depot-*` and `namespace-profile-*` labels. GitHub-hosted runners are smaller than most of these, so heavy suites can start timing out after the swap; classify those timeouts as fork infrastructure.\n\nClosing a cycle's PRs with `--delete-branch` removes their test branches, but each closed PR's commits stay reachable at `refs/pull/<N>/head`. Find the previous cycle's PR per repository (`gh pr list --repo vite-plus-ecosystem-ci/<repo> --state all`); an org-wide `gh search` stops at 100 results and silently misses forks. Before a new sweep, list the non-upgrade commits on the previous cycle's PRs (supply-chain exemptions, runner-label maps, added workflows), fetch them, and cherry-pick them onto the new test branches. Re-apply by hand when upstream drift makes a cherry-pick conflict. Commits made after the upgrade commit (formatting of migrate-written files, regenerated lockfiles, restored dependencies) only apply after migrating: retry them after the new upgrade commit, take the new lockfile when only lockfiles conflict, and drop a formatting commit that would add migrate-generated `package.json` or workspace keys before migrate runs.\n\n## 5. Release-branch CI\n\nMatch checks to the current PR head and the latest applicable workflow runs. Superseded canceled runs can leave failed aggregate checks in the PR rollup. Check required statuses with `gh pr checks <PR#> --required`, and report required reviewer approval separately from technical CI readiness.\n\nFixes for CI failures go through a **separate PR to `main`**, never as commits on the release branch (the release-version syncs in step 2 are the exceptions). After the fix PR merges:\n\n```bash\ngit checkout release/vX.Y.Z && git merge origin/main --no-edit && git push origin release/vX.Y.Z\n```\n\nDo not assume the merge brought in only the fix PR: `main` may have accumulated several. Before merging, list everything that will come in with `git log origin/release/vX.Y.Z..origin/main --oneline`, then add a changelog entry for **every** newly included PR and rerun the step 3 validation (its missing/extra diff against `generate-notes` catches any entry you missed). When the merged commits change bundled tool versions or `vp migrate` behavior, rebuild the preview (step 4) and repeat the smoke test on the new build: start each fork from its current test branch so earlier fixes carry over, open new fork PRs, close the superseded ones, and report results per build.\n\nKnown release-branch-only failure modes:\n\n- **Binding version drift**: CI's no-unexpected-changes guard reports a diff flipping version strings in `binding/index.cjs`. Fix: step 2.\n- **Registry flakes**: registry-bound fixtures can time out (about 50s) and look like regressions. Rerun before diagnosing, and never commit a `[timeout]` snapshot.\n- **New RustSec advisories**: Security Analysis runs cargo-deny only when `Cargo.lock` changes, and the version bump always changes it. An advisory published since `main` last touched `Cargo.lock` therefore fails first on the release PR. The check is not required; trace which dependency pulls the crate in and let the release manager decide whether to fix it before or after the release. Before opening a fix PR, check open dependency-update PRs (Renovate): one may already bump the crate and clear the check.\n\n## 6. Merge\n\nMerging the release PR is the release trigger. Before merging confirm: CI green, changelog validated, binding and documentation versions synced (including all three prompts), and (if used) the preview build verified.\n\nWhen the release publishes an npm package for the first time, confirm before merging that the package's npm settings list a trusted publisher for `voidzero-dev/vite-plus`, workflow `release.yml`, environment `release`. A missing or mismatched entry fails that publish with `ERR_PNPM_AUTH_TOKEN_EXCHANGE ... (status code 404)` after `vite-plus` is already on `latest`, and the GitHub release, Docker image, docs deployment, and Discord notification are skipped until the job is rerun.\n\nAuto-merge being enabled is not a completed merge. Confirm `mergedAt` and the merge commit, then follow the Release run for that commit; older successful runs can have skipped publishing because the version did not change.\n\n## 7. Automated release pipeline (what happens after merge)\n\n`release.yml` runs on the `main` push because `packages/cli/package.json` changed:\n\n1. `check`: compares the local version against `unpkg.com/vite-plus@latest`; everything below is skipped unless it changed.\n2. `build-rust`: full multi-platform build.\n3. `request-approval`: posts an approval request to the releases Discord channel, and the `Release` job waits on the `release` GitHub environment. **A person with environment approval rights must approve the run in the Actions UI.** The environment sets `prevent_self_review: true`, so whoever merged the release PR triggered the run and cannot approve it: a _different_ reviewer must. Check who can, and tell the release manager rather than leaving them waiting on themselves:\n\n   ```bash\n   gh api repos/voidzero-dev/vite-plus/actions/runs/<run-id>/pending_deployments \\\n     -q '.[] | \"\\(.environment.name) can_approve=\\(.current_user_can_approve) reviewers=\\([.reviewers[]?.reviewer.login] | join(\",\"))\"'\n   ```\n\n4. `Release`: publishes the NAPI bindings (`@voidzero-dev/vite-plus-<platform>`) and standalone CLI packages (`@voidzero-dev/vite-plus-cli-<platform>`, via `packages/cli/publish-native-addons.ts`), then `@voidzero-dev/vite-plus-core`, `vite-plus`, and `create-vite-plus` to npm (`--tag latest`). Each dependency tier waits for npm propagation before publication advances. It then creates the `vX.Y.Z` GitHub release (draft, with installer/binary assets, then undrafted). The generated body has only Published Packages and Installation sections.\n5. `publish-docker`: multi-arch toolchain image to `ghcr.io/voidzero-dev/vite-plus`, after npm publish (the image installs vp from npm).\n6. `deploy-docs`: deploys the production docs after a stable release is published. It is skipped for prereleases. When a prerelease is published to `latest` and its notes or CLI messages link to docs that production does not serve yet, ask the release manager whether to run `gh workflow run deploy-docs.yml --ref <ref>` once the `Release` job is publishing. Use the `vX.Y.Z` tag, or `main` while it still points at the release commit, and confirm the run's head SHA. The `vp` version that builds the docs does not need to match the release: the site and its install scripts come from the checked-out commit. The job authenticates with the `VOID_TOKEN` repository secret. When that token has expired, the job fails with `` `VOID_TOKEN` is invalid or expired `` and `discord-notify` is skipped. Ask someone with access to rotate the secret, then run `gh run rerun <run-id> --failed` to rerun the docs deployment and the Discord notification.\n7. `discord-notify`: announces to Discord after Docker publishing and docs deployment succeed (docs are skipped for prereleases).\n\n**When the `Release` job fails partway, fix the cause, then use \"Re-run failed jobs\", never \"Re-run all jobs\".** A full rerun repeats `check`, which now sees the new version on `unpkg.com/vite-plus@latest` and skips the whole release. A failed-jobs rerun reuses `check` and the build artifacts, and every npm publish step is idempotent: `.github/scripts/publish-npm-package.ts` skips versions the registry already serves (`<pkg>@X.Y.Z is already published; skipping upload.`), so only the unpublished packages upload. Confirm that from the rerun log. The rerun waits on the `release` environment again, and whoever started the rerun cannot approve it.\n\n**A successful publish command does not mean the packages are installable.** `pnpm publish` prints `✅ Published package <name>@X.Y.Z` as soon as the registry accepts the request, and the registry can then take tens of minutes to actually serve that version. This has shipped a broken release: `vite-plus@X.Y.Z` went live on `latest` with an exact dependency on `@voidzero-dev/vite-plus-core@X.Y.Z` that was invisible for about 35 minutes, so every `npm install vite-plus` failed with `ETARGET` and both `publish-docker` and `Deploy docs` failed on `ERR_PNPM_NO_MATCHING_VERSION`. The downstream job failures are the symptom, not the cause; do not re-run them until the registry has the package.\n\nCheck visibility directly, not through `npm view`, which caches:\n\n```bash\nfor pkg in '@voidzero-dev%2Fvite-plus-core' 'vite-plus'; do\n  curl -s -H 'Cache-Control: no-cache' \"https://registry.npmjs.org/$pkg?t=$(date +%s)\" |\n    python3 -c \"import json,sys;d=json.load(sys.stdin);print('$pkg', d['dist-tags'].get('latest'), 'X.Y.Z' in d['versions'])\"\ndone\n```\n\nBoth must report `True` before you trust the release. A stale `modified` timestamp on the packument is the giveaway that nothing landed. If `vite-plus` is visible and `core` is not, the release is broken **right now** for every new install: tell the release manager immediately and offer to move the tag back (`npm dist-tag add vite-plus@<prev> latest`) while the publish is sorted out. Confirm the fix with a real install in a temp directory, not just a registry read:\n\n```bash\nd=$(mktemp -d); cd \"$d\" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z --no-audit --no-fund\n```\n\nThe full package document can update before npm's separately cached installation metadata. The workflow's `.github/scripts/wait-for-npm-packages.ts` checks that installation metadata and the referenced tarball, then waits for settlement. Use that script to reproduce its availability check; local visibility does not prove the workflow runner sees the same state.\n\nLocal clients can also mislead in the other direction. npm and pnpm cache a lookup made before the version landed, so pass `--prefer-online` when a just-published version reports `ETARGET`/`notarget` locally. pnpm 11 and later default to a one-day `minimumReleaseAge`, so `pnpm create vite-plus` (an unpinned `pnpm dlx create-vite-plus`) resolves to the previous `create-vite-plus` until the new version is a day old; verify it with `--config.minimumReleaseAge=0`.\n\n## 8. Post-release\n\n1. **Polish the GitHub release notes** (ask first): the auto-created release body has only Published Packages and Installation. Build the polished notes from the final release PR body:\n   - Drop the closing `---` / `Merging this PR ...` boilerplate.\n   - Preserve every changelog section through **Full Changelog**, including any later revisions requested by the release manager.\n   - Append the generated Published Packages and Installation sections, omit the redundant `View the full commit` line, and end Installation with a Docker usage block (keep the explanation to one short sentence):\n\n     ````markdown\n     **Docker:**\n\n     ```bash\n     docker run --rm -it -v \"$PWD:/app\" -w /app ghcr.io/voidzero-dev/vite-plus:X.Y.Z vp build\n     ```\n\n     Run any `vp` command without installing it; see the [Docker guide](https://viteplus.dev/guide/docker) for more.\n     ````\n\n   - After Installation, add a **Create a New App** section with the pnpm example only:\n\n     ````markdown\n     ### Create a New App\n\n     Start a project without installing the global `vp`: `create-vite-plus` runs `vp create` through your package manager.\n\n     ```bash\n     pnpm create vite-plus vite:application --directory my-app\n     cd my-app && pnpm exec vp dev\n     ```\n\n     Run the command without arguments to pick a template interactively, and see the [create guide](https://viteplus.dev/guide/create#without-the-global-cli) for other package managers, templates, and options.\n     ````\n\n   - **Present the draft to the release manager and apply only after approval.** Before review, write the complete draft to a temporary Markdown file with the proposed release title at the top and the full body below it. Update that file after every requested revision; do not treat chat excerpts as the canonical draft. After approval, use a body-only notes file (without the review title) to retitle the release and apply the notes:\n\n     ```bash\n     gh release edit vX.Y.Z --repo voidzero-dev/vite-plus \\\n       --title \"vite-plus vX.Y.Z\" --notes-file /tmp/release-notes.md\n     ```\n\n   - Keep the review draft, body-only notes file, and live release aligned after requested edits. Read back the live title and body to verify the update. Normalize CRLF and LF before comparing the approved file with the live body, because GitHub can change line endings. Re-run the step 3 validation greps, plus `grep -c 'Merging this PR'` (must be 0).\n   - For a SemVer prerelease, confirm that GitHub marks the release as a prerelease. The automated release can create a prerelease tag without setting that flag. Add `--prerelease` when applying the approved notes, then verify `isPrerelease` with `gh release view vX.Y.Z --json isPrerelease`.\n\n2. **Verify**:\n\n   ```bash\n   npm view vite-plus version                       # X.Y.Z\n   npm view @voidzero-dev/vite-plus-core version    # X.Y.Z\n   npm view create-vite-plus version                # X.Y.Z\n   npm vi\n\n<!-- [TRUNCATED AT 50KB] -->",".claude/skills/spawn-process/SKILL.md":"---\nname: spawn-process\ndescription: Guide for writing subprocess execution code using the vp_command crate\nallowed-tools: Read, Grep, Glob, Edit, Write, Bash\n---\n\n# Add Subprocess Execution Code\n\nWhen writing Rust code that needs to spawn subprocesses (resolve binaries, build commands, execute programs), always use the `vp_command` crate. Never use `which`, `tokio::process::Command::new`, or `std::process::Command::new` directly.\n\n## Available APIs\n\n### `vp_command::resolve_bin(name, path_env, cwd)` — Resolve a binary name to an absolute path\n\nHandles PATHEXT (`.cmd`/`.bat`) on Windows. Pass `None` for `path_env` to search the current process PATH.\n\n```rust\n// Resolve using current PATH\nlet bin = vp_command::resolve_bin(\"node\", None, &cwd)?;\n\n// Resolve using a custom PATH\nlet custom_path = std::ffi::OsString::from(&path_env_str);\nlet bin = vp_command::resolve_bin(\"eslint\", Some(&custom_path), &cwd)?;\n```\n\n### `vp_command::build_command(bin_path, cwd)` — Build a command for a pre-resolved binary\n\nReturns `tokio::process::Command` with cwd, inherited stdio, and `fix_stdio_streams` on Unix already configured. Add args, envs, or override stdio as needed.\n\n```rust\nlet bin = vp_command::resolve_bin(\"eslint\", None, &cwd)?;\nlet mut cmd = vp_command::build_command(&bin, &cwd);\ncmd.args(&[\".\", \"--fix\"]);\ncmd.env(\"NODE_ENV\", \"production\");\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::build_shell_command(shell_cmd, cwd)` — Build a shell command\n\nUses `/bin/sh -c` on Unix, `cmd.exe /C` on Windows. Same stdio and `fix_stdio_streams` setup as `build_command`.\n\n```rust\nlet mut cmd = vp_command::build_shell_command(\"echo hello && ls\", &cwd);\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::run_command(bin_name, args, envs, cwd)` — Resolve + build + run in one call\n\nCombines resolve_bin, build_command, and status().await. The `envs` HashMap must include `\"PATH\"` if you want custom PATH resolution.\n\n```rust\nlet envs = HashMap::from([(\"PATH\".to_string(), path_value)]);\nlet status = vp_command::run_command(\"node\", &[\"--version\"], &envs, &cwd).await?;\n```\n\n## Dependency Setup\n\nAdd `vp_command` to the crate's `Cargo.toml`:\n\n```toml\n[dependencies]\nvp_command = { workspace = true }\n```\n\nDo NOT add `which` as a direct dependency — binary resolution goes through `vp_command::resolve_bin`.\n\n## Exception\n\n`crates/vp_global_cli/src/shim/exec.rs` uses synchronous `std::process::Command` with Unix `exec()` for process replacement. This is the only place that bypasses `vp_command`.\n",".claude/skills/sync-tsdown-cli/SKILL.md":"---\nname: sync-tsdown-cli\ndescription: Sync tsdown runtime CLI options with vp pack after a tsdown upgrade. Use for option forwarding changes; use sync-upstream-cli-help for static help wording.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync tsdown CLI\n\nRuntime options live in `packages/cli/src/pack-bin.ts`; static help lives in\n`packages/cli/src/help.ts`.\n\n1. Run `npx tsdown --help` from `packages/cli/` and compare it with `pack-bin.ts`.\n2. Add new forwarded options using the existing cac `.option()` pattern. For removed\n   options, add `// NOTE: removed from tsdown CLI in vX.Y.Z` for reviewer follow-up.\n3. Preserve runtime differences: `-c, --config` stays disabled because Vite+ uses\n   `vite.config.ts`, and `--env-prefix` keeps the `['VITE_PACK_', 'TSDOWN_']` default.\n4. For static labels and descriptions, follow `sync-upstream-cli-help`; do not adapt\n   upstream wording to explain runtime differences.\n5. Run `pnpm --filter vite-plus build-ts` and `vp pack -h`. Add a focused PTY snapshot\n   case under `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/` when a new runtime\n   option is exposed.\n",".claude/skills/sync-upstream-cli-help/SKILL.md":"---\nname: sync-upstream-cli-help\ndescription: Sync Vite+'s static CLI help with Vite, Vitest, Oxlint, Oxfmt, and tsdown while preserving intentional omissions. Use when an upstream dependency upgrade changes CLI help.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream CLI help\n\n## Input and target\n\n- Read the diff from `$CLI_HELP_DIFF_REPORT`; first run\n  `test -r \"$CLI_HELP_DIFF_REPORT\"`. Act only when `$CLI_HELP_DIFF_CHANGED` is\n  `true`.\n- Treat the upgraded tool's `--help` output as the source of truth. The report locates\n  changes and versions; rerun the exact version when its diff is truncated.\n- Edit `commandHelpDocs` in `packages/cli/src/help.ts`.\n- Do not edit `packages/cli/src/utils/help.ts` for content drift. It owns terminal\n  wrapping, alignment, and the right margin.\n\n| Upstream help         | Document entry |\n| --------------------- | -------------- |\n| `vite --help`         | `dev`          |\n| `vite build --help`   | `build`        |\n| `vite preview --help` | `preview`      |\n| `vitest --help`       | `test`         |\n| `oxlint --help`       | `lint`         |\n| `oxfmt --help`        | `fmt`          |\n| `tsdown --help`       | `pack`         |\n\n## Change\n\n- For items Vite+ exposes, copy upstream labels, descriptions, section titles, and\n  section guidance exactly. Preserve intentional lines and lists, but not terminal\n  padding, automatic wrapping, ANSI color, or version banners.\n- Remove an upstream item only after confirming Vite+ no longer supports or\n  deliberately retains it.\n\n## Do not change\n\n- Keep Vite+-owned usage, summaries, examples, and documentation URLs.\n- Keep config selectors/loaders hidden, including `--config`, `--configLoader`, and\n  `--disable-nested-config`.\n- Do not add standalone modes such as `--init`, `--migrate`, or `--lsp`, top-level\n  `--version`, or options Vite+ does not forward.\n- Do not change runtime forwarding or rewrite upstream wording for a Vite+-specific\n  runtime default. Use `sync-tsdown-cli` for tsdown runtime changes.\n\nRe-record the affected CLI help snapshots and inspect their diffs. Do not modify help\ndocuments when the report contains no actionable exposed change.\n",".claude/skills/sync-upstream-dependency-docs/SKILL.md":"---\nname: sync-upstream-dependency-docs\ndescription: Sync exact upstream dependency versions embedded in Vite+ documentation after the automated dependency upgrade. Use when bundled Vite, Vitest, Oxlint, Oxfmt, tsdown, Rolldown, or related package versions change.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream dependency docs\n\n1. Read `$UPGRADE_DEPS_META_DIR/versions.json` and consider only entries whose `old`\n   and `new` values differ.\n2. Search `README.md`, `packages/*/README.md`, and `docs/**/*.md` for each changed\n   package name and its old exact version. Do not treat changelogs, RFC examples,\n   snapshots, or broad ranges such as `vitest@4.x` as current-version references.\n3. Update references that promise to match Vite+'s currently bundled version.\n   Manual-installation examples use placeholders for the `vite-plus` and Vitest\n   versions, so they do not need updates.\n4. Preserve the surrounding wording and formatting. Do not rewrite examples whose\n   version is intentionally historical or illustrative.\n5. Re-run the searches for the changed packages and inspect the focused diff. No\n   stale exact version may remain in documentation that describes the current bundle.\n",".claude/skills/test-pkg-pr-new-migrate/SKILL.md":"---\nname: test-pkg-pr-new-migrate\ndescription: Verify a preview (registry bridge) build of vite-plus against a real project before release — run `vp migrate` from the preview commit against a local project, deps resolved through the registry bridge. Use when asked to verify/e2e-test a preview or pkg-pr-new build against a project, \"test PR #<N> on <project>\", or check a prerelease against a repo.\nallowed-tools: Bash, Read\n---\n\n# Verify a preview build against one project\n\nInstalls an isolated global `vp` built from a registry bridge commit build and runs `vp migrate` on a specified local project. The global CLI and the migrated project both pin `vite-plus`/`vite` to the clearly-defined `0.0.0-commit.<sha>` build. `vp migrate` itself writes the bridge registry into the project's `.npmrc` (or `.yarnrc.yml` for Yarn Berry) so the deps resolve, during this run and in the project's own CI; this script only force-stages that file past `.gitignore`.\n\nRequired inputs: a `<PR-or-SHA>` (the build to verify) and a `<project-path>`. If either is missing from the request, **ask the user** for it — never guess the PR/SHA, as the build under test is the user's choice.\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh <PR-or-SHA> <project-path> [migrate-options...]\n# e.g.\n.github/scripts/test-pkg-pr-new-migrate.sh 1891 /path/to/npmx.dev --no-interactive\n```\n\n- First arg is a PR number or commit SHA; the script resolves the immutable commit via the bridge `x-commit-key` header and verifies the bridge serves it (the preview publish workflow, triggered by the `preview-build` label, registers each commit).\n- Never touches `~/.vite-plus`; installs the project's committed dependencies before migrating, because `vp migrate` reads the original Vitest version from the lockfile or `node_modules`; refuses a dirty worktree unless `ALLOW_DIRTY=1`; prints the project's `git status`/`diff` at the end — inspect that to confirm the migration result.\n\n**The build under test must include the \"migrate writes the bridge registry\" feature** (this session's work / current branch head onward). The harness no longer writes the registry itself — it relies on `vp migrate` doing it. Testing an older build with this harness would leave the project with no bridge registry, so its deps resolve from npmjs (`ERR_PNPM_NO_MATCHING_VERSION` on the `0.0.0-commit.<sha>` version). Always verify a fresh build of the branch, not a stale published commit.\n\nThe script prints the resolved versions at the end, querying one package at a time (npm/yarn/bun `why` only accept a single package; `-r` is pnpm-only). Each must resolve to exactly ONE version: `vite-plus` and `vite` at the expected `0.0.0-commit.<sha>` (vite via the `@voidzero-dev/vite-plus-core` alias), `vitest` at the bundled upstream version. Multiple versions, or a stale/wrong version, means the migration or install is broken. To re-check by hand:\n\n```bash\ncd <project-path>\n# pnpm: one call, recursive across workspaces\nvp why -r vite-plus vite vitest\n# npm / yarn / bun: one package per call, no -r\nvp why vite-plus && vp why vite && vp why vitest\n```\n",".claude/skills/verify-interactive-cli/SKILL.md":"---\nname: verify-interactive-cli\ndescription: Drive and capture vp's interactive (clack) prompts in a tmux session to manually verify interactive UX and catch spinner-over-prompt bugs. Use when asked to test/verify/capture an interactive vp command's prompts (vp migrate, vp create, ...), reproduce a prompt-rendering bug, or show the real interactive CLI output for a PR.\nallowed-tools: Bash, Read\n---\n\n# Verify vp's interactive CLI prompts\n\nThe PTY snapshot suite covers scripted prompt flows. This skill complements it by driving an installed CLI in tmux, capturing clean output, and checking a prompt over several seconds for a spinner animating underneath it (a real, recurring UX bug class, e.g. the \"Preparing migration\" and \"Checking Node.js version support\" spinners).\n\n## Prerequisites\n\n- `tmux` (macOS has none by default): `brew install tmux`.\n- The global `vp` must contain the code under test. To exercise working-tree changes, rebuild first: `pnpm bootstrap-cli` (~5 min), then confirm with `vp --version`.\n\n## Driver\n\n`interactive-cli-tmux-driver.sh` is bundled in this skill's directory.\n\n```bash\n# Run to completion, auto-accepting every prompt's DEFAULT; prints a clean transcript.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\"\n\n# STOP at a specific prompt (do NOT answer it) and check for a spinner animating under it.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\" \"Upgrade Node.js\"\n```\n\nHow it works:\n\n- Runs the command in a detached tmux session; `tmux capture-pane -p` yields clean text (clack's in-place redraws overwrite, so only resolved lines remain), far cleaner than `expect`'s raw ANSI capture.\n- Auto-accepts each prompt's default by sending Enter when the pane goes STABLE. A waiting prompt is static; animating spinners keep the pane changing, so Enter never fires mid-work.\n- End-detection uses `; echo \"$M1$M2 exit=$?\"` where M1/M2 are split vars, so the literal end marker is not in the typed command line (otherwise a grep matches the echoed command, not the program output).\n- With a STOP_AT regex it halts at the target prompt and captures twice ~3s apart: identical captures = static prompt (OK); differing captures (or a `Checking ... (Xs)` line) = a spinner is animating under the prompt = a UX bug.\n\n## Setting up a target project\n\nCreate a throwaway project that triggers the prompts you want to see, e.g. a fresh Vite app whose `.node-version` is below the supported range to exercise the Node-upgrade confirm:\n\n```bash\nmkdir -p /tmp/vp-demo && cd /tmp/vp-demo\nprintf '{\\n  \"name\":\"d\",\"private\":true,\"type\":\"module\",\"packageManager\":\"pnpm@10.18.0\",\\n  \"scripts\":{\"build\":\"vite build\",\"test\":\"vitest run\"},\\n  \"devDependencies\":{\"vite\":\"^8.0.0\",\"vitest\":\"^4.1.0\"}\\n}\\n' > package.json\necho \"24.3.0\" > .node-version\nprintf 'import { defineConfig } from \"vite\";\\nexport default defineConfig({});\\n' > vite.config.ts\ngit init -q && git add -A && git -c user.email=x@x -c user.name=x commit -qm init\n```\n\n## When you find a spinner-over-prompt bug\n\nFix it test-first: the prompt's code must pause the migration progress spinner before the confirm renders. Assert the call order is `['pause', 'confirm']`. Reference fix: `upgradeUnsupportedNodeVersions` (in `packages/cli/src/migration/migrator/setup.ts`) takes a `pauseProgress` callback that `bin.ts` wires to `clearMigrationProgress`, called right before `prompts.confirm`.\n\n## Gotchas\n\n- clack's active-prompt marker here is `›` (not always `◆`); detect a waiting prompt by pane stability, not a specific glyph.\n- `VP_SKIP_INSTALL=1` skips the dependency install but breaks steps that load `vite.config.ts` (e.g. the prettier auto-migration) because `vite` isn't installed; use it only when you stop before the install step.\n- A `&` inside a background task detaches the script, so the task reports \"completed\" early while it keeps running; poll a status file or `capture-pane` to track real progress.\n","AGENTS.md":"# Vite+\n\nVite+ is the web toolchain behind `vp`: Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, Vite Task, runtime and package-manager workflows, and project creation/migration.\n\nThis guide applies to this repository; `CLAUDE.md` points here. For changes to the guidance shipped to user projects, use `packages/cli/AGENTS.md` and `packages/cli/src/utils/agent.ts`. Leave those files unchanged for root-guide tasks.\n\n## Context by Task\n\n- **CLI routing**: `packages/cli/src/bin.ts` dispatches JS commands; `packages/cli/binding/src/lib.rs` and `packages/cli/binding/src/cli/mod.rs` handle local NAPI commands. Global routing starts at `crates/vp_global_cli/src/main.rs` and `crates/vp_global_cli/src/cli.rs`. Compare global and local paths for routing bugs.\n- **Package managers and runtimes**: use `crates/vp_pm_cli/` and `crates/vp_js_runtime/`. The Windows shim trampoline lives in `crates/vp_trampoline/`, outside the Cargo workspace.\n- **Config loading**: compare the [static extractor](crates/vp_static_config/README.md) with the JS fallback in `packages/cli/src/resolve-vite-config.ts`.\n- **Migration**: follow the [migrator instructions](packages/cli/src/migration/migrator/README.md) when changing `vp migrate`; use [migration rules](docs/guide/migrate-rules.md) for expected behavior.\n- **Bundled tools and exports**: use [CLI bundling](packages/cli/BUNDLING.md) and [core bundling](packages/core/BUNDLING.md). Test resolution starts at `packages/cli/src/resolve-test.ts`; `packages/cli/build.ts` generates test API shims.\n- **Testing a local build in a project**: follow [CONTRIBUTING.md](CONTRIBUTING.md) for linking and installation. For `vp migrate` / `vp create`, use the local registry in `packages/tools/src/local-npm-registry.ts`, which also serves PTY fixtures and ecosystem tests.\n- **Product documentation**: start at `README.md` and the VitePress site in `docs/guide/` and `docs/config/`.\n- **Setup and build commands**: use `CONTRIBUTING.md`, `justfile`, and `package.json`. Start with `just init` for initial setup. Repository lint, format, and test configuration lives in `vite.config.ts`; TypeScript settings live in `tsconfig.json`.\n\n## Repository Constraints\n\n- Distinguish built-ins from tasks: `vp test` executes upstream Vitest; `vp run test` executes a script or configured task named `test`. `vpr` is shorthand for `vp run`. Inspect command surfaces with `vp help` / `vp <command> --help`, and bundled versions with `vp --version`.\n- Keep task configuration under `run` in `vite.config.ts`; do not introduce `vite-task.json`. Existing `package.json` scripts are `vp run` targets without caching by default. Use `run.tasks` for explicit command configuration, default caching, dependencies, or input/environment tracking. A task name must occur in only one of these files. See the [run guide](docs/guide/run.md) and [run configuration](docs/config/run.md).\n- Keep public test imports on `vite-plus/test*`, which wraps upstream `vitest` and `@vitest/browser*`. Do not recreate `packages/test` or `@voidzero-dev/vite-plus-test`.\n- Vite Task crates are git dependencies in `Cargo.toml`; there is no local `crates/vt`. Do not run `cargo test -p vt` here.\n- Use `vp_shared::VpDirs` for Vite+ directory roots. See `crates/vp_shared/src/dirs.rs` and `crates/vp_shared/src/dirs/resolution.rs`. Call sites must not construct category paths or read `VP_HOME` or `XDG_*` directly.\n- Follow [`.clippy.toml`](.clippy.toml) for Rust restrictions and replacements. Use `crates/vp_shared/src/output.rs` for user-facing output; command output that can be piped must use `print_and_flush` or another broken-pipe-safe helper instead of `print!` or `println!`. Enable `clippy::print_stdout` in migrated modules so CI prevents direct stdout macros from returning. Use `crates/vp_shared/src/env_config.rs` for test-scoped environment configuration. See `crates/vp_command/src/lib.rs` for `vt_path` usage.\n- For TypeScript CLI output, use `packages/cli/src/utils/terminal.ts` and match the surrounding command style.\n- Keep changes scoped to the task and leave unrelated tracked and untracked files alone. Prefer source references over duplicated instructions in this guide.\n\n## Checks and Tests\n\nPrefer local fixtures or mock servers for tests that depend on external services. Use real network requests only when they are necessary to verify the behavior under test, and explain that need in the test. Reuse existing test infrastructure, preserve behavioral coverage, and keep mutable state isolated between cases.\n\nFor behavior changes, find nearby tests before editing and add or update coverage in the same area. Choose checks for the changed layer:\n\n| Change                         | Validation                                                                                                                         |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |\n| Documentation / agent guidance | Verify referenced paths, commands, and links; `git diff --check -- <files>`                                                        |\n| TypeScript / JS CLI            | `vp check`, `pnpm test:unit`, and focused package tests                                                                            |\n| Rust CLI / crates              | `just check`, `just test`, `just lint`                                                                                             |\n| CLI output / interactions      | Focused tests and `just snapshot-test <filter>`; review snapshot diffs                                                             |\n| Global CLI                     | `pnpm bootstrap-cli` and `vp --version` for installed end-to-end checks; `just snapshot-test-global <filter>` for global snapshots |\n| Release / build                | `just build`                                                                                                                       |\n| Full pre-merge check           | `pnpm bootstrap-cli && pnpm test && git status`                                                                                    |\n\nValidate `vp check`, lint, format, and type-check changes end-to-end: bundled-tool routing can hide config drift. Use `vp check --fix` only when you intend to apply formatting or lint fixes. Documentation-only changes need no unrelated test suites.\n\n### CLI Snapshots\n\nWrite new CLI tests, including interactive flows, in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`. Read the [snapshot runner instructions](crates/vp_cli_snapshots/tests/cli_snapshots/README.md) before adding cases; they define `snapshots.toml`, CLI flavors, `vpt` helpers, and prompt milestones. See [the RFC](rfcs/interactive-snapshot-tests.md) for design rationale.\n\n- Steps are argv arrays without an implicit shell. Use `vpt` helpers instead of coreutils for portable file operations.\n- For package-manager cases, prefer the runner's local registry, [shared registry fixtures](crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md), and response cache over new public-registry requests. Keep package-manager caches isolated per case, and refresh recorded fixtures only through the explicit maintenance command.\n- For changes to network use or scheduling, compare request counts and elapsed time with the same cases, worker count, and initial cache state. Do not remove assertions or share mutable case state to improve speed.\n- Local-flavor cases require a fresh `packages/cli/dist`. Use `just snapshot-test-global <filter>` when no JS build is available.\n- For intended snapshot changes, run `UPDATE_SNAPSHOTS=1 just snapshot-test <filter>`. Review and commit the recorded `.md` files with the fixture; mismatches fail the run and produce `.md.new` files.\n\n## Pull Requests\n\nPrefer a stack of small PRs for work with separable layers. Use the `gh-stack` extension or github.com; stacks require branches in this repository, so use standalone PRs from forks. Follow [CONTRIBUTING.md](CONTRIBUTING.md#submitting-pull-requests) for submission and commit-signing requirements.\n","CLAUDE.md":"@AGENTS.md\n","packages/cli/AGENTS.md":"<!--VITE PLUS START-->\n\n# Using Vite+, the Unified Toolchain for the Web\n\nThis project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, and it invokes Vite through `vp dev` and `vp build`. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.\n\nDocs are local at `node_modules/vite-plus/docs` or online at https://viteplus.dev/guide/.\n\n## Built-in Commands vs Scripts\n\n`vp <name>` runs a built-in command. `vp run <name>` runs a `package.json` script or a `vite.config.ts` task. Scripts cannot overwrite built-ins, so `vp dev` and `vp run dev` may do different things. Check `package.json` and `vite.config.ts` first, and run `vp run <name>` when the project defines a script or task with that name.\n\n## Running Scripts\n\nRun TypeScript and JavaScript files with `vpx <file>`, for example `vpx ./scripts/seed.ts --dry-run`. It handles enums, decorators, JSX, and tsconfig `paths`, so do not add `tsx` or `ts-node` to run scripts. `vpx --watch <file>` reruns the file when a file it uses changes, in place of `tsx watch`. `vp node <file>` runs plain Node.js.\n\n## Tool Versions\n\nRun `vp toolchain` to show versions and relationships in the active Vite+\nrelease. Add a tool name to select part of the graph. For example, run\n`vp toolchain vite`. Use `--global` to ignore the local `vite-plus` package. Use\n`vp why <package>` to show the package-manager dependency graph.\n\n## Review Checklist\n\n- [ ] Run `vp install` after pulling remote changes and before getting started.\n- [ ] Run `vp check` and `vp test` to format, lint, type check and test changes.\n- [ ] Check if there are `vite.config.ts` tasks or `package.json` scripts necessary for validation, run via `vp run <script>`.\n- [ ] If setup, runtime, or package-manager behavior looks wrong, run `vp env doctor` and include its output when asking for help.\n\n<!--VITE PLUS END-->\n","packages/cli/README.md":"# VITE+(⚡︎) Local CLI\n\n**The Unified Toolchain for the Web**\n_runtime and package management, create, dev, check, test, build, pack, and monorepo task caching in a single dependency_\n\nThis package provides the project-local version of Vite+. The global `vp` command automatically delegates to this package for all project-specific tasks.\n\n---\n\nVite+ is the unified entry point for local web development. It combines [Vite](https://vite.dev/), [Vitest](https://vitest.dev/), [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [Rolldown](https://rolldown.rs/), [tsdown](https://tsdown.dev/), and [Vite Task](https://github.com/voidzero-dev/vite-task) into one zero-config toolchain that also manages runtime and package manager workflows:\n\n- **`vp env`:** Manage Node.js and package managers globally and per project\n- **`vp install`:** Install dependencies with automatic package manager detection\n- **`vp dev`:** Run Vite's fast native ESM dev server with instant HMR\n- **`vp check`:** Run formatting, linting, and type checks in one command\n- **`vp test`:** Run tests through bundled Vitest\n- **`vp build`:** Build applications for production with Vite + Rolldown\n- **`vp run`:** Run `package.json` scripts and monorepo tasks with caching and dependency-aware scheduling\n- **`vp pack`:** Build libraries for npm publishing or standalone app binaries\n- **`vp toolchain`:** Show the versions of Vite+, Vite, Rolldown, Oxc, and other tools\n- **`vp create` / `vp migrate`:** Scaffold new projects and migrate existing ones\n\nAll of this is configured from your project root and works across Vite's framework ecosystem.\nVite+ is fully open-source under the MIT license.\n\n## Getting Started\n\nInstall Vite+ globally as `vp`:\n\nFor Linux or macOS:\n\n```bash\ncurl -fsSL https://vite.plus | bash\n```\n\nFor Windows:\n\n```bash\nirm https://viteplus.dev/install.ps1 | iex\n```\n\n`vp` handles the full development lifecycle such as package management, development servers, linting, formatting, testing and building for production.\n\n## Configuring Vite+\n\nVite+ can be configured using a single `vite.config.ts` at the root of your project:\n\n```ts\nimport { defineConfig } from 'vite-plus';\n\nexport default defineConfig({\n  // Standard Vite configuration for dev/build/preview.\n  plugins: [],\n\n  // Vitest configuration.\n  test: {\n    include: ['src/**/*.test.ts'],\n  },\n\n  // Oxlint configuration.\n  lint: {\n    ignorePatterns: ['dist/**'],\n  },\n\n  // Oxfmt configuration.\n  fmt: {\n    semi: true,\n    singleQuote: true,\n  },\n\n  // Vite Task configuration.\n  run: {\n    tasks: {\n      'generate:icons': {\n        command: 'node scripts/generate-icons.js',\n        cache: {\n          env: ['ICON_THEME'],\n        },\n      },\n    },\n  },\n\n  // `vp staged` configuration.\n  staged: {\n    '*': 'vp check --fix',\n  },\n});\n```\n\nThis lets you keep the configuration for your development server, build, test, lint, format, task runner, and staged-file workflow in one place with type-safe config and shared defaults.\n\nUse `vp migrate` to migrate to Vite+. It merges tool-specific config files such as `.oxlintrc*`, `.oxfmtrc*`, and lint-staged config into `vite.config.ts`.\n\n### CLI Workflows (`vp help`)\n\n#### Start\n\n- **create** - Create a new project from a template\n- **migrate** - Migrate an existing project to Vite+\n- **config** - Configure hooks and agent integration\n- **hooks** - Manage the Git hook dispatcher\n- **staged** - Run linters on staged files\n- **install** (`i`) - Install dependencies\n- **env** - Manage Node.js and package managers\n\n#### Develop\n\n- **dev** - Run the development server\n- **check** - Run format, lint, and type checks\n- **lint** - Lint code\n- **fmt** - Format code\n- **test** - Run tests\n\n#### Execute\n\n- **run** - Run monorepo tasks\n- **exec** - Execute a command from local `node_modules/.bin`\n- **node** - Run a Node.js script with the resolved Vite+ environment; use `vpx <file>` for TypeScript\n- **dlx** - Execute a package binary without installing it as a dependency\n- **cache** - Manage the task cache\n\n#### Build\n\n- **build** - Build for production\n- **pack** - Build libraries\n- **preview** - Preview production build\n\n#### Manage Dependencies\n\nVite+ automatically wraps your package manager (pnpm, npm, Yarn, or Bun) based on `packageManager` and lockfiles:\n\n- **add** - Add packages to dependencies\n- **remove** (`rm`, `un`, `uninstall`) - Remove packages from dependencies\n- **update** (`up`) - Update packages to latest versions\n- **dedupe** - Deduplicate dependencies\n- **outdated** - Check outdated packages\n- **list** (`ls`) - List installed packages\n- **why** (`explain`) - Show why a package is installed\n- **info** (`view`, `show`) - View package metadata from the registry\n- **link** (`ln`) / **unlink** - Manage local package links\n- **rebuild** - Rebuild native modules\n- **pm** - Forward a command to the package manager\n\n#### Maintain\n\n- **toolchain** - Show Vite+ tool versions and their relationships\n- **upgrade** - Update `vp` itself to the latest version\n- **implode** - Remove `vp` and all related data\n\n### Scaffolding your first Vite+ project\n\nUse `vp create` to create a new project:\n\n```bash\nvp create\n```\n\nYou can run `vp create` inside of a project to add new apps or libraries to your project.\n\nOrganizations can expose a curated set of templates under their npm scope by\npublishing `@org/create` with a `createConfig.templates` manifest in its `package.json`.\nOnce published, `vp create @org` opens an interactive picker over those\ntemplates, and setting `create: { defaultTemplate: '@org' }` in\n`vite.config.ts` makes it the default for bare `vp create`. See the\n[Organization Templates guide](https://viteplus.dev/guide/create#organization-templates)\nfor the authoring workflow and\n[`create.defaultTemplate`](https://viteplus.dev/config/create) for the\nconfig reference.\n\n### Migrating an existing project\n\nYou can migrate an existing project to Vite+:\n\n```bash\nvp migrate\n```\n\n### GitHub Actions\n\nUse the official [`setup-vp`](https://github.com/voidzero-dev/setup-vp) action to install Vite+ in GitHub Actions:\n\n```yaml\n- uses: voidzero-dev/setup-vp@<setup-vp-version>\n  with:\n    node-version: '22'\n    cache: true\n```\n\nSet `<setup-vp-version>` to an exact version from the [`setup-vp` releases page](https://github.com/voidzero-dev/setup-vp/releases). You can use a commit SHA instead. Do not use the `v1` tag. The `v1` tag no longer receives updates.\n\nSee [Automatic Version Updates](https://viteplus.dev/guide/ci#automatic-version-updates) to configure Dependabot or Renovate.\n\n#### Manual Installation & Migration\n\nIf you are manually migrating a project to Vite+, install these dev dependencies first:\n\n```bash\nvp install -D vite-plus\n```\n\nAdd package-manager overrides so that other packages use the Vite+ versions. Alias `vite` to `@voidzero-dev/vite-plus-core`, setting `<vite-plus-version>` to the installed `vite-plus` version. Pin `vitest` to `<vitest-version>`, using the bundled version shown by `vp toolchain vitest`. The project and `vp test` then use the same Vitest copy. Without the pin, a dependency or workspace package can install a different Vitest version. The two versions can use separate mocks, `expect` functions, and runner states:\n\n```json\n\"overrides\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\nIf you are using `pnpm`, add this to your `pnpm-workspace.yaml`:\n\n```yaml\noverrides:\n  vite@*: npm:@voidzero-dev/vite-plus-core@<vite-plus-version> # Match the installed vite-plus version\n  vitest@*: <vitest-version> # Match the version from vp toolchain vitest\n```\n\nOr, if you are using Yarn:\n\n```json\n\"resolutions\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\n## Sponsors\n\nThanks to [namespace.so](https://namespace.so) for powering our CI/CD pipelines with fast, free macOS, Linux, and Windows runners.\n","packages/cli/src/migration/migrator/README.md":"# `migrator/` — migration logic, split by category\n\nThe `vp migrate` implementation used to live in a single ~7,300-line\n`packages/cli/src/migration/migrator.ts`. It is now split into category\nmodules in this directory. `migration/migrator.ts` is a **barrel** that only\nre-exports them:\n\n```ts\n// migration/migrator.ts\nexport * from './migrator/shared.ts';\nexport * from './migrator/eslint.ts';\n// ...one line per module\n```\n\nSo **external code keeps importing from `./migrator.ts`** (the barrel) and\nnothing outside this directory had to change.\n\n## Modules\n\nPick the file by what a function _does_, not by where it happens to be called.\n\n| File                     | Owns                                                                                                                                                                                                                    |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `orchestrators.ts`       | Top-level entry points that wire everything together: `rewriteStandaloneProject`, `rewriteMonorepo`, `rewriteMonorepoProject`.                                                                                          |\n| `package-json.ts`        | `rewritePackageJson` and the direct dependency-spec rewriting it does.                                                                                                                                                  |\n| `vite-plus-bootstrap.ts` | \"Already on Vite+\" detection and the bootstrap/reconcile path: `detectVitePlusBootstrapPending`, `ensureVitePlusBootstrap`, `reconcileVitePlusBootstrapPackage`, the `ensure*`/`*Pending`/`*SatisfiesVitePlus` helpers. |\n| `catalog.ts`             | `pnpm-workspace.yaml` / catalog / overrides / build-allowance writers and catalog-dependency resolvers; bun catalog; `rewriteRootWorkspacePackageJson`; pnpm workspace-settings migration.                              |\n| `vitest-ecosystem.ts`    | Detecting direct vitest usage, the override-key (\"dependency selector\") parsing/dropping logic, managed-override sets, ecosystem alignment, legacy `vite-plus-test` wrapper-alias pruning.                              |\n| `vitest-v5.ts`           | Versioned Vitest v5 preflight, compatibility edits, and review findings. Pure AST and command transforms live under `../vitest-v5/`.                                                                                    |\n| `yarn.ts`                | `.yarnrc.yml`, Yarn PnP detection, workspace-hoisting fix, webdriverio detection.                                                                                                                                       |\n| `source-scan.ts`         | Scanning a project's source tree for signals (browser-mode, opt-in providers, `@nuxt/test-utils`, retained upstream-vitest references).                                                                                 |\n| `vite-config.ts`         | `vite.config.ts` merging, default-config injection, staged-config merge, lazy-plugin wrapping, import rewriting (`rewriteAllImports`), migrated-oxlint-config sanitization, lint-staged removal.                        |\n| `oxlint-config.ts`       | Oxlint JSON inheritance graphs and shared TypeScript config generation.                                                                                                                                                 |\n| `eslint.ts`              | ESLint → Oxlint migration, oxlint JS-plugin namespace handling, ESLint prompts/warnings.                                                                                                                                |\n| `prettier.ts`            | Prettier → Oxfmt migration and its prompts/warnings.                                                                                                                                                                    |\n| `tsup.ts`                | tsup → tsdown (`vp pack`) migration via `tsdown-migrate` and its prompts/warnings.                                                                                                                                      |\n| `tsx.ts`                 | tsx → `vpx` script migration, tsx dependency retention, and its prompt/warnings.                                                                                                                                        |\n| `tsconfig.ts`            | `tsconfig.json` cleanup and `types` rewriting.                                                                                                                                                                          |\n| `framework-shim.ts`      | Framework (Vue/Astro) shim detection and injection.                                                                                                                                                                     |\n| `git-hooks.ts`           | Vite+ hook defaults, project-owned hook preservation, and conservative detection of existing hook tools.                                                                                                                |\n| `setup.ts`               | `packageManager` pin, `setup-vp` GitHub Actions version, and Volta to `.node-version` migration.                                                                                                                        |\n| `core-finalization.ts`   | Finalizing an existing-Vite+ core migration (rules YAML, core package scripts).                                                                                                                                         |\n| `shared.ts`              | Cross-cutting constants, types, and tiny utilities used by **two or more** modules. The only module that other modules import from directly (see rules).                                                                |\n\n## Rules for adding / changing code\n\n1. **Add a function to the module that matches its category.** Keep it\n   `export`ed so the barrel surfaces it. If nothing fits, prefer extending an\n   existing module over creating a new one; if you do add a module, add a line\n   for it to `migration/migrator.ts`.\n\n2. **Importing another module's helper — use the barrel.** Reference\n   cross-module **functions** via the barrel:\n\n   ```ts\n   import { managedOverridePackages, rewritePackageJson } from '../migrator.ts';\n   ```\n\n   This creates an import cycle (the barrel re-exports your module), which is\n   **safe only because** these helpers are referenced _inside function bodies_\n   (at runtime), never at module-evaluation time. Preserve that invariant: do\n   not call a cross-module helper at the top level of a module.\n\n3. **`shared.ts` is the one exception — import it directly, never via the\n   barrel.** Things in `shared.ts` (e.g. `REMOVE_PACKAGES`,\n   `OPT_IN_BROWSER_PROVIDERS`, shared types) are referenced at _module-load_\n   time, so they must be imported from a fully-evaluated leaf:\n\n   ```ts\n   import { REMOVE_PACKAGES, type CatalogDependencyResolver } from './shared.ts';\n   ```\n\n   Keep `shared.ts` a **pure leaf**: it may import external packages and\n   `../utils/*` / `./report.ts` etc., but it must **not** import from any\n   sibling module or the barrel.\n\n4. **Where does a new shared thing go?** A constant / type / helper used by a\n   single module lives in that module. The moment a second module needs it,\n   move it to `shared.ts` and `export` it.\n\n5. **This split is structure only.** The barrel contains no logic; behavior\n   changes belong in the relevant module and need their unit test (and snap\n   test) updated, not the barrel.\n\n## When to add a new module\n\nDefault to extending an existing module. Add a new file only when one of these\nholds:\n\n- **A new self-contained category appears** — usually a new tool/format being\n  migrated (mirrors `eslint.ts`, `prettier.ts`, `yarn.ts`, `git-hooks.ts`).\n  Test: you can name its single responsibility without saying \"and\".\n- **An existing module outgrows readability _and_ has a clean seam** — a\n  cohesive, loosely-coupled sub-cluster. Rough trigger: **>~900–1,000 lines plus\n  a natural split point** (e.g. `catalog.ts` could become `catalog.ts` +\n  `pnpm-workspace.ts`). Size alone is not enough.\n- **Scattered helpers form a coherent theme** and consolidating them aids\n  discoverability.\n\nDo **not** add a file when: the function fits an existing category (extend it);\nit's one small helper (owning module, or `shared.ts` if shared); it would be 1–2\nfunctions with no theme (fragmentation is as bad as a monolith); or it would\nhave a tight two-way dependency with another module (they belong together).\n\nWhen you do add one: give it a single-responsibility name, add its `export *`\nline to `migration/migrator.ts`, add a row to the module table above, and decide\nits layer — if siblings reference it at **load time** it must be a pure leaf (or\nthose pieces go in `shared.ts`); helpers used only inside function bodies may\nimport from the barrel.\n\n## Validating a change\n\nFrom the repo root:\n\n```bash\nvp check                                             # format + lint + type-check\npnpm -F vite-plus exec vitest run src/migration      # migration unit tests\njust snapshot-test migration                         # migration CLI snapshot tests\n```\n\nReview any changed `.md` snapshots like code. A pure reorganization must not\nchange type-check results, the unit-test count, or snapshots. A behavior change\nshould be accompanied by the matching unit or snapshot test updates.\n","packages/cli/templates/generator/README.md":"# Vite+ Code Generator Starter\n\nA starter for creating a Vite+ code generator.\n\n## Usage\n\nFrom monorepo root:\n\n```bash\n# run and select the generator\nvp create\n```\n\nFor automation, provide the directory and every required template option:\n\n```bash\nvp create <generator-name> --no-interactive -- --directory new-package --name new-package\n```\n\nVite+ sets `VP_CREATE_INTERACTIVE=0` for non-interactive local Bingo generators.\nThis starter then validates the arguments and runs Bingo's programmatic API.\nMissing options and existing directories fail before any files are generated.\nExisting generators are copied project files and are not updated by upgrading Vite+.\nTo adopt this behavior, update their entrypoint to match this starter.\nDirect invocation uses the interactive CLI unless this variable is set to `0`.\nWhen adding template options, also add their CLI types in `bin/index.ts`.\n\n## Development\n\n```bash\n# Edit the template\ncode src/template.ts\n\n# Test the generator CLI\nvp run dev\n\n# Run tests\nvp run test\n```\n\n## Customization\n\nEdit `src/template.ts` to customize:\n\n- Options schema (using Zod)\n- File generation logic\n- Scripts and suggestions\n\nMore information about the [Bingo Templates](https://create.bingo/) can be found [here](https://create.bingo/build/concepts/creations).\n","packages/cli/templates/monorepo/README.md":"# Vite+ Monorepo Starter\n\nA starter for creating a Vite+ monorepo.\n\n## Development\n\n- Check everything is ready:\n\n```bash\nvp run ready\n```\n\n- Run the tests:\n\n```bash\nvp run -r test\n```\n\n- Build the monorepo:\n\n```bash\nvp run -r build\n```\n\n- Run the development server:\n\n```bash\nvp run dev\n```\n","packages/create-vite-plus/README.md":"# create-vite-plus\n\nCreate a new [Vite+](https://viteplus.dev) project without installing the global `vp` CLI.\n\n```bash\npnpm create vite-plus\nnpm create vite-plus@latest\nyarn create vite-plus\nbun create vite-plus\n```\n\nThis runs [`vp create`](https://viteplus.dev/guide/create) from the `vite-plus` package and uses the package manager that ran it, unless you pass `--package-manager` or create the project inside an existing workspace. Arguments are forwarded unchanged, so the first argument is the template:\n\n```bash\npnpm create vite-plus vite:application --directory my-app\npnpm create vite-plus vite -- --template react-ts\n```\n\nnpm needs `--` before options meant for `vp create`, for example `npm create vite-plus@latest -- vite:library --no-interactive`.\n","packages/prompts/README.md":"# @voidzero-dev/vite-plus-prompts\n\nTerminal prompts shared by Vite+ and tools that use its visual identity.\n\n```ts\nimport { isCancel, log, note, select } from '@voidzero-dev/vite-plus-prompts';\n\nconst framework = await select({\n  message: 'Choose a framework:',\n  options: [\n    { value: 'react', label: 'React', hint: 'TSX Pages' },\n    { value: 'vue', label: 'Vue', hint: 'Vue SFC Pages' },\n  ],\n});\n\nif (!isCancel(framework)) {\n  log.success(`Selected ${framework}`);\n  note('Run vp dev to start developing.', 'Next steps');\n}\n```\n\nBlue `›` pointers identify focused choices and active prompts. Completed steps\nand rounded box borders use gray; warnings use yellow and errors use red.\nInactive choices are dimmed, and descriptive hints appear on the focused choice.\nLogs retain severity symbols without guide bars and use compact spacing.\nNotes have complete borders and readable content by default.\n\nGuides are off by default. Pass `withGuide: true` for one call, or use\n`updateSettings({ withGuide: true })` to enable them globally. This controls the\nprompt gutter, not box borders. Logs accept `spacing` for explicit blank rows;\nnotes accept `format` and boxes accept `formatBorder` and `rounded`.\n\nSelect and multiselect prompts show compact keyboard instructions. Pass\n`showInstructions: false` to hide them. Text, password, path, and autocomplete\nvalidation supports synchronous or asynchronous functions and Standard Schema.\nPasswords accept a placeholder and always mask entered values. Path prompts\nsupport directory navigation and Tab completion; general autocomplete can opt\nin with `completeOnTab: true`.\n\n`accessible: true`, `updateSettings({ accessible: true })`, or `ACCESSIBLE=1`\nenables static prompt output for screen readers. Spinners use static output in\naccessible mode, CI, and non-TTY streams. Their `pause()` and `resume()` methods\npreserve elapsed time while excluding pauses. `CANCEL_SYMBOL` is exported for\ncallers that need the exact cancellation sentinel; `isCancel()` narrows results.\n\nThe interaction engine is `@clack/core` 1.5.1 or newer. Rendering and the shared\nvisual defaults are maintained in this package, so consumers can use the same\nappearance without implementing their own prompt renderers.\n\n## Verification\n\nFrom the repository root:\n\n```sh\nvp check packages/prompts/src\nvp test run packages/prompts/src/__tests__\npnpm --filter @voidzero-dev/vite-plus-prompts build\n```\n","packages/tools/README.md":"# tools for internal development use\n\n## Bins\n\n- json-edit: A CLI tool to edit JSON files such as package.json, used by the release workflow to stamp build versions\n\n## `tool` subcommands\n\nRun with `tool <name>`:\n\n- sync-remote: Sync upstream dependency sources and catalog versions from `.upstream-versions.json`\n- install-global-cli: Install the local build of the global `vp` CLI. The tool\n  reuses an existing `~/.vite-plus` install. Otherwise, it uses the split\n  platform data directory.\n- brand-vite: Apply Vite+ branding patches to the synced vite source (also runs at the end of sync-remote)\n- local-npm-registry: Serve locally packed checkout packages behind a real registry HTTP interface for snapshot tests, ecosystem e2e, and local `vp migrate`/`vp create` iteration\n\n## Local npm registry\n\nSee the [contributing guide](../../CONTRIBUTING.md#test-vp-migrate--vp-create-through-a-local-npm-registry) for local build and usage commands.\n\n[`src/local-npm-registry.ts`](src/local-npm-registry.ts) packs the checkout, serves its tarballs through a registry HTTP interface, and proxies other packages upstream.\n\n- Served versions use an old publish time so package-manager minimum-release-age checks allow local builds immediately.\n- Wrapped commands use temporary Yarn Berry and bun caches to avoid reusing stale local builds with the same package version.\n- The server also backs [PTY snapshot cases](../../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) and [ecosystem e2e tests](../../ecosystem-ci/patch-project.ts). Snapshots use recorded test packages and a run-scoped response cache. `local-registry = true` additionally serves the packed checkout; ecosystem and standalone runs retain upstream version merging.\n- Refresh the small snapshot registry fixtures explicitly with `node packages/tools/src/record-snapshot-registry.ts`. See the [fixture documentation](../../crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md).\n"},"files":{".claude/agents/cargo-workspace-merger.md":"---\nname: cargo-workspace-merger\ndescription: \"Use this agent when you need to merge one Cargo workspace into another, specifically when integrating a subproject's crates and dependencies into a root workspace. This includes tasks like: adding crate path references to workspace members, merging workspace dependency definitions while avoiding duplicates, and ensuring only production dependencies (not unnecessary dev dependencies) are included.\\\\n\\\\n<example>\\\\nContext: The user wants to integrate the rolldown project into their existing Cargo workspace.\\\\nuser: \\\"I need to merge the rolldown Cargo workspace into our root workspace\\\"\\\\nassistant: \\\"I'll use the cargo-workspace-merger agent to handle this integration. This involves analyzing both Cargo.toml files, identifying the crates to add, and merging the necessary dependencies.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\\\\n\\\\n<example>\\\\nContext: The user has cloned a Rust project as a subdirectory and wants to integrate it.\\\\nuser: \\\"Can you add all the crates from ./external-lib into our workspace?\\\"\\\\nassistant: \\\"I'll launch the cargo-workspace-merger agent to analyze the external library's workspace structure and merge it into your root Cargo.toml.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\"\nmodel: opus\ncolor: yellow\n---\n\nYou are an expert Rust build system engineer specializing in Cargo workspace management and dependency resolution. You have deep knowledge of Cargo.toml structure, workspace inheritance, and dependency deduplication strategies.\n\n## Your Primary Mission\n\nMerge a child Cargo workspace (located in a subdirectory) into a parent root Cargo workspace. This involves two main tasks:\n\n1. **Adding crate references**: Add all crates from the child workspace to the root workspace's `[workspace.dependencies]` section with proper path references.\n\n2. **Merging workspace dependencies**: Combine the child workspace's `[workspace.dependencies]` with the root's dependencies, ensuring no duplicates and only including dependencies actually used by the crates being merged.\n\n## Step-by-Step Process\n\n### Step 1: Analyze the Child Workspace\n\n- Read the child workspace's `Cargo.toml` (e.g., `./rolldown/Cargo.toml`)\n- Identify all workspace members from the `[workspace.members]` section\n- Extract all `[workspace.dependencies]` definitions\n\n### Step 2: Identify Crates to Add\n\n- For each workspace member, locate its `Cargo.toml`\n- Extract the crate name from `[package].name`\n- Build a list of path references in the format: `crate_name = { path = \"./child/crates/crate_name\" }`\n\n### Step 3: Analyze Dependency Usage\n\n- For each crate in the child workspace, read its `Cargo.toml`\n- Collect all dependencies from `[dependencies]`, `[dev-dependencies]`, and `[build-dependencies]`\n- Focus on dependencies that reference `workspace = true` - these need the workspace-level definition\n- Create a set of actually-used workspace dependencies\n\n### Step 4: Filter and Merge Dependencies\n\n- From the child's `[workspace.dependencies]`, only include those that are actually used by the crates\n- Check for conflicts with existing root workspace dependencies:\n  - Same dependency, same version: Skip (already exists)\n  - Same dependency, different version: Flag for manual resolution and suggest keeping the newer version\n- Exclude dev-only dependencies that aren't needed for the merged crates\n\n### Step 5: Update Root Cargo.toml\n\n- Add all crate path references to `[workspace.dependencies]`\n- Add filtered workspace dependencies to `[workspace.dependencies]`\n- Maintain alphabetical ordering within sections for cleanliness\n- Preserve any existing comments and formatting\n\n## Output Format\n\nProvide:\n\n1. A summary of crates being added\n2. A summary of dependencies being merged\n3. Any conflicts or issues requiring manual attention\n4. The exact additions to make to the root `Cargo.toml`\n\n## Quality Checks\n\n- Verify all paths exist before adding references\n- Ensure no duplicate entries are created\n- Validate that merged dependencies don't break existing crates\n- After modifications, suggest running `cargo check --workspace` to verify the merge\n- Use highest compatible semver versions (if not pinned) and merge features in crates\n\n## Important Considerations\n\n- Use `vt_path` types for path operations as per project conventions\n- Dependencies with `path` references in the child workspace may need path adjustments\n- Feature flags on dependencies must be preserved\n- Optional dependencies must maintain their optional status\n- If a dependency exists in both workspaces with different features, merge the feature lists\n\n### Workspace Package Inheritance\n\nChild crates may inherit fields from `[workspace.package]` using `field.workspace = true`. Common inherited fields include:\n\n- `homepage`\n- `repository`\n- `license`\n- `edition`\n- `authors`\n- `rust-version`\n\n**Important**: If the child workspace's `[workspace.package]` defines fields that the root workspace does not, you must add those fields to the root workspace's `[workspace.package]` section. Otherwise, crates that inherit these fields will fail to build with errors like:\n\n```\nerror inheriting `homepage` from workspace root manifest's `workspace.package.homepage`\nCaused by: `workspace.package.homepage` was not defined\n```\n\n**Steps to handle this**:\n\n1. Read the child workspace's `[workspace.package]` section\n2. Compare with the root workspace's `[workspace.package]` section\n3. Add any missing fields to the root workspace (use the root project's own values, not the child's)\n\n## Error Handling\n\n- If a crate path doesn't exist, report it clearly and skip\n- If Cargo.toml parsing fails, provide the specific error\n- If version conflicts exist, list all conflicts before proceeding and ask for guidance\n\n### Crates with Compile-Time Environment Variables\n\nSome crates use `env!()` macros that require compile-time environment variables set via `.cargo/config.toml`. These crates often have `relative = true` paths that only work when building from their original workspace root.\n\n**Example**: `rolldown_workspace` uses `env!(\"WORKSPACE_DIR\")` which is set in `rolldown/.cargo/config.toml`.\n\n**How to handle**:\n\n1. Check child workspace's `.cargo/config.toml` for `[env]` section\n2. If crates use these env vars with `relative = true`, copy those env vars to root `.cargo/config.toml` with paths adjusted to point to the child workspace directory\n3. Example: If child has `WORKSPACE_DIR = { value = \"\", relative = true }`, root should have `WORKSPACE_DIR = { value = \"child-dir\", relative = true }`\n",".claude/agents/monorepo-architect.md":"---\nname: monorepo-architect\ndescription: Use this agent when you need architectural guidance for monorepo tooling, particularly for reviewing code organization, module boundaries, and ensuring proper separation of concerns in Rust/Node.js projects. This agent should be invoked after implementing new features or refactoring existing code to validate architectural decisions and placement of functionality.\\n\\nExamples:\\n- <example>\\n  Context: The user has just implemented a new caching mechanism for the monorepo task runner.\\n  user: \"I've added a new caching system to handle task outputs\"\\n  assistant: \"I'll use the monorepo-architect agent to review the architectural decisions and ensure the caching logic is properly placed within the module structure.\"\\n  <commentary>\\n  Since new functionality was added, use the monorepo-architect agent to review the code architecture and module boundaries.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is refactoring the task dependency resolution system.\\n  user: \"I've refactored how we resolve task dependencies across packages\"\\n  assistant: \"Let me invoke the monorepo-architect agent to review the refactored code and ensure proper separation of concerns.\"\\n  <commentary>\\n  After refactoring core functionality, use the monorepo-architect agent to validate architectural decisions.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is adding cross-package communication features.\\n  user: \"I've implemented a new IPC mechanism for packages to communicate during builds\"\\n  assistant: \"I'll use the monorepo-architect agent to review where this IPC logic lives and ensure it doesn't create inappropriate cross-module dependencies.\"\\n  <commentary>\\n  When adding features that span multiple modules, use the monorepo-architect agent to prevent architectural violations.\\n  </commentary>\\n</example>\nmodel: opus\ncolor: purple\n---\n\nYou are a senior software architect with deep expertise in Rust and Node.js ecosystems, specializing in monorepo tooling and build systems. You have extensively studied and analyzed the architectures of nx, Turborepo, Rush, and Lage, understanding their design decisions, trade-offs, and implementation patterns.\n\nYour primary responsibility is to review code architecture and ensure that functionality is properly organized within the codebase. You focus on:\n\n**Core Architectural Principles:**\n\n- Single Responsibility: Each module, file, and function should have one clear purpose\n- Separation of Concerns: Business logic, I/O operations, and configuration should be clearly separated\n- Module Boundaries: Enforce clean interfaces between modules, preventing tight coupling\n- Dependency Direction: Dependencies should flow in one direction, typically from high-level to low-level modules\n\n**When reviewing code, you will:**\n\n1. **Analyze Module Structure**: Examine where new functionality has been placed and determine if it belongs there based on the module's responsibility. Look for code that crosses logical boundaries or mixes concerns.\n\n2. **Identify Architectural Violations**:\n   - Cross-module responsibilities where one module is doing work that belongs to another\n   - Circular dependencies or bidirectional coupling\n   - Business logic mixed with I/O operations\n   - Configuration logic scattered across multiple modules\n   - Violation of the dependency inversion principle\n\n3. **Suggest Proper Placement**: When you identify misplaced functionality, provide specific recommendations:\n   - Identify the correct module/file where the code should reside\n   - Explain why the current placement violates architectural principles\n   - Suggest how to refactor without breaking existing functionality\n   - Consider the impact on testing and maintainability\n\n4. **Reference Industry Standards**: Draw from your knowledge of nx, Turborepo, Rush, and Lage to:\n   - Compare architectural decisions with proven patterns from these tools\n   - Highlight when a different approach might be more scalable or maintainable\n   - Suggest battle-tested patterns for common monorepo challenges\n\n5. **Focus on Rust/Node.js Best Practices**:\n   - In Rust: Ensure proper use of ownership, traits for abstraction, and module organization\n   - In Node.js: Validate CommonJS/ESM module patterns, async patterns, and package boundaries\n   - For interop: Review FFI boundaries and data serialization approaches\n\n**Review Methodology:**\n\n1. Start by understanding the intent of the recent changes\n2. Map out the affected modules and their responsibilities\n3. Identify any code that seems out of place or creates inappropriate coupling\n4. Provide a prioritized list of architectural concerns (critical, important, minor)\n5. For each concern, explain the principle being violated and suggest a concrete fix\n\n**Output Format:**\n\nStructure your review as:\n\n- **Summary**: Brief overview of architectural health\n- **Critical Issues**: Must-fix architectural violations that will cause problems\n- **Recommendations**: Suggested improvements with rationale\n- **Positive Patterns**: Acknowledge well-architected decisions\n- **Comparison Notes**: When relevant, note how similar problems are solved in nx/Turborepo/Rush/Lage\n\nYou are pragmatic and understand that perfect architecture must be balanced with delivery speed. Focus on issues that will genuinely impact maintainability, testability, or scalability. Avoid nitpicking and recognize when 'good enough' is appropriate for the current stage of the project.\n\nWhen you lack context about the broader system, ask clarifying questions rather than making assumptions. Your goal is to ensure the codebase remains maintainable and follows established architectural patterns while evolving to meet new requirements.\n",".claude/skills/add-ecosystem-ci/SKILL.md":"---\nname: add-ecosystem-ci\ndescription: Add a new ecosystem-ci test case for testing real-world projects against vite-plus\nallowed-tools: Bash, Read, Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Add Ecosystem-CI Test Case\n\nAdd a new ecosystem-ci test case following this process:\n\n## Step 1: Get Repository Information\n\nAsk the user for the GitHub repository URL if not provided as argument: $ARGUMENTS\n\nUse GitHub CLI to get repository info:\n\n```bash\ngh api repos/OWNER/REPO --jq '.default_branch'\ngh api repos/OWNER/REPO/commits/BRANCH --jq '.sha'\n```\n\n## Step 2: Auto-detect Project Configuration\n\n### 2.1 Check for Subdirectory\n\nFetch the repository's root to check if the main package.json is in a subdirectory (like `web/`, `app/`, `frontend/`).\n\n### 2.2 Check if Project Already Uses Vite-Plus\n\nCheck the project's root `package.json` for `vite-plus` in `dependencies` or `devDependencies`. If the project already uses vite-plus, set `forceFreshMigration: true` in `repo.json`. This tells `patch-project.ts` to set `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping with \"already using Vite+\".\n\n### 2.3 Auto-detect Commands from GitHub Workflows\n\nFetch the project's GitHub workflow files to detect available commands:\n\n```bash\n# List workflow files\ngh api repos/OWNER/REPO/contents/.github/workflows --jq '.[].name'\n\n# Fetch workflow content (for each .yml/.yaml file)\ngh api repos/OWNER/REPO/contents/.github/workflows/ci.yml --jq '.content' | base64 -d\n```\n\nLook for common patterns in workflow files:\n\n- `pnpm run <command>` / `npm run <command>` / `yarn <command>`\n- Commands like: `lint`, `build`, `test`, `type-check`, `typecheck`, `format`, `format:check`\n- Map detected commands to `vp` equivalents: `vp run lint`, `vp run build`, etc.\n\n### 2.4 Ask User to Confirm\n\nPresent the auto-detected configuration and ask user to confirm or modify:\n\n- Which directory contains the main package.json? (auto-detected or manual)\n- What Node.js version to use? (22 or 24, try to detect from workflow)\n- Which commands to run? (show detected commands as multi-select options)\n- Which OS to run on? (both, ubuntu-only, windows-only) - default: both\n\n## Step 3: Update Files\n\n1. **Add to `ecosystem-ci/repo.json`**:\n\n   ```json\n   {\n     \"project-name\": {\n       \"repository\": \"https://github.com/owner/repo.git\",\n       \"branch\": \"main\",\n       \"hash\": \"full-commit-sha\",\n       \"forceFreshMigration\": true // only if project already uses vite-plus\n     }\n   }\n   ```\n\n2. **Add to `.github/workflows/e2e-test.yml`** matrix:\n   ```yaml\n   - name: project-name\n     node-version: 24\n     directory: web # only if subdirectory is needed\n     command: |\n       vp run lint\n       vp run build\n   ```\n\n## Step 4: Verify\n\n### 4.1 Build fresh tgz packages\n\nAlways rebuild tgz packages from latest source to avoid using stale cached versions:\n\n```bash\n# Rebuild the global CLI first (includes Rust binary + NAPI binding)\npnpm bootstrap-cli\n\n# Pack fresh tgz files into tmp/tgz/\nrm -rf tmp/tgz && mkdir -p tmp/tgz\ncd packages/core && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/test && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/cli && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\nls -la tmp/tgz\n```\n\n### 4.2 Clone and test locally\n\n```bash\nnode ecosystem-ci/clone.ts project-name\n```\n\n### 4.3 Patch and run commands\n\n```bash\n# Run from the ecosystem-ci temp directory\ncd $(node -e \"const os=require('os'); console.log(os.tmpdir() + '/vite-plus-ecosystem-ci')\")\n\n# Migrate the project (uses tgz files from tmp/tgz/)\nnode /path/to/vite-plus/ecosystem-ci/patch-project.ts project-name\n\n# Run the configured commands\ncd project-name\nvp run build\n```\n\n3. **Add OS exclusion to `.github/workflows/e2e-test.yml`** (if not running on both):\n\n   For ubuntu-only:\n\n   ```yaml\n   exclude:\n     - os: windows-latest\n       project:\n         name: project-name\n   ```\n\n   For windows-only:\n\n   ```yaml\n   exclude:\n     - os: ubuntu-latest\n       project:\n         name: project-name\n   ```\n\n## Important Notes\n\n- The workflow matrix `directory` field is optional - only add it if the project commands must run in a subdirectory (like a workspace member)\n- `patch-project.ts` always runs `vp migrate` and `vp install` at the clone root; `vp migrate` rejects workspace member targets\n- `forceFreshMigration` is required for projects that already have `vite-plus` in their package.json — it sets `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping\n- OS exclusions are added to the existing `exclude` section in the workflow matrix\n",".claude/skills/bump-vite-task/SKILL.md":"---\nname: bump-vite-task\ndescription: Bump vite-task git dependency to the latest main commit. Use when you need to update the vite-task git-dependency crates (vt, fspy, vt_pty_terminal_test, and friends; the authoritative set lives in Cargo.toml) in vite-plus.\nallowed-tools: Read, Grep, Glob, Edit, Bash, Agent, WebFetch\n---\n\n# Bump vite-task to Latest Main\n\nUpdate the vite-task git dependency in `Cargo.toml` to the latest commit on the vite-task main branch, fix any breaking changes, and create a PR.\n\n## Steps\n\n### 1. Get current and target commits\n\n- Read `Cargo.toml` and find the current `rev = \"...\"` for any vite-task git dependency; enumerate the full set with `grep 'voidzero-dev/vite-task' Cargo.toml` instead of trusting a hardcoded list. They all share the same revision.\n- Get the latest commit hash on vite-task's main branch:\n  ```bash\n  git ls-remote https://github.com/voidzero-dev/vite-task.git refs/heads/main\n  ```\n\n### 2. Update Cargo.toml\n\n- Replace **all** occurrences of the old commit hash with the new one in `Cargo.toml`. The set of crates sharing the vite-task revision changes over time; the grep from step 1 is the authoritative list, so update every entry it returns.\n- The commented `[patch.\"https://github.com/voidzero-dev/vite-task.git\"]` section near the bottom of `Cargo.toml` mirrors the same crates for local vite-task development; keep it in sync when crates are added or removed (a plain rev bump does not touch it).\n\n### 3. Ensure upstream dependencies are cloned\n\n- `cargo check` requires the `./rolldown` and `./vite` directories to exist (many workspace path dependencies point to `./rolldown/crates/...`).\n- Locally, clone them using the commit hashes from `packages/tools/.upstream-versions.json`.\n- CI handles this automatically via the `.github/actions/clone` action.\n\n### 4. Verify compilation\n\n- Run `cargo check` to ensure the new vite-task compiles without errors.\n- If there are compilation errors, these are **breaking changes** from vite-task. Fix them in the vite-plus codebase (the consuming side), not in vite-task.\n- Common breaking changes include: renamed functions/methods, changed function signatures, new required fields in structs, removed public APIs.\n\n### 5. Run tests\n\n- Run `cargo test -p vp_command -p vp_error -p vp_pm_cli -p vp_js_runtime -p vp_migration -p vp_shared -p vp_static_config -p vite-plus-cli -p vp_global_cli` to run the vite-plus crate tests.\n- Note: Some tests require network access (e.g., `vp_pm_cli::package_manager` tests, `vp_global_cli::commands::env` tests). These may fail in sandboxed environments. Verify they also fail on the main branch before dismissing them.\n- Note: `cargo test -p vt` will NOT work because vt is a git dependency, not a workspace member.\n- The PTY snapshot suite (`crates/vp_cli_snapshots`) is excluded from `just test`; it is covered in step 6.\n\n### 6. Update snapshot tests\n\nvite-task changes often affect CLI output, which means snapshot tests need updating. Common output changes:\n\n- **Status icons**: e.g., cache hit/miss indicators may change\n- **New CLI options**: e.g., new flags added to `vp run` that show up in help output\n- **Cache behavior messages**: e.g., new summary lines about cache status\n- **Task output formatting**: e.g., step numbering, separator lines\n\n**PTY snapshot suite (`crates/vp_cli_snapshots`):**\n\n- A bump can break it two ways: runner compilation (it consumes vite-task's `vt_pty_terminal_test`, `vt_pty_terminal_test_client`, and `vt_snapshot_test` crates directly, so their API changes surface here; fix in the runner) and recorded CLI output.\n- Update output locally with real assertions: `UPDATE_SNAPSHOTS=1 just snapshot-test`, then review the `.md` diffs like code. Without a built `packages/cli/dist`, run the global flavor only: `VP_SNAP_SKIP_FLAVORS=local UPDATE_SNAPSHOTS=1 just snapshot-test`.\n- Windows runs in the `CLI snapshot test (Windows)` CI job via a cross-compiled nextest archive; snapshots are OS-shared, so a Windows-only diff there means a redaction gap, not a re-record.\n- Reference: `crates/vp_cli_snapshots/tests/cli_snapshots/README.md`.\n\n### 7. Review changelog and update docs\n\n- Fetch the vite-task `CHANGELOG.md` diff between old and new commits to see what changed:\n  ```\n  https://raw.githubusercontent.com/voidzero-dev/vite-task/<new-hash>/CHANGELOG.md\n  ```\n- Review each changelog entry and determine if it affects user-facing behavior: new CLI options, changed defaults, new config fields, removed features, etc.\n- The changelog contains links to the corresponding vite-task PRs. For complex changes, check the PR description and code diff (especially any docs changes in the PR) to understand the full scope of the change.\n- If user-facing changes are found, update the relevant docs in `docs/` (e.g., `docs/guide/`, `docs/config/`).\n- Common doc updates include:\n  - **New CLI flags/options**: Update the relevant config doc (e.g., `docs/config/run.md`, `docs/config/build.md`)\n  - **New features or commands**: Add or update the relevant guide page (e.g., `docs/guide/cache.md`)\n  - **Changed defaults or behavior**: Update any docs that describe the old behavior\n  - **Removed/deprecated options**: Remove or mark as deprecated in the relevant docs\n- If no user-facing changes are found, skip this step.\n\n### 8. Create the PR\n\n- Commit message: `chore: bump vite-task to <short-hash>`\n- PR title: `chore: bump vite-task to <short-hash>`\n- PR body: Link to vite-task CHANGELOG.md diff between old and new commits:\n  ```\n  https://github.com/voidzero-dev/vite-task/compare/<old-hash>...<new-hash>#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed\n  ```\n\n### 9. Verify CI\n\nAfter creating the PR, automatically watch CI without asking the user first. Ensure the `done` check passes. Key checks to monitor:\n\n- **Lint**: Clippy and format checks\n- **Test** (Linux, Mac, Windows): Rust unit tests\n- **CLI snapshot test** (Linux, Mac, Windows): the PTY snapshot suite - most likely to fail on a vite-task bump (runner compiles against vite-task crates AND asserts CLI output)\n- **Run task**: Task runner integration tests\n- **Security Analysis**: Fix any `cargo deny` advisories. It runs only when `Cargo.lock` changes, so a green `main` does not mean the lockfile is clean.\n\nThe only **required** status check for merging is `done`, which aggregates the other checks (excluding Security Analysis).\n\n## Notes\n\n- Building the full CLI locally (`pnpm bootstrap-cli`) requires the rolldown Node.js package to be built first, which is complex. Prefer the global-only snapshot command above or CI when no local CLI build is available.\n- `Cargo.lock` is automatically updated by cargo when you change the revision in `Cargo.toml`.\n",".claude/skills/release-manager/SKILL.md":"---\nname: release-manager\ndescription: Run the standard vite-plus release process end-to-end as the release manager. Covers preparing the release PR, syncing the NAPI binding version, writing the categorized changelog, smoke-testing via a preview build, handling release-branch CI failures, merging, and post-release verification and announcements. Use when asked to cut, prepare, or manage a vite-plus release (e.g. \"release v0.2.3\", \"act as release manager\").\nallowed-tools: Bash, Read, Edit, Write, Grep, Glob, WebFetch\n---\n\n# Vite+ Release Manager\n\nRun a standard vite-plus release from version bump to published announcement. Any maintainer with repo write access can follow this; the only extra privilege needed is approval rights on the `release` GitHub environment (step 7).\n\n## Usage\n\n```\n/release-manager                    # start a new release: ask for the target version, begin at step 1\n/release-manager X.Y.Z              # start a new release for that version\n/release-manager <PR URL or #N>     # take over an in-flight release\n```\n\nWhen given a release PR (URL or number), do not start from step 1. First audit the release's current state, then continue from the earliest unfinished step:\n\n- Is the binding version synced? (step 2: `grep -c \"'<prev>'\" packages/cli/binding/index.cjs` on the release branch)\n- Do the release version examples in the migration guide and the setup, migration, and upgrade prompts match `packages/cli/package.json`? (step 2)\n- Is the PR description still the `prepare_release` boilerplate, or already a categorized changelog? (step 3)\n- Is a preview build present and for the current head? (step 4)\n- Does `main` have commits the release branch lacks? (`git log origin/release/vX.Y.Z..origin/main`, step 5)\n- What is CI status? (`gh pr checks <PR#>`, step 5)\n- Already merged? Check the Release workflow (`gh run list --workflow Release --repo voidzero-dev/vite-plus`) and whether the GitHub release body is still the generated stub, then continue at step 7 or 8.\n\nReport the detected state before making changes, so the previous release manager's work is not redone or overwritten.\n\nBefore post-release work, fetch `origin/main` and read its copy of this skill (`git show origin/main:.claude/skills/release-manager/SKILL.md`). A checkout left behind after the merge can contain superseded release or announcement instructions.\n\n## Pipeline overview\n\n1. `Prepare Release` workflow bumps versions and opens the release PR (`release/vX.Y.Z` -> `main`).\n2. Release manager: sync `binding/index.cjs` and the release versions in the documentation and prompts, write the changelog PR description, offer the preview-build smoke test (recommend it when the release has more than 10 commits since the previous tag), get CI green.\n3. Merging the PR pushes a `packages/cli/package.json` change to `main`, which triggers `release.yml`: build, manual approval gate, npm publish, GitHub release, Docker image, Discord notification.\n4. Release manager: polish the GitHub release notes, verify installs, announce.\n\nCanonical sources: `.github/workflows/prepare_release.yml`, `.github/workflows/release.yml`, `.github/workflows/publish-preview.yml`.\n\n## 1. Start the release\n\n```bash\ngh workflow run prepare_release.yml --repo voidzero-dev/vite-plus -f version=X.Y.Z\n```\n\nThe workflow bumps `packages/cli/package.json`, `packages/core/package.json`, `packages/cli/binding/Cargo.toml`, and `crates/vp_global_cli/Cargo.toml`, refreshes `Cargo.lock`, and opens a PR titled `release: vX.Y.Z` from branch `release/vX.Y.Z`. The PR body ends with `Merging this PR will trigger the release workflow.` and that line must survive every later edit.\n\n## 2. Sync release versions (required every release)\n\n### NAPI binding\n\nNAPI bakes the package version into version checks in `packages/cli/binding/index.cjs` (26+ sites). `prepare_release` bumps `package.json` but does not regenerate this file, so CI's `Ensure no unexpected file changes after build` step in the `CLI E2E test` job fails until it is synced. Do this immediately; do not wait for CI to fail.\n\n```bash\ngit fetch origin release/vX.Y.Z && git checkout release/vX.Y.Z\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs          # non-zero means sync needed\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs\n```\n\nApply two whole-file text replacements (`<prev>` is the previous release version, e.g. `0.2.1`):\n\n1. `'<prev>'` -> `'<curr>'`\n2. `expected <prev> but got` -> `expected <curr> but got`\n\nThen confirm the replacement took; both counts must now be zero:\n\n```bash\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs                 # 0\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs  # 0\n```\n\nDo not regenerate via a full build; the text replace is deterministic and byte-identical to what `napi build` would produce for a version-only bump. Commit with this exact message shape (it makes `git log --grep` find the sync across releases) and push:\n\n```\nchore(release): sync binding/index.cjs version to <curr>\n\nNAPI bakes the package.json version into binding/index.cjs version\nchecks. The prepare_release workflow bumps package.json but does not\nregenerate this file, so the CI build's regeneration step produces a\ndiff that the post-build no-unexpected-changes guard rejects.\n```\n\n### Documentation and prompts\n\nUse the version in the release branch's `packages/cli/package.json` as the target release version in these files:\n\n- `docs/guide/migrate.md`: pnpm and npm migration command examples and matching release prose.\n- `docs/.vitepress/theme/data/migration-prompts.ts`: `setupPrompt`, `migrationPrompt`, `upgradePrompt`, and their shared instructions, including command examples and matching release prose. `CopyPrompt` uses `setupPrompt` on both the homepage and Getting Started guide.\n\nUpdate every `--package=vite-plus@<curr>` pin and the corresponding `For the <curr> release` and `Replace <curr>` text. Keep an exact version; do not replace it with a placeholder, a major range, or `latest`.\n\nPreserve historical versions such as the migration's source version and the release that introduced a breaking change. Leave Node.js requirements, bundled tool versions, and preview-registry instructions unchanged unless their requirements change.\n\nCommit these updates on the release branch with the binding sync or in a separate release-version sync commit. Recheck both files and the binding after a target-version change or a merge from `main`. Before merging, confirm that the guide and all three prompts use the target release in both package-manager commands and their matching prose, then run `git diff --check`.\n\nOnly these release-version sync commits go directly on the release branch. Everything else goes through `main` (see step 5).\n\n## 3. Write the release PR description\n\nThe release tag does not exist yet, so read release files from the PR head branch and generate notes against `main`:\n\n```bash\ngit fetch --tags && git tag --sort=-version:refname | head -5   # find <prev>\ngit log --oneline v<prev>..origin/main\ngh api repos/voidzero-dev/vite-plus/releases/generate-notes \\\n  -f tag_name=v<curr> -f previous_tag_name=v<prev> -f target_commitish=main\n\ngit show origin/release/v<curr>:packages/core/package.json          # bundledVersions: vite, rolldown, tsdown\ngit show origin/release/v<curr>:packages/tools/.upstream-versions.json  # vite/rolldown commit hashes\ngit show origin/release/v<curr>:pnpm-workspace.yaml                 # vitest/oxlint/oxlint-tsgolint/oxfmt catalog pins\n```\n\n### Structure\n\n```markdown\n<One or two sentences on the release theme. Do not repeat the PR title as an opener line; GitHub renders the title directly above the body, and step 8 would only strip it again. When a blog post accompanies the release, read it first (via its preview URL if not yet deployed), align the theme with it, and link the final URL here even if that URL is not live yet.>\n\n### Breaking Changes\n\n### Features\n\n### Fixes & Enhancements\n\n### Refactor\n\n### Docs\n\n### Chore\n\n### Bundled Versions\n\n### Upgrade\n\n### New Contributors\n\n**Full Changelog**: https://github.com/voidzero-dev/vite-plus/compare/v<prev>...v<curr>\n\n---\n\nMerging this PR will trigger the release workflow.\n```\n\n### Categorization rules\n\n- Every PR from `generate-notes` appears exactly once, except fully reverted changes described below and bot-authored PRs that carry nothing for a user to read or act on (a docs stats refresh, a badge update). Keep bot PRs that do change what users get, such as the upstream dependency upgrades. Record each omitted PR and its reason when reporting validation counts.\n- **Breaking Changes goes first, above Features, and only when the release has one.** A rename is breaking only when the old name stops working; if a deprecated alias is retained it is not breaking, so keep the two in different sections rather than merging them into one entry. Give each breaking entry an old -> new table when several names change, plus one line telling readers where to update (shell profile, CI job, Dockerfile). Do not editorialize about the version number.\n- When several breaking changes affect different workflows, group them under short `####` headings. Explain the changed behavior and required action before each table; keep automatic migration steps separate from changes users must make manually.\n- **Describe the net change between the two released versions, not intra-cycle churn.** When several PRs touch the same area within one release (one narrows a behavior, a later one broadens it back), the reader only sees the delta from `v<prev>` to `v<curr>`; describe that once, listing every PR number, and do not narrate a regression that was introduced and then fixed inside the cycle. Apply this to the intro/theme sentence too.\n- If a change and its complete revert are both unreleased, omit both when they leave no net change. Remove sections with no remaining entries. A revert of behavior in the previous release still needs an entry.\n- `feat` -> Features, `fix` -> Fixes & Enhancements, `refactor` and `revert` -> Refactor (never Chore), `docs` -> Docs, `test` / `ci` / `chore` -> Chore.\n- **Place an entry by its user impact when the prefix disagrees.** A `fix` that gives an existing command new observable behavior (for example, starting to set environment variables for child processes) belongs in Features. A fix that makes a command reject input it used to drop silently stays in Fixes & Enhancements, not Breaking Changes.\n- `feat(docs)` goes in Docs when the user-facing surface is the docs site.\n- **Docs means the published docs site, not contributor files.** A `docs` commit that changes an RFC, `AGENTS.md`, the repo map, or a skill under `.claude/` belongs in Chore: a vite-plus user never reads those. Docs should hold only entries a reader could go and look at on the site or in the README.\n- **Describe behaviour, not resolution logic.** An entry states what a user now observes. Rules the implementation follows internally (target-selection signals, config precedence, detection order) belong in the RFC or the PR, not the changelog. If an entry needs a nested list to explain how a decision is reached, cut it down to the outcome.\n- **A breaking change needs its migration path.** State what existing installs or projects do by default, then how to move to the new behaviour deliberately, then what that costs. Link the guide rather than restating it, and say plainly when doing nothing is a valid choice.\n- There is no separate Highlights section. Lead Features with up to 5 new capabilities a vite-plus user will notice, plus security fixes, then the remaining features; the upstream upgrade entry stays last. Bug fixes go in Fixes & Enhancements even when they are prominent. Skip developer-tooling-only conveniences.\n- Entry format: `Description ([#N](https://github.com/voidzero-dev/vite-plus/pull/N)), by @author`. Describe the user-visible behavior, not the implementation. Group supporting implementation PRs under the user-visible change they enable instead of giving them separate entries. Never include defensive edge cases or internal mechanics unless users need them to use or understand the feature; use concrete behavior instead of internal UI taxonomy that needs extra context. For a fix to an intermittent failure, say that it was rare and when it happened, so the entry does not read as if it always failed. When a PR carries over a superseded PR from another author (its body says so, or `gh pr view N --json commits` lists their commits), credit both authors.\n- **Upstream dependency upgrade PRs** (`feat(deps): upgrade upstream dependencies`): consolidate all of them into one Features entry with net oldest-to-latest version changes (e.g. `vite 8.0.16 -> 8.1.2`), listing every PR number. Check the upgraded range for security fixes (search the upstream changelog for CVE/GHSA); if present, add a dedicated security entry quoting severity and linking the advisory. Cite only published advisories: a draft advisory that maintainer access lets you see is still embargoed, so keep it and its fix's security framing out of the notes and the announcement. When oxfmt or oxlint changed version, add one clause telling users the new versions can flag code that passed before, so they should run `vp fmt` after upgrading if their CI runs `vp check`; in ecosystem testing this is reliably the largest single class of post-upgrade CI failures. Put this entry last in Features as a parent bullet carrying the PR links and credits, with one sub-item per upgraded tool and one per user-facing consequence (new formatting or lint behavior, migration changes, compatibility notes); a single long line is hard to read.\n- **vite-task bumps** (`bump vite-task to <commit>`): expand the full rev range (compare `Cargo.toml` at `v<prev>` vs the release branch), run `git log <old>..<new>` in the local vite-task checkout, and read vite-task's `CHANGELOG.md` at the new commit for wording. Promote user-visible upstream changes into Features / Fixes with `[vite-task#N](https://github.com/voidzero-dev/vite-task/pull/N)` links, crediting the upstream PR author (`gh pr view N --repo voidzero-dev/vite-task --json author`). Cross-repo link format is `[vite-task#N]` / `[vite#N]`, not `[owner/repo#N]`.\n- New Contributors: copy from `generate-notes`, exclude bots (`renovate[bot]`, `voidzero-guard[bot]`, `github-actions[bot]`), list as inline `@mentions`. `generate-notes` only covers vite-plus, so also add first-time contributors from the expanded vite-task range: an author is new when `gh api -X GET search/issues -f q='repo:voidzero-dev/vite-task is:pr is:merged author:<login> merged:<<date of the range start>' -q .total_count` is 0. List them with the others, without a repository label, in the release notes and the Discord thanks line.\n\n### Bundled Versions table\n\n| Tool            | Version | Source                                                                  | Changelog                                                              |\n| --------------- | ------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| vite            | `X.Y.Z` | [`<short-sha>`](https://github.com/vitejs/vite/commit/<full-sha>)       | [X.Y.Z](https://github.com/vitejs/vite/releases/tag/vX.Y.Z)            |\n| rolldown        | `X.Y.Z` | [`<short-sha>`](https://github.com/rolldown/rolldown/commit/<full-sha>) | [X.Y.Z](https://github.com/rolldown/rolldown/releases/tag/vX.Y.Z)      |\n| tsdown          | `X.Y.Z` | [npm](https://npmx.dev/package/tsdown/v/X.Y.Z)                          | [X.Y.Z](https://github.com/rolldown/tsdown/releases/tag/vX.Y.Z)        |\n| vitest          | `X.Y.Z` | [npm](https://npmx.dev/package/vitest/v/X.Y.Z)                          | [X.Y.Z](https://github.com/vitest-dev/vitest/releases/tag/vX.Y.Z)      |\n| oxlint          | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint/v/X.Y.Z)                          | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxlint_vX.Y.Z) |\n| oxlint-tsgolint | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint-tsgolint/v/X.Y.Z)                 | [X.Y.Z](https://github.com/oxc-project/tsgolint/releases/tag/vX.Y.Z)   |\n| oxfmt           | `X.Y.Z` | [npm](https://npmx.dev/package/oxfmt/v/X.Y.Z)                           | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxfmt_vX.Y.Z)  |\n\nvite and rolldown are built from pinned commits, so link the commit. The npm-installed tools link to npmx.dev. The Changelog column links the upstream release notes for every version in the upgraded range, comma-separated (for example the two patch releases between the previous and new pin); write `unchanged` when the version did not move.\n\n### Style rules\n\n- No em dashes or en dashes anywhere in the title or body. Use commas, colons, or parentheses.\n- Keep the PR title plain (`release: vX.Y.Z`) and the GitHub release title plain (`vite-plus vX.Y.Z`); the theme goes in the opening sentence. Lead the opening theme with the most important user-visible behavior. Avoid vague benefit-only wording that the body must explain.\n- When naming a package version in prose or a heading, use one inline literal, `package@version`. Keep separate version columns in tables.\n- Link unfamiliar technical abbreviations to the relevant documentation section; keep the short abbreviation as the link text.\n- The Upgrade section is a `vp upgrade` code block.\n- Apply via a temp file, never a heredoc (heredoc quoting can escape backticks inside the table and break rendering):\n\n```bash\ngh pr edit <PR#> --repo voidzero-dev/vite-plus --title \"release: vX.Y.Z\" --body-file /tmp/pr-body.md\n```\n\n### Validate before finishing\n\n```bash\nBODY=$(gh pr view <PR#> --repo voidzero-dev/vite-plus --json body -q '.body')\n# every generate-notes PR present (minus documented omissions), none duplicated:\necho \"$BODY\" | grep -oE 'voidzero-dev/vite-plus/pull/[0-9]+' | sort -u | wc -l\necho \"$BODY\" | grep -oE '(vite-plus|vite-task)/pull/[0-9]+' | sort | uniq -d   # must be empty\necho \"$BODY\" | grep -nE '[—–]'                                                  # must be empty\necho \"$BODY\" | grep -c '\\\\`'                                                    # must be 0 (escaped backticks)\necho \"$BODY\" | tail -1                                                          # boilerplate closing line intact\n```\n\nDiff the body's PR numbers against `generate-notes` rather than only counting them: a count alone hides one missing entry offsetting one extra. Every number in the missing list must have a documented reason for omission.\n\n## 4. Preview build smoke test (before merging)\n\nThis step runs **after the changelog (step 3) is complete and before merging (step 6)**. **Always ask the release manager whether to run it, and ask about both levels explicitly** (the local `vp migrate` sweep, and the fork-PR CI validation below); never silently skip either, and do not add the label on your own.\n\nDecide what to recommend before you ask, by counting the commits the release actually contains:\n\n```bash\ngit rev-list --count v<prev>..origin/main\n```\n\n**Recommend running the smoke test when that count is above 10**, or when the release touches migrate/create behavior, package-manager or install-path handling, or the native bindings, whatever the count. Recommend skipping only for a release that is both small (10 commits or fewer) and clear of those areas. State the count and your recommendation in the question so the release manager can overrule it, and say roughly what it costs, since the full catalog runs for hours.\n\nIf the release manager approves, **read and follow [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md) first**, then validate against the **full ecosystem-ci catalog** (every runnable fork), not a single project.\n\nIf the release manager says yes:\n\n1. Add the `preview-build` label to the release PR to publish installable `0.0.0-commit.<head-sha>` builds through the registry bridge:\n\n   ```bash\n   gh pr edit <PR#> --repo voidzero-dev/vite-plus --add-label \"preview-build\"\n   ```\n\n2. Wait for the `Publish preview build` workflow run on the release branch to succeed (it packs the built package directories and registers the commit with the registry bridge, then comments the build info on the PR).\n3. Verify the build against every runnable project in the catalog with the `test-pkg-pr-new-migrate` skill: it runs `vp migrate` from the preview commit against each local checkout, with dependencies resolved through the registry bridge. Report the outcome to the release manager before moving on.\n\n**The catalog.** The smoke-test catalog and the local-setup rules live in the ecosystem-ci org: [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md), with the machine-readable list in [`ecosystem.json`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/ecosystem.json) (each fork's upstream, tracked branch, and package manager). Run every runnable fork; filter `ecosystem.json` with `jq` to skip non-JS `other` repos and any the release manager says to ignore. Forks pinned to the immediately previous release exercise a real upgrade rather than a no-op.\n\n> **Mandatory: open any test PR against the `vite-plus-ecosystem-ci` fork, never the upstream repo.** `gh pr create` inside a fork defaults its base repo to the parent (upstream), so pass `--repo vite-plus-ecosystem-ci/<repo>` (or run `gh repo set-default vite-plus-ecosystem-ci/<repo>` first). See TESTING.md.\n\n`test-pkg-pr-new-migrate` needs a **local** checkout on the fork's **tracked branch** (often not the default branch, e.g. `vue-core` tracks `minor`). Clone under one directory so the whole test environment cleans up in one step:\n\n```bash\nrepo=<repo>; branch=<tracked-branch>   # from ecosystem.json\ngit clone git@github.com:vite-plus-ecosystem-ci/$repo.git ~/git/github.com/vite-plus-ecosystem-ci/$repo\ngit -C ~/git/github.com/vite-plus-ecosystem-ci/$repo checkout \"$branch\"\n# ... run the harness against ~/git/github.com/vite-plus-ecosystem-ci/$repo ...\n# cleanup after the release: rm -rf ~/git/github.com/vite-plus-ecosystem-ci\n```\n\nThe `.github` repo also ships `scripts/setup-local.sh <repo>` (or `--all`), which does the clone, tracked-branch checkout, remotes, and fork base-repo pinning from the manifest in one step.\n\n**Sync every fork to upstream before you test anything.** The forks drift, often by hundreds of commits, so a checkout straight from `origin` validates stale code and any PR you open against it carries all that drift instead of just the upgrade. From the `.github` checkout, run the safe sync command before the local sweep:\n\n```bash\nscripts/sync-forks.sh --all\n```\n\nThe command fast-forwards only forks with no fork-only commits. Exit code `2` means that at least one fork needs a sync or manual work. Resolve or explicitly skip each reported fork. Never overwrite a divergent tracked branch.\n\nRun `scripts/sync-forks.sh \"$repo\"` again immediately before you open each fork PR. If it prints `REOPEN`, close and reopen the current release PR so GitHub calculates a new merge base. Close superseded PRs and delete their branches. TESTING.md carries the full procedure.\n\n**Validate in the project's own CI.** Beyond the local `vp migrate`, exercise the prerelease in the fork's real CI by opening a draft PR on the fork, following \"Smoke-test via a fork PR\" in TESTING.md: branch `update-vite-plus-prerelease-test-<version>` synced from `source`, apply the upgrade, open a **draft** PR on the fork (never upstream) **assigned to the release manager**, then watch its checks for upgrade-related failures. Offer this alongside the local sweep rather than treating it as an afterthought; it is the only level that exercises each project's own build and tests. Some projects' CIs install with a non-standard tool that cannot resolve preview builds through the bridge `.npmrc` (e.g. cnpmcore's `utoo`), so check the install step before trusting fork-CI results.\n\nThe workflow triggers only on the `labeled` event, not on new pushes. To rebuild after the head moves (e.g. after a step 5 merge from `main`), remove and re-add the label (this cancels an in-flight build for the branch). A stale build whose diff to the new head is test-only is still valid for smoke testing; ask before re-triggering.\n\nRecord each project's base commit, starting version, resolved preview commit, fork PR head, and CI run. After a new preview build, identify which projects ran again and which retain earlier evidence. Do not report a partial repeat as a full-catalog run on the new preview.\n\n### Example (v0.2.2, PR #2016)\n\nChangelog complete, CI green, release manager approved the smoke test. A build existed for head `06708538`; the head had since moved by a test-only merge from `main`, so that build was still valid and was not re-triggered.\n\nHere the target was vibe-dashboard `main` (a `vite-plus-ecosystem-ci` fork, pnpm monorepo on `vite-plus 0.2.1`, i.e. the previous release), so the run exercised the common upgrade path. Pass the release PR number; the harness resolves it through the bridge to the latest published immutable commit and prints the resolved SHA (confirm it matches the build you expect). Pass a full commit SHA instead only to pin a specific build when several have been published:\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh 2016 ~/git/github.com/vite-plus-ecosystem-ci/vibe-dashboard --no-interactive\n```\n\nA passing run looks like:\n\n```\n◇ Updated . to Vite+ 0.0.0-commit.06708538...\n• Dependencies:\n    vite-plus  0.2.1 → 0.0.0-commit.06708538...\n    vite             → 8.1.2\n✓ Dependencies installed in 5.7s\n\nMigration worktree changes (.npmrc force-staged so it survives .gitignore):\nA  .npmrc                      # bridge registry written by vp migrate\n M package.json / pnpm-workspace.yaml / pnpm-lock.yaml\n\nFound 1 version of @voidzero-dev/vite-plus-core\nFound 1 version of vite-plus\nFound 1 version of vitest\n```\n\nPass criteria: the upgrade lands on the `0.0.0-commit.<sha>` build, the install succeeds through the bridge registry, and each of `@voidzero-dev/vite-plus-core`, `vite-plus`, and `vitest` resolves to exactly ONE version (`vitest` at the bundled upstream version). Multiple or stale versions mean the migration or install is broken: stop and treat it as a release blocker. Report the outcome to the release manager either way.\n\nOne exception to rule out first: `0.0.0-commit.<sha>` is a prerelease, and a `*` range does not match prereleases. A package that declares an optional `vite-plus: '*'` peer (oxlint and oxfmt do) can therefore keep a stale `vite-plus` from the old lockfile under pnpm, so the check reports two versions. If the previous-release control resolves to one version and the extra copy hangs only off such a peer, the duplicate is a preview-build artifact.\n\n### Triaging failures across the catalog\n\nAcross the full catalog most failures are not regressions, and reporting them as \"N failed\" without triage is useless to the release manager. Sort every failure into one of these before drawing any conclusion:\n\n- **Registry-bridge fetch flakes.** Check these first, because they are common and they masquerade as something far worse. The bridge drops tarball requests under load: pnpm logs `error (23). Will retry`, or the install dies with `ECONNRESET  aborted`. The dangerous case is a platform binding, because `@voidzero-dev/vite-plus-<platform>` is an **optional** dependency: when its download exhausts the retries, the installer skips it and still reports success, and the job then fails much later at the first command that loads the binding, with `Cannot find native binding` / `Cannot find module '@voidzero-dev/vite-plus-linux-x64-gnu'`. That is a local `node_modules` resolution failure and says nothing about the registry, but NAPI's loader appends generic \"npm has a bug related to optional dependencies\" boilerplate that reads like a publishing problem. Do not conclude the addon was unpublished; the bridge publishes every platform package for every commit build. Confirm, then re-run:\n\n  ```bash\n  curl -s \"https://registry-bridge.viteplus.dev/@voidzero-dev%2fvite-plus-linux-x64-gnu\" \\\n    | python3 -c \"import json,sys; print('0.0.0-commit.<sha>' in json.load(sys.stdin)['versions'])\"\n  gh run rerun <run-id> --failed --repo <owner>/<repo>\n  ```\n\n  Grep every failing log for `error (23)` and `ECONNRESET` before classifying it as anything else. In one release this single cause accounted for 8 fork failures, all of which passed on re-run.\n\n- **Local TLS inspection.** If every package fails locally with `ERR_PNPM_META_FETCH_FAIL ... fetch failed` while `curl` to the bridge succeeds, a TLS-inspecting client (such as a corporate zero-trust agent) may be re-signing `registry-bridge.viteplus.dev` with a root that the OS trusts but Node does not. `node -e \"fetch('https://registry-bridge.viteplus.dev/vite-plus')\"` then fails with `SELF_SIGNED_CERT_IN_CHAIN`. Export that root certificate and set `NODE_EXTRA_CA_CERTS` for the harness and every control run. A follow-up \"latest release is ...\" message comes from pnpm's stale metadata cache and is a symptom, not the cause.\n\n- **Preview-build artifacts.** These are caused by the `0.0.0-commit.<sha>` version string itself and cannot happen for a real npm release, so they are never blockers. The recurring ones: pnpm `ERR_PNPM_TRUST_DOWNGRADE` (\"possible package takeover\"), npm `ETARGET` from a `before`/min-release-age policy, bun `minimum release age`, `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` when a project declares `vite` as a peer (migrate writes the `npm:@voidzero-dev/vite-plus-core@...` alias there), `ERR_PNPM_TARBALL_URL_MISMATCH` or a failed supply-chain policy check against the bridge tarball URLs, Docker builds whose context does not carry the bridge `.npmrc`, project scripts that query `vite@*` or `vite-plus@*` and find nothing (the prerelease does not match `*`), and npm 10 failing with `Unable to resolve reference $vite-plus` on a nested `overrides` entry. Confirm the last two by swapping the commit version for a real release in a scratch copy.\n- **Pre-existing failures.** Prove it rather than asserting it, with whichever control is cheaper: install the previous release into an isolated home and re-run the same command, or check whether the fork's base branch CI already fails. The isolated-home control is the highest-value technique in this step, since it converts a scary-looking failure into a one-line fact:\n\n  ```bash\n  VP_HOME=$HOME/.cache/vp-control-<prev> VP_VERSION=<prev> VP_NODE_MANAGER=no \\\n    VP_SELF_SETUP_NO_MODIFY_PATH=1 bash packages/cli/install.sh\n  cd <project> && VP_HOME=$HOME/.cache/vp-control-<prev> VP_NODE_MANAGER=no \\\n    PATH=\"$HOME/.cache/vp-control-<prev>/bin:$PATH\" vp migrate <project> --no-interactive\n  ```\n\n  `VP_SELF_SETUP_NO_MODIFY_PATH=1` keeps the control installation out of the user's shell profiles; without it, every new shell sets `VP_HOME` to the control.\n\n  **Run the control from inside the project directory.** Launching it from the vite-plus checkout makes `vp` delegate to that checkout's `packages/cli/dist` instead of the pinned release, which silently invalidates the comparison (it fails with an unrelated error such as `Fail to parse yaml as RuleConfig`).\n\n  Run candidate and control in isolated checkouts of the same project base, starting with the same lockfile and no `node_modules`. Compare any lockfile changes made by migration so unrelated dependency versions do not invalidate the control. When the control reproduces the failure, report the evidence (same exit code and error class).\n\n- **Stale pins: weight the forks that were actually on the previous release.** A fork pinned several releases back does not test the release under review at all; `vp migrate` performs a multi-release jump, and any resulting type errors are evidence about that jump. Before drawing a conclusion, work out what each fork was on and judge the release primarily on the forks upgrading from the immediately previous release. Derive the pin from the upgrade commit itself, not from `HEAD`, since later commits on the test branch hide it:\n\n  ```bash\n  sha=$(git -C <dir> log --format=%H --grep='^test: upgrade vite-plus to prerelease' -n 1)\n  git -C <dir> show \"$sha\" | grep -E '^-.*vite-plus'\n  ```\n\n  Parse every YAML document when auditing pnpm lockfiles. Importers and package resolutions can be in separate documents; a single-document parser can miss the resolved versions.\n\n  Report that subset separately; \"2 of the 7 forks on the previous release pass, the other 5 fail on fork infrastructure\" is a far stronger statement than a headline pass rate over the whole catalog.\n\n- **Project-side and infra failures.** Dependency conflicts between the project's own packages, missing fork secrets, third-party GitHub Apps not installed on the fork, network timeouts. Retry once before classifying anything as a network failure; they pass on retry. Two recurring shapes worth naming: a package that imports a dependency it never declared and only ever resolved through hoisting (`Cannot find package 'oxfmt'`) breaks as soon as the harness regenerates the lockfile; and a project whose own dependency has no `main`/`module`/`exports` cannot load its config under any vite-plus version.\n- **Dependency drift during migration.** Regenerating a lockfile can move unrelated floating or nightly dependencies to incompatible versions. Compare with the base lockfile before blaming the candidate. On the test branch, retain the original versions and their dependency graph, then verify a frozen install and rerun the failing command.\n- **Custom quality checks.** Check that project wrappers still load their plugins and recognize migrated test imports. Preserve existing lint diagnostic coverage when repairing migration issues; a smaller baseline can mean that checks stopped running.\n- **Release-age gates on fresh dependencies.** A project's `minimumReleaseAge` can reject packages that Vite+ itself pins and that were published shortly before the release. That is the project's policy, not a vite-plus bug: add the package to `minimumReleaseAgeExclude` on the test branch, and do not propose extending migrate's exemption list. Real installs of the release hit the same gate until those packages age past the project's window.\n- **Harness artifacts.** Failures your own test setup caused, such as a lockfile the harness deleted and the install never regenerated. Fix these and re-run rather than reporting them.\n\nReport the tally by cause, not just pass/fail, and state plainly which failures you controlled for and which you classified from the error text alone. Only a failure that reproduces on the candidate but not on the previous release is a regression.\n\nBefore filing an upstream issue for a finding, search every repository that could own it, including closed issues and open PRs. For a type-aware oxlint diagnostic that means both `oxc-project/oxc` and `oxc-project/tsgolint`.\n\nWhen repairing timing-sensitive smoke tests, keep their assertions and make readiness or timing deterministic. Use a negative control when changing how a test observes behavior: temporarily remove or break that behavior, confirm the test fails, and restore it before committing.\n\nTwo long-run mechanics worth knowing: `vp migrate` installs Vite+ git hooks in the project, so any later `git commit`/`git push` there needs `--no-verify`; and macOS has no GNU `timeout`, so a driver script that time-boxes runs needs its own watchdog. If that driver runs projects in parallel, kill the whole process tree on timeout, not just the wrapper: an orphaned `pnpm install` holds the store lock and the next project then hangs at 0% CPU with no output, which reads like a vite-plus hang and is not one. Concurrent installs that share one store can stall the same way without any orphan, with one process idle while holding the store's `index.db`; rerun the stuck project alone before treating it as a hang.\n\nTwo fork-CI blockers are worth fixing rather than reporting, both on the **test branch only** so the tracked branch stays clean against upstream. A fork whose workflows never trigger on `pull_request` reports \"no checks\" and proves nothing: add a minimal workflow that runs `vp run build` through whatever setup the project already uses. A fork whose workflows target third-party runners (self-hosted labels such as `blacksmith-*`) queues every job forever, because those labels only resolve for the upstream org: map them to GitHub-hosted equivalents, replacing the longest label first so an `-arm` suffix is not left half-rewritten. Runner-specific _actions_ need more than a label swap and are usually not worth fixing. The same applies to `depot-*` and `namespace-profile-*` labels. GitHub-hosted runners are smaller than most of these, so heavy suites can start timing out after the swap; classify those timeouts as fork infrastructure.\n\nClosing a cycle's PRs with `--delete-branch` removes their test branches, but each closed PR's commits stay reachable at `refs/pull/<N>/head`. Find the previous cycle's PR per repository (`gh pr list --repo vite-plus-ecosystem-ci/<repo> --state all`); an org-wide `gh search` stops at 100 results and silently misses forks. Before a new sweep, list the non-upgrade commits on the previous cycle's PRs (supply-chain exemptions, runner-label maps, added workflows), fetch them, and cherry-pick them onto the new test branches. Re-apply by hand when upstream drift makes a cherry-pick conflict. Commits made after the upgrade commit (formatting of migrate-written files, regenerated lockfiles, restored dependencies) only apply after migrating: retry them after the new upgrade commit, take the new lockfile when only lockfiles conflict, and drop a formatting commit that would add migrate-generated `package.json` or workspace keys before migrate runs.\n\n## 5. Release-branch CI\n\nMatch checks to the current PR head and the latest applicable workflow runs. Superseded canceled runs can leave failed aggregate checks in the PR rollup. Check required statuses with `gh pr checks <PR#> --required`, and report required reviewer approval separately from technical CI readiness.\n\nFixes for CI failures go through a **separate PR to `main`**, never as commits on the release branch (the release-version syncs in step 2 are the exceptions). After the fix PR merges:\n\n```bash\ngit checkout release/vX.Y.Z && git merge origin/main --no-edit && git push origin release/vX.Y.Z\n```\n\nDo not assume the merge brought in only the fix PR: `main` may have accumulated several. Before merging, list everything that will come in with `git log origin/release/vX.Y.Z..origin/main --oneline`, then add a changelog entry for **every** newly included PR and rerun the step 3 validation (its missing/extra diff against `generate-notes` catches any entry you missed). When the merged commits change bundled tool versions or `vp migrate` behavior, rebuild the preview (step 4) and repeat the smoke test on the new build: start each fork from its current test branch so earlier fixes carry over, open new fork PRs, close the superseded ones, and report results per build.\n\nKnown release-branch-only failure modes:\n\n- **Binding version drift**: CI's no-unexpected-changes guard reports a diff flipping version strings in `binding/index.cjs`. Fix: step 2.\n- **Registry flakes**: registry-bound fixtures can time out (about 50s) and look like regressions. Rerun before diagnosing, and never commit a `[timeout]` snapshot.\n- **New RustSec advisories**: Security Analysis runs cargo-deny only when `Cargo.lock` changes, and the version bump always changes it. An advisory published since `main` last touched `Cargo.lock` therefore fails first on the release PR. The check is not required; trace which dependency pulls the crate in and let the release manager decide whether to fix it before or after the release. Before opening a fix PR, check open dependency-update PRs (Renovate): one may already bump the crate and clear the check.\n\n## 6. Merge\n\nMerging the release PR is the release trigger. Before merging confirm: CI green, changelog validated, binding and documentation versions synced (including all three prompts), and (if used) the preview build verified.\n\nWhen the release publishes an npm package for the first time, confirm before merging that the package's npm settings list a trusted publisher for `voidzero-dev/vite-plus`, workflow `release.yml`, environment `release`. A missing or mismatched entry fails that publish with `ERR_PNPM_AUTH_TOKEN_EXCHANGE ... (status code 404)` after `vite-plus` is already on `latest`, and the GitHub release, Docker image, docs deployment, and Discord notification are skipped until the job is rerun.\n\nAuto-merge being enabled is not a completed merge. Confirm `mergedAt` and the merge commit, then follow the Release run for that commit; older successful runs can have skipped publishing because the version did not change.\n\n## 7. Automated release pipeline (what happens after merge)\n\n`release.yml` runs on the `main` push because `packages/cli/package.json` changed:\n\n1. `check`: compares the local version against `unpkg.com/vite-plus@latest`; everything below is skipped unless it changed.\n2. `build-rust`: full multi-platform build.\n3. `request-approval`: posts an approval request to the releases Discord channel, and the `Release` job waits on the `release` GitHub environment. **A person with environment approval rights must approve the run in the Actions UI.** The environment sets `prevent_self_review: true`, so whoever merged the release PR triggered the run and cannot approve it: a _different_ reviewer must. Check who can, and tell the release manager rather than leaving them waiting on themselves:\n\n   ```bash\n   gh api repos/voidzero-dev/vite-plus/actions/runs/<run-id>/pending_deployments \\\n     -q '.[] | \"\\(.environment.name) can_approve=\\(.current_user_can_approve) reviewers=\\([.reviewers[]?.reviewer.login] | join(\",\"))\"'\n   ```\n\n4. `Release`: publishes the NAPI bindings (`@voidzero-dev/vite-plus-<platform>`) and standalone CLI packages (`@voidzero-dev/vite-plus-cli-<platform>`, via `packages/cli/publish-native-addons.ts`), then `@voidzero-dev/vite-plus-core`, `vite-plus`, and `create-vite-plus` to npm (`--tag latest`). Each dependency tier waits for npm propagation before publication advances. It then creates the `vX.Y.Z` GitHub release (draft, with installer/binary assets, then undrafted). The generated body has only Published Packages and Installation sections.\n5. `publish-docker`: multi-arch toolchain image to `ghcr.io/voidzero-dev/vite-plus`, after npm publish (the image installs vp from npm).\n6. `deploy-docs`: deploys the production docs after a stable release is published. It is skipped for prereleases. When a prerelease is published to `latest` and its notes or CLI messages link to docs that production does not serve yet, ask the release manager whether to run `gh workflow run deploy-docs.yml --ref <ref>` once the `Release` job is publishing. Use the `vX.Y.Z` tag, or `main` while it still points at the release commit, and confirm the run's head SHA. The `vp` version that builds the docs does not need to match the release: the site and its install scripts come from the checked-out commit. The job authenticates with the `VOID_TOKEN` repository secret. When that token has expired, the job fails with `` `VOID_TOKEN` is invalid or expired `` and `discord-notify` is skipped. Ask someone with access to rotate the secret, then run `gh run rerun <run-id> --failed` to rerun the docs deployment and the Discord notification.\n7. `discord-notify`: announces to Discord after Docker publishing and docs deployment succeed (docs are skipped for prereleases).\n\n**When the `Release` job fails partway, fix the cause, then use \"Re-run failed jobs\", never \"Re-run all jobs\".** A full rerun repeats `check`, which now sees the new version on `unpkg.com/vite-plus@latest` and skips the whole release. A failed-jobs rerun reuses `check` and the build artifacts, and every npm publish step is idempotent: `.github/scripts/publish-npm-package.ts` skips versions the registry already serves (`<pkg>@X.Y.Z is already published; skipping upload.`), so only the unpublished packages upload. Confirm that from the rerun log. The rerun waits on the `release` environment again, and whoever started the rerun cannot approve it.\n\n**A successful publish command does not mean the packages are installable.** `pnpm publish` prints `✅ Published package <name>@X.Y.Z` as soon as the registry accepts the request, and the registry can then take tens of minutes to actually serve that version. This has shipped a broken release: `vite-plus@X.Y.Z` went live on `latest` with an exact dependency on `@voidzero-dev/vite-plus-core@X.Y.Z` that was invisible for about 35 minutes, so every `npm install vite-plus` failed with `ETARGET` and both `publish-docker` and `Deploy docs` failed on `ERR_PNPM_NO_MATCHING_VERSION`. The downstream job failures are the symptom, not the cause; do not re-run them until the registry has the package.\n\nCheck visibility directly, not through `npm view`, which caches:\n\n```bash\nfor pkg in '@voidzero-dev%2Fvite-plus-core' 'vite-plus'; do\n  curl -s -H 'Cache-Control: no-cache' \"https://registry.npmjs.org/$pkg?t=$(date +%s)\" |\n    python3 -c \"import json,sys;d=json.load(sys.stdin);print('$pkg', d['dist-tags'].get('latest'), 'X.Y.Z' in d['versions'])\"\ndone\n```\n\nBoth must report `True` before you trust the release. A stale `modified` timestamp on the packument is the giveaway that nothing landed. If `vite-plus` is visible and `core` is not, the release is broken **right now** for every new install: tell the release manager immediately and offer to move the tag back (`npm dist-tag add vite-plus@<prev> latest`) while the publish is sorted out. Confirm the fix with a real install in a temp directory, not just a registry read:\n\n```bash\nd=$(mktemp -d); cd \"$d\" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z --no-audit --no-fund\n```\n\nThe full package document can update before npm's separately cached installation metadata. The workflow's `.github/scripts/wait-for-npm-packages.ts` checks that installation metadata and the referenced tarball, then waits for settlement. Use that script to reproduce its availability check; local visibility does not prove the workflow runner sees the same state.\n\nLocal clients can also mislead in the other direction. npm and pnpm cache a lookup made before the version landed, so pass `--prefer-online` when a just-published version reports `ETARGET`/`notarget` locally. pnpm 11 and later default to a one-day `minimumReleaseAge`, so `pnpm create vite-plus` (an unpinned `pnpm dlx create-vite-plus`) resolves to the previous `create-vite-plus` until the new version is a day old; verify it with `--config.minimumReleaseAge=0`.\n\n## 8. Post-release\n\n1. **Polish the GitHub release notes** (ask first): the auto-created release body has only Published Packages and Installation. Build the polished notes from the final release PR body:\n   - Drop the closing `---` / `Merging this PR ...` boilerplate.\n   - Preserve every changelog section through **Full Changelog**, including any later revisions requested by the release manager.\n   - Append the generated Published Packages and Installation sections, omit the redundant `View the full commit` line, and end Installation with a Docker usage block (keep the explanation to one short sentence):\n\n     ````markdown\n     **Docker:**\n\n     ```bash\n     docker run --rm -it -v \"$PWD:/app\" -w /app ghcr.io/voidzero-dev/vite-plus:X.Y.Z vp build\n     ```\n\n     Run any `vp` command without installing it; see the [Docker guide](https://viteplus.dev/guide/docker) for more.\n     ````\n\n   - After Installation, add a **Create a New App** section with the pnpm example only:\n\n     ````markdown\n     ### Create a New App\n\n     Start a project without installing the global `vp`: `create-vite-plus` runs `vp create` through your package manager.\n\n     ```bash\n     pnpm create vite-plus vite:application --directory my-app\n     cd my-app && pnpm exec vp dev\n     ```\n\n     Run the command without arguments to pick a template interactively, and see the [create guide](https://viteplus.dev/guide/create#without-the-global-cli) for other package managers, templates, and options.\n     ````\n\n   - **Present the draft to the release manager and apply only after approval.** Before review, write the complete draft to a temporary Markdown file with the proposed release title at the top and the full body below it. Update that file after every requested revision; do not treat chat excerpts as the canonical draft. After approval, use a body-only notes file (without the review title) to retitle the release and apply the notes:\n\n     ```bash\n     gh release edit vX.Y.Z --repo voidzero-dev/vite-plus \\\n       --title \"vite-plus vX.Y.Z\" --notes-file /tmp/release-notes.md\n     ```\n\n   - Keep the review draft, body-only notes file, and live release aligned after requested edits. Read back the live title and body to verify the update. Normalize CRLF and LF before comparing the approved file with the live body, because GitHub can change line endings. Re-run the step 3 validation greps, plus `grep -c 'Merging this PR'` (must be 0).\n   - For a SemVer prerelease, confirm that GitHub marks the release as a prerelease. The automated release can create a prerelease tag without setting that flag. Add `--prerelease` when applying the approved notes, then verify `isPrerelease` with `gh release view vX.Y.Z --json isPrerelease`.\n\n2. **Verify**:\n\n   ```bash\n   npm view vite-plus version                       # X.Y.Z\n   npm view @voidzero-dev/vite-plus-core version    # X.Y.Z\n   npm view create-vite-plus version                # X.Y.Z\n   npm vi\n\n<!-- [TRUNCATED AT 50KB] -->",".claude/skills/spawn-process/SKILL.md":"---\nname: spawn-process\ndescription: Guide for writing subprocess execution code using the vp_command crate\nallowed-tools: Read, Grep, Glob, Edit, Write, Bash\n---\n\n# Add Subprocess Execution Code\n\nWhen writing Rust code that needs to spawn subprocesses (resolve binaries, build commands, execute programs), always use the `vp_command` crate. Never use `which`, `tokio::process::Command::new`, or `std::process::Command::new` directly.\n\n## Available APIs\n\n### `vp_command::resolve_bin(name, path_env, cwd)` — Resolve a binary name to an absolute path\n\nHandles PATHEXT (`.cmd`/`.bat`) on Windows. Pass `None` for `path_env` to search the current process PATH.\n\n```rust\n// Resolve using current PATH\nlet bin = vp_command::resolve_bin(\"node\", None, &cwd)?;\n\n// Resolve using a custom PATH\nlet custom_path = std::ffi::OsString::from(&path_env_str);\nlet bin = vp_command::resolve_bin(\"eslint\", Some(&custom_path), &cwd)?;\n```\n\n### `vp_command::build_command(bin_path, cwd)` — Build a command for a pre-resolved binary\n\nReturns `tokio::process::Command` with cwd, inherited stdio, and `fix_stdio_streams` on Unix already configured. Add args, envs, or override stdio as needed.\n\n```rust\nlet bin = vp_command::resolve_bin(\"eslint\", None, &cwd)?;\nlet mut cmd = vp_command::build_command(&bin, &cwd);\ncmd.args(&[\".\", \"--fix\"]);\ncmd.env(\"NODE_ENV\", \"production\");\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::build_shell_command(shell_cmd, cwd)` — Build a shell command\n\nUses `/bin/sh -c` on Unix, `cmd.exe /C` on Windows. Same stdio and `fix_stdio_streams` setup as `build_command`.\n\n```rust\nlet mut cmd = vp_command::build_shell_command(\"echo hello && ls\", &cwd);\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::run_command(bin_name, args, envs, cwd)` — Resolve + build + run in one call\n\nCombines resolve_bin, build_command, and status().await. The `envs` HashMap must include `\"PATH\"` if you want custom PATH resolution.\n\n```rust\nlet envs = HashMap::from([(\"PATH\".to_string(), path_value)]);\nlet status = vp_command::run_command(\"node\", &[\"--version\"], &envs, &cwd).await?;\n```\n\n## Dependency Setup\n\nAdd `vp_command` to the crate's `Cargo.toml`:\n\n```toml\n[dependencies]\nvp_command = { workspace = true }\n```\n\nDo NOT add `which` as a direct dependency — binary resolution goes through `vp_command::resolve_bin`.\n\n## Exception\n\n`crates/vp_global_cli/src/shim/exec.rs` uses synchronous `std::process::Command` with Unix `exec()` for process replacement. This is the only place that bypasses `vp_command`.\n",".claude/skills/sync-tsdown-cli/SKILL.md":"---\nname: sync-tsdown-cli\ndescription: Sync tsdown runtime CLI options with vp pack after a tsdown upgrade. Use for option forwarding changes; use sync-upstream-cli-help for static help wording.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync tsdown CLI\n\nRuntime options live in `packages/cli/src/pack-bin.ts`; static help lives in\n`packages/cli/src/help.ts`.\n\n1. Run `npx tsdown --help` from `packages/cli/` and compare it with `pack-bin.ts`.\n2. Add new forwarded options using the existing cac `.option()` pattern. For removed\n   options, add `// NOTE: removed from tsdown CLI in vX.Y.Z` for reviewer follow-up.\n3. Preserve runtime differences: `-c, --config` stays disabled because Vite+ uses\n   `vite.config.ts`, and `--env-prefix` keeps the `['VITE_PACK_', 'TSDOWN_']` default.\n4. For static labels and descriptions, follow `sync-upstream-cli-help`; do not adapt\n   upstream wording to explain runtime differences.\n5. Run `pnpm --filter vite-plus build-ts` and `vp pack -h`. Add a focused PTY snapshot\n   case under `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/` when a new runtime\n   option is exposed.\n",".claude/skills/sync-upstream-cli-help/SKILL.md":"---\nname: sync-upstream-cli-help\ndescription: Sync Vite+'s static CLI help with Vite, Vitest, Oxlint, Oxfmt, and tsdown while preserving intentional omissions. Use when an upstream dependency upgrade changes CLI help.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream CLI help\n\n## Input and target\n\n- Read the diff from `$CLI_HELP_DIFF_REPORT`; first run\n  `test -r \"$CLI_HELP_DIFF_REPORT\"`. Act only when `$CLI_HELP_DIFF_CHANGED` is\n  `true`.\n- Treat the upgraded tool's `--help` output as the source of truth. The report locates\n  changes and versions; rerun the exact version when its diff is truncated.\n- Edit `commandHelpDocs` in `packages/cli/src/help.ts`.\n- Do not edit `packages/cli/src/utils/help.ts` for content drift. It owns terminal\n  wrapping, alignment, and the right margin.\n\n| Upstream help         | Document entry |\n| --------------------- | -------------- |\n| `vite --help`         | `dev`          |\n| `vite build --help`   | `build`        |\n| `vite preview --help` | `preview`      |\n| `vitest --help`       | `test`         |\n| `oxlint --help`       | `lint`         |\n| `oxfmt --help`        | `fmt`          |\n| `tsdown --help`       | `pack`         |\n\n## Change\n\n- For items Vite+ exposes, copy upstream labels, descriptions, section titles, and\n  section guidance exactly. Preserve intentional lines and lists, but not terminal\n  padding, automatic wrapping, ANSI color, or version banners.\n- Remove an upstream item only after confirming Vite+ no longer supports or\n  deliberately retains it.\n\n## Do not change\n\n- Keep Vite+-owned usage, summaries, examples, and documentation URLs.\n- Keep config selectors/loaders hidden, including `--config`, `--configLoader`, and\n  `--disable-nested-config`.\n- Do not add standalone modes such as `--init`, `--migrate`, or `--lsp`, top-level\n  `--version`, or options Vite+ does not forward.\n- Do not change runtime forwarding or rewrite upstream wording for a Vite+-specific\n  runtime default. Use `sync-tsdown-cli` for tsdown runtime changes.\n\nRe-record the affected CLI help snapshots and inspect their diffs. Do not modify help\ndocuments when the report contains no actionable exposed change.\n",".claude/skills/sync-upstream-dependency-docs/SKILL.md":"---\nname: sync-upstream-dependency-docs\ndescription: Sync exact upstream dependency versions embedded in Vite+ documentation after the automated dependency upgrade. Use when bundled Vite, Vitest, Oxlint, Oxfmt, tsdown, Rolldown, or related package versions change.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream dependency docs\n\n1. Read `$UPGRADE_DEPS_META_DIR/versions.json` and consider only entries whose `old`\n   and `new` values differ.\n2. Search `README.md`, `packages/*/README.md`, and `docs/**/*.md` for each changed\n   package name and its old exact version. Do not treat changelogs, RFC examples,\n   snapshots, or broad ranges such as `vitest@4.x` as current-version references.\n3. Update references that promise to match Vite+'s currently bundled version.\n   Manual-installation examples use placeholders for the `vite-plus` and Vitest\n   versions, so they do not need updates.\n4. Preserve the surrounding wording and formatting. Do not rewrite examples whose\n   version is intentionally historical or illustrative.\n5. Re-run the searches for the changed packages and inspect the focused diff. No\n   stale exact version may remain in documentation that describes the current bundle.\n",".claude/skills/test-pkg-pr-new-migrate/SKILL.md":"---\nname: test-pkg-pr-new-migrate\ndescription: Verify a preview (registry bridge) build of vite-plus against a real project before release — run `vp migrate` from the preview commit against a local project, deps resolved through the registry bridge. Use when asked to verify/e2e-test a preview or pkg-pr-new build against a project, \"test PR #<N> on <project>\", or check a prerelease against a repo.\nallowed-tools: Bash, Read\n---\n\n# Verify a preview build against one project\n\nInstalls an isolated global `vp` built from a registry bridge commit build and runs `vp migrate` on a specified local project. The global CLI and the migrated project both pin `vite-plus`/`vite` to the clearly-defined `0.0.0-commit.<sha>` build. `vp migrate` itself writes the bridge registry into the project's `.npmrc` (or `.yarnrc.yml` for Yarn Berry) so the deps resolve, during this run and in the project's own CI; this script only force-stages that file past `.gitignore`.\n\nRequired inputs: a `<PR-or-SHA>` (the build to verify) and a `<project-path>`. If either is missing from the request, **ask the user** for it — never guess the PR/SHA, as the build under test is the user's choice.\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh <PR-or-SHA> <project-path> [migrate-options...]\n# e.g.\n.github/scripts/test-pkg-pr-new-migrate.sh 1891 /path/to/npmx.dev --no-interactive\n```\n\n- First arg is a PR number or commit SHA; the script resolves the immutable commit via the bridge `x-commit-key` header and verifies the bridge serves it (the preview publish workflow, triggered by the `preview-build` label, registers each commit).\n- Never touches `~/.vite-plus`; installs the project's committed dependencies before migrating, because `vp migrate` reads the original Vitest version from the lockfile or `node_modules`; refuses a dirty worktree unless `ALLOW_DIRTY=1`; prints the project's `git status`/`diff` at the end — inspect that to confirm the migration result.\n\n**The build under test must include the \"migrate writes the bridge registry\" feature** (this session's work / current branch head onward). The harness no longer writes the registry itself — it relies on `vp migrate` doing it. Testing an older build with this harness would leave the project with no bridge registry, so its deps resolve from npmjs (`ERR_PNPM_NO_MATCHING_VERSION` on the `0.0.0-commit.<sha>` version). Always verify a fresh build of the branch, not a stale published commit.\n\nThe script prints the resolved versions at the end, querying one package at a time (npm/yarn/bun `why` only accept a single package; `-r` is pnpm-only). Each must resolve to exactly ONE version: `vite-plus` and `vite` at the expected `0.0.0-commit.<sha>` (vite via the `@voidzero-dev/vite-plus-core` alias), `vitest` at the bundled upstream version. Multiple versions, or a stale/wrong version, means the migration or install is broken. To re-check by hand:\n\n```bash\ncd <project-path>\n# pnpm: one call, recursive across workspaces\nvp why -r vite-plus vite vitest\n# npm / yarn / bun: one package per call, no -r\nvp why vite-plus && vp why vite && vp why vitest\n```\n",".claude/skills/verify-interactive-cli/SKILL.md":"---\nname: verify-interactive-cli\ndescription: Drive and capture vp's interactive (clack) prompts in a tmux session to manually verify interactive UX and catch spinner-over-prompt bugs. Use when asked to test/verify/capture an interactive vp command's prompts (vp migrate, vp create, ...), reproduce a prompt-rendering bug, or show the real interactive CLI output for a PR.\nallowed-tools: Bash, Read\n---\n\n# Verify vp's interactive CLI prompts\n\nThe PTY snapshot suite covers scripted prompt flows. This skill complements it by driving an installed CLI in tmux, capturing clean output, and checking a prompt over several seconds for a spinner animating underneath it (a real, recurring UX bug class, e.g. the \"Preparing migration\" and \"Checking Node.js version support\" spinners).\n\n## Prerequisites\n\n- `tmux` (macOS has none by default): `brew install tmux`.\n- The global `vp` must contain the code under test. To exercise working-tree changes, rebuild first: `pnpm bootstrap-cli` (~5 min), then confirm with `vp --version`.\n\n## Driver\n\n`interactive-cli-tmux-driver.sh` is bundled in this skill's directory.\n\n```bash\n# Run to completion, auto-accepting every prompt's DEFAULT; prints a clean transcript.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\"\n\n# STOP at a specific prompt (do NOT answer it) and check for a spinner animating under it.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\" \"Upgrade Node.js\"\n```\n\nHow it works:\n\n- Runs the command in a detached tmux session; `tmux capture-pane -p` yields clean text (clack's in-place redraws overwrite, so only resolved lines remain), far cleaner than `expect`'s raw ANSI capture.\n- Auto-accepts each prompt's default by sending Enter when the pane goes STABLE. A waiting prompt is static; animating spinners keep the pane changing, so Enter never fires mid-work.\n- End-detection uses `; echo \"$M1$M2 exit=$?\"` where M1/M2 are split vars, so the literal end marker is not in the typed command line (otherwise a grep matches the echoed command, not the program output).\n- With a STOP_AT regex it halts at the target prompt and captures twice ~3s apart: identical captures = static prompt (OK); differing captures (or a `Checking ... (Xs)` line) = a spinner is animating under the prompt = a UX bug.\n\n## Setting up a target project\n\nCreate a throwaway project that triggers the prompts you want to see, e.g. a fresh Vite app whose `.node-version` is below the supported range to exercise the Node-upgrade confirm:\n\n```bash\nmkdir -p /tmp/vp-demo && cd /tmp/vp-demo\nprintf '{\\n  \"name\":\"d\",\"private\":true,\"type\":\"module\",\"packageManager\":\"pnpm@10.18.0\",\\n  \"scripts\":{\"build\":\"vite build\",\"test\":\"vitest run\"},\\n  \"devDependencies\":{\"vite\":\"^8.0.0\",\"vitest\":\"^4.1.0\"}\\n}\\n' > package.json\necho \"24.3.0\" > .node-version\nprintf 'import { defineConfig } from \"vite\";\\nexport default defineConfig({});\\n' > vite.config.ts\ngit init -q && git add -A && git -c user.email=x@x -c user.name=x commit -qm init\n```\n\n## When you find a spinner-over-prompt bug\n\nFix it test-first: the prompt's code must pause the migration progress spinner before the confirm renders. Assert the call order is `['pause', 'confirm']`. Reference fix: `upgradeUnsupportedNodeVersions` (in `packages/cli/src/migration/migrator/setup.ts`) takes a `pauseProgress` callback that `bin.ts` wires to `clearMigrationProgress`, called right before `prompts.confirm`.\n\n## Gotchas\n\n- clack's active-prompt marker here is `›` (not always `◆`); detect a waiting prompt by pane stability, not a specific glyph.\n- `VP_SKIP_INSTALL=1` skips the dependency install but breaks steps that load `vite.config.ts` (e.g. the prettier auto-migration) because `vite` isn't installed; use it only when you stop before the install step.\n- A `&` inside a background task detaches the script, so the task reports \"completed\" early while it keeps running; poll a status file or `capture-pane` to track real progress.\n","AGENTS.md":"# Vite+\n\nVite+ is the web toolchain behind `vp`: Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, Vite Task, runtime and package-manager workflows, and project creation/migration.\n\nThis guide applies to this repository; `CLAUDE.md` points here. For changes to the guidance shipped to user projects, use `packages/cli/AGENTS.md` and `packages/cli/src/utils/agent.ts`. Leave those files unchanged for root-guide tasks.\n\n## Context by Task\n\n- **CLI routing**: `packages/cli/src/bin.ts` dispatches JS commands; `packages/cli/binding/src/lib.rs` and `packages/cli/binding/src/cli/mod.rs` handle local NAPI commands. Global routing starts at `crates/vp_global_cli/src/main.rs` and `crates/vp_global_cli/src/cli.rs`. Compare global and local paths for routing bugs.\n- **Package managers and runtimes**: use `crates/vp_pm_cli/` and `crates/vp_js_runtime/`. The Windows shim trampoline lives in `crates/vp_trampoline/`, outside the Cargo workspace.\n- **Config loading**: compare the [static extractor](crates/vp_static_config/README.md) with the JS fallback in `packages/cli/src/resolve-vite-config.ts`.\n- **Migration**: follow the [migrator instructions](packages/cli/src/migration/migrator/README.md) when changing `vp migrate`; use [migration rules](docs/guide/migrate-rules.md) for expected behavior.\n- **Bundled tools and exports**: use [CLI bundling](packages/cli/BUNDLING.md) and [core bundling](packages/core/BUNDLING.md). Test resolution starts at `packages/cli/src/resolve-test.ts`; `packages/cli/build.ts` generates test API shims.\n- **Testing a local build in a project**: follow [CONTRIBUTING.md](CONTRIBUTING.md) for linking and installation. For `vp migrate` / `vp create`, use the local registry in `packages/tools/src/local-npm-registry.ts`, which also serves PTY fixtures and ecosystem tests.\n- **Product documentation**: start at `README.md` and the VitePress site in `docs/guide/` and `docs/config/`.\n- **Setup and build commands**: use `CONTRIBUTING.md`, `justfile`, and `package.json`. Start with `just init` for initial setup. Repository lint, format, and test configuration lives in `vite.config.ts`; TypeScript settings live in `tsconfig.json`.\n\n## Repository Constraints\n\n- Distinguish built-ins from tasks: `vp test` executes upstream Vitest; `vp run test` executes a script or configured task named `test`. `vpr` is shorthand for `vp run`. Inspect command surfaces with `vp help` / `vp <command> --help`, and bundled versions with `vp --version`.\n- Keep task configuration under `run` in `vite.config.ts`; do not introduce `vite-task.json`. Existing `package.json` scripts are `vp run` targets without caching by default. Use `run.tasks` for explicit command configuration, default caching, dependencies, or input/environment tracking. A task name must occur in only one of these files. See the [run guide](docs/guide/run.md) and [run configuration](docs/config/run.md).\n- Keep public test imports on `vite-plus/test*`, which wraps upstream `vitest` and `@vitest/browser*`. Do not recreate `packages/test` or `@voidzero-dev/vite-plus-test`.\n- Vite Task crates are git dependencies in `Cargo.toml`; there is no local `crates/vt`. Do not run `cargo test -p vt` here.\n- Use `vp_shared::VpDirs` for Vite+ directory roots. See `crates/vp_shared/src/dirs.rs` and `crates/vp_shared/src/dirs/resolution.rs`. Call sites must not construct category paths or read `VP_HOME` or `XDG_*` directly.\n- Follow [`.clippy.toml`](.clippy.toml) for Rust restrictions and replacements. Use `crates/vp_shared/src/output.rs` for user-facing output; command output that can be piped must use `print_and_flush` or another broken-pipe-safe helper instead of `print!` or `println!`. Enable `clippy::print_stdout` in migrated modules so CI prevents direct stdout macros from returning. Use `crates/vp_shared/src/env_config.rs` for test-scoped environment configuration. See `crates/vp_command/src/lib.rs` for `vt_path` usage.\n- For TypeScript CLI output, use `packages/cli/src/utils/terminal.ts` and match the surrounding command style.\n- Keep changes scoped to the task and leave unrelated tracked and untracked files alone. Prefer source references over duplicated instructions in this guide.\n\n## Checks and Tests\n\nPrefer local fixtures or mock servers for tests that depend on external services. Use real network requests only when they are necessary to verify the behavior under test, and explain that need in the test. Reuse existing test infrastructure, preserve behavioral coverage, and keep mutable state isolated between cases.\n\nFor behavior changes, find nearby tests before editing and add or update coverage in the same area. Choose checks for the changed layer:\n\n| Change                         | Validation                                                                                                                         |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |\n| Documentation / agent guidance | Verify referenced paths, commands, and links; `git diff --check -- <files>`                                                        |\n| TypeScript / JS CLI            | `vp check`, `pnpm test:unit`, and focused package tests                                                                            |\n| Rust CLI / crates              | `just check`, `just test`, `just lint`                                                                                             |\n| CLI output / interactions      | Focused tests and `just snapshot-test <filter>`; review snapshot diffs                                                             |\n| Global CLI                     | `pnpm bootstrap-cli` and `vp --version` for installed end-to-end checks; `just snapshot-test-global <filter>` for global snapshots |\n| Release / build                | `just build`                                                                                                                       |\n| Full pre-merge check           | `pnpm bootstrap-cli && pnpm test && git status`                                                                                    |\n\nValidate `vp check`, lint, format, and type-check changes end-to-end: bundled-tool routing can hide config drift. Use `vp check --fix` only when you intend to apply formatting or lint fixes. Documentation-only changes need no unrelated test suites.\n\n### CLI Snapshots\n\nWrite new CLI tests, including interactive flows, in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`. Read the [snapshot runner instructions](crates/vp_cli_snapshots/tests/cli_snapshots/README.md) before adding cases; they define `snapshots.toml`, CLI flavors, `vpt` helpers, and prompt milestones. See [the RFC](rfcs/interactive-snapshot-tests.md) for design rationale.\n\n- Steps are argv arrays without an implicit shell. Use `vpt` helpers instead of coreutils for portable file operations.\n- For package-manager cases, prefer the runner's local registry, [shared registry fixtures](crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md), and response cache over new public-registry requests. Keep package-manager caches isolated per case, and refresh recorded fixtures only through the explicit maintenance command.\n- For changes to network use or scheduling, compare request counts and elapsed time with the same cases, worker count, and initial cache state. Do not remove assertions or share mutable case state to improve speed.\n- Local-flavor cases require a fresh `packages/cli/dist`. Use `just snapshot-test-global <filter>` when no JS build is available.\n- For intended snapshot changes, run `UPDATE_SNAPSHOTS=1 just snapshot-test <filter>`. Review and commit the recorded `.md` files with the fixture; mismatches fail the run and produce `.md.new` files.\n\n## Pull Requests\n\nPrefer a stack of small PRs for work with separable layers. Use the `gh-stack` extension or github.com; stacks require branches in this repository, so use standalone PRs from forks. Follow [CONTRIBUTING.md](CONTRIBUTING.md#submitting-pull-requests) for submission and commit-signing requirements.\n","CLAUDE.md":"@AGENTS.md\n","packages/cli/AGENTS.md":"<!--VITE PLUS START-->\n\n# Using Vite+, the Unified Toolchain for the Web\n\nThis project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, and it invokes Vite through `vp dev` and `vp build`. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.\n\nDocs are local at `node_modules/vite-plus/docs` or online at https://viteplus.dev/guide/.\n\n## Built-in Commands vs Scripts\n\n`vp <name>` runs a built-in command. `vp run <name>` runs a `package.json` script or a `vite.config.ts` task. Scripts cannot overwrite built-ins, so `vp dev` and `vp run dev` may do different things. Check `package.json` and `vite.config.ts` first, and run `vp run <name>` when the project defines a script or task with that name.\n\n## Running Scripts\n\nRun TypeScript and JavaScript files with `vpx <file>`, for example `vpx ./scripts/seed.ts --dry-run`. It handles enums, decorators, JSX, and tsconfig `paths`, so do not add `tsx` or `ts-node` to run scripts. `vpx --watch <file>` reruns the file when a file it uses changes, in place of `tsx watch`. `vp node <file>` runs plain Node.js.\n\n## Tool Versions\n\nRun `vp toolchain` to show versions and relationships in the active Vite+\nrelease. Add a tool name to select part of the graph. For example, run\n`vp toolchain vite`. Use `--global` to ignore the local `vite-plus` package. Use\n`vp why <package>` to show the package-manager dependency graph.\n\n## Review Checklist\n\n- [ ] Run `vp install` after pulling remote changes and before getting started.\n- [ ] Run `vp check` and `vp test` to format, lint, type check and test changes.\n- [ ] Check if there are `vite.config.ts` tasks or `package.json` scripts necessary for validation, run via `vp run <script>`.\n- [ ] If setup, runtime, or package-manager behavior looks wrong, run `vp env doctor` and include its output when asking for help.\n\n<!--VITE PLUS END-->\n","packages/cli/README.md":"# VITE+(⚡︎) Local CLI\n\n**The Unified Toolchain for the Web**\n_runtime and package management, create, dev, check, test, build, pack, and monorepo task caching in a single dependency_\n\nThis package provides the project-local version of Vite+. The global `vp` command automatically delegates to this package for all project-specific tasks.\n\n---\n\nVite+ is the unified entry point for local web development. It combines [Vite](https://vite.dev/), [Vitest](https://vitest.dev/), [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [Rolldown](https://rolldown.rs/), [tsdown](https://tsdown.dev/), and [Vite Task](https://github.com/voidzero-dev/vite-task) into one zero-config toolchain that also manages runtime and package manager workflows:\n\n- **`vp env`:** Manage Node.js and package managers globally and per project\n- **`vp install`:** Install dependencies with automatic package manager detection\n- **`vp dev`:** Run Vite's fast native ESM dev server with instant HMR\n- **`vp check`:** Run formatting, linting, and type checks in one command\n- **`vp test`:** Run tests through bundled Vitest\n- **`vp build`:** Build applications for production with Vite + Rolldown\n- **`vp run`:** Run `package.json` scripts and monorepo tasks with caching and dependency-aware scheduling\n- **`vp pack`:** Build libraries for npm publishing or standalone app binaries\n- **`vp toolchain`:** Show the versions of Vite+, Vite, Rolldown, Oxc, and other tools\n- **`vp create` / `vp migrate`:** Scaffold new projects and migrate existing ones\n\nAll of this is configured from your project root and works across Vite's framework ecosystem.\nVite+ is fully open-source under the MIT license.\n\n## Getting Started\n\nInstall Vite+ globally as `vp`:\n\nFor Linux or macOS:\n\n```bash\ncurl -fsSL https://vite.plus | bash\n```\n\nFor Windows:\n\n```bash\nirm https://viteplus.dev/install.ps1 | iex\n```\n\n`vp` handles the full development lifecycle such as package management, development servers, linting, formatting, testing and building for production.\n\n## Configuring Vite+\n\nVite+ can be configured using a single `vite.config.ts` at the root of your project:\n\n```ts\nimport { defineConfig } from 'vite-plus';\n\nexport default defineConfig({\n  // Standard Vite configuration for dev/build/preview.\n  plugins: [],\n\n  // Vitest configuration.\n  test: {\n    include: ['src/**/*.test.ts'],\n  },\n\n  // Oxlint configuration.\n  lint: {\n    ignorePatterns: ['dist/**'],\n  },\n\n  // Oxfmt configuration.\n  fmt: {\n    semi: true,\n    singleQuote: true,\n  },\n\n  // Vite Task configuration.\n  run: {\n    tasks: {\n      'generate:icons': {\n        command: 'node scripts/generate-icons.js',\n        cache: {\n          env: ['ICON_THEME'],\n        },\n      },\n    },\n  },\n\n  // `vp staged` configuration.\n  staged: {\n    '*': 'vp check --fix',\n  },\n});\n```\n\nThis lets you keep the configuration for your development server, build, test, lint, format, task runner, and staged-file workflow in one place with type-safe config and shared defaults.\n\nUse `vp migrate` to migrate to Vite+. It merges tool-specific config files such as `.oxlintrc*`, `.oxfmtrc*`, and lint-staged config into `vite.config.ts`.\n\n### CLI Workflows (`vp help`)\n\n#### Start\n\n- **create** - Create a new project from a template\n- **migrate** - Migrate an existing project to Vite+\n- **config** - Configure hooks and agent integration\n- **hooks** - Manage the Git hook dispatcher\n- **staged** - Run linters on staged files\n- **install** (`i`) - Install dependencies\n- **env** - Manage Node.js and package managers\n\n#### Develop\n\n- **dev** - Run the development server\n- **check** - Run format, lint, and type checks\n- **lint** - Lint code\n- **fmt** - Format code\n- **test** - Run tests\n\n#### Execute\n\n- **run** - Run monorepo tasks\n- **exec** - Execute a command from local `node_modules/.bin`\n- **node** - Run a Node.js script with the resolved Vite+ environment; use `vpx <file>` for TypeScript\n- **dlx** - Execute a package binary without installing it as a dependency\n- **cache** - Manage the task cache\n\n#### Build\n\n- **build** - Build for production\n- **pack** - Build libraries\n- **preview** - Preview production build\n\n#### Manage Dependencies\n\nVite+ automatically wraps your package manager (pnpm, npm, Yarn, or Bun) based on `packageManager` and lockfiles:\n\n- **add** - Add packages to dependencies\n- **remove** (`rm`, `un`, `uninstall`) - Remove packages from dependencies\n- **update** (`up`) - Update packages to latest versions\n- **dedupe** - Deduplicate dependencies\n- **outdated** - Check outdated packages\n- **list** (`ls`) - List installed packages\n- **why** (`explain`) - Show why a package is installed\n- **info** (`view`, `show`) - View package metadata from the registry\n- **link** (`ln`) / **unlink** - Manage local package links\n- **rebuild** - Rebuild native modules\n- **pm** - Forward a command to the package manager\n\n#### Maintain\n\n- **toolchain** - Show Vite+ tool versions and their relationships\n- **upgrade** - Update `vp` itself to the latest version\n- **implode** - Remove `vp` and all related data\n\n### Scaffolding your first Vite+ project\n\nUse `vp create` to create a new project:\n\n```bash\nvp create\n```\n\nYou can run `vp create` inside of a project to add new apps or libraries to your project.\n\nOrganizations can expose a curated set of templates under their npm scope by\npublishing `@org/create` with a `createConfig.templates` manifest in its `package.json`.\nOnce published, `vp create @org` opens an interactive picker over those\ntemplates, and setting `create: { defaultTemplate: '@org' }` in\n`vite.config.ts` makes it the default for bare `vp create`. See the\n[Organization Templates guide](https://viteplus.dev/guide/create#organization-templates)\nfor the authoring workflow and\n[`create.defaultTemplate`](https://viteplus.dev/config/create) for the\nconfig reference.\n\n### Migrating an existing project\n\nYou can migrate an existing project to Vite+:\n\n```bash\nvp migrate\n```\n\n### GitHub Actions\n\nUse the official [`setup-vp`](https://github.com/voidzero-dev/setup-vp) action to install Vite+ in GitHub Actions:\n\n```yaml\n- uses: voidzero-dev/setup-vp@<setup-vp-version>\n  with:\n    node-version: '22'\n    cache: true\n```\n\nSet `<setup-vp-version>` to an exact version from the [`setup-vp` releases page](https://github.com/voidzero-dev/setup-vp/releases). You can use a commit SHA instead. Do not use the `v1` tag. The `v1` tag no longer receives updates.\n\nSee [Automatic Version Updates](https://viteplus.dev/guide/ci#automatic-version-updates) to configure Dependabot or Renovate.\n\n#### Manual Installation & Migration\n\nIf you are manually migrating a project to Vite+, install these dev dependencies first:\n\n```bash\nvp install -D vite-plus\n```\n\nAdd package-manager overrides so that other packages use the Vite+ versions. Alias `vite` to `@voidzero-dev/vite-plus-core`, setting `<vite-plus-version>` to the installed `vite-plus` version. Pin `vitest` to `<vitest-version>`, using the bundled version shown by `vp toolchain vitest`. The project and `vp test` then use the same Vitest copy. Without the pin, a dependency or workspace package can install a different Vitest version. The two versions can use separate mocks, `expect` functions, and runner states:\n\n```json\n\"overrides\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\nIf you are using `pnpm`, add this to your `pnpm-workspace.yaml`:\n\n```yaml\noverrides:\n  vite@*: npm:@voidzero-dev/vite-plus-core@<vite-plus-version> # Match the installed vite-plus version\n  vitest@*: <vitest-version> # Match the version from vp toolchain vitest\n```\n\nOr, if you are using Yarn:\n\n```json\n\"resolutions\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\n## Sponsors\n\nThanks to [namespace.so](https://namespace.so) for powering our CI/CD pipelines with fast, free macOS, Linux, and Windows runners.\n","packages/cli/src/migration/migrator/README.md":"# `migrator/` — migration logic, split by category\n\nThe `vp migrate` implementation used to live in a single ~7,300-line\n`packages/cli/src/migration/migrator.ts`. It is now split into category\nmodules in this directory. `migration/migrator.ts` is a **barrel** that only\nre-exports them:\n\n```ts\n// migration/migrator.ts\nexport * from './migrator/shared.ts';\nexport * from './migrator/eslint.ts';\n// ...one line per module\n```\n\nSo **external code keeps importing from `./migrator.ts`** (the barrel) and\nnothing outside this directory had to change.\n\n## Modules\n\nPick the file by what a function _does_, not by where it happens to be called.\n\n| File                     | Owns                                                                                                                                                                                                                    |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `orchestrators.ts`       | Top-level entry points that wire everything together: `rewriteStandaloneProject`, `rewriteMonorepo`, `rewriteMonorepoProject`.                                                                                          |\n| `package-json.ts`        | `rewritePackageJson` and the direct dependency-spec rewriting it does.                                                                                                                                                  |\n| `vite-plus-bootstrap.ts` | \"Already on Vite+\" detection and the bootstrap/reconcile path: `detectVitePlusBootstrapPending`, `ensureVitePlusBootstrap`, `reconcileVitePlusBootstrapPackage`, the `ensure*`/`*Pending`/`*SatisfiesVitePlus` helpers. |\n| `catalog.ts`             | `pnpm-workspace.yaml` / catalog / overrides / build-allowance writers and catalog-dependency resolvers; bun catalog; `rewriteRootWorkspacePackageJson`; pnpm workspace-settings migration.                              |\n| `vitest-ecosystem.ts`    | Detecting direct vitest usage, the override-key (\"dependency selector\") parsing/dropping logic, managed-override sets, ecosystem alignment, legacy `vite-plus-test` wrapper-alias pruning.                              |\n| `vitest-v5.ts`           | Versioned Vitest v5 preflight, compatibility edits, and review findings. Pure AST and command transforms live under `../vitest-v5/`.                                                                                    |\n| `yarn.ts`                | `.yarnrc.yml`, Yarn PnP detection, workspace-hoisting fix, webdriverio detection.                                                                                                                                       |\n| `source-scan.ts`         | Scanning a project's source tree for signals (browser-mode, opt-in providers, `@nuxt/test-utils`, retained upstream-vitest references).                                                                                 |\n| `vite-config.ts`         | `vite.config.ts` merging, default-config injection, staged-config merge, lazy-plugin wrapping, import rewriting (`rewriteAllImports`), migrated-oxlint-config sanitization, lint-staged removal.                        |\n| `oxlint-config.ts`       | Oxlint JSON inheritance graphs and shared TypeScript config generation.                                                                                                                                                 |\n| `eslint.ts`              | ESLint → Oxlint migration, oxlint JS-plugin namespace handling, ESLint prompts/warnings.                                                                                                                                |\n| `prettier.ts`            | Prettier → Oxfmt migration and its prompts/warnings.                                                                                                                                                                    |\n| `tsup.ts`                | tsup → tsdown (`vp pack`) migration via `tsdown-migrate` and its prompts/warnings.                                                                                                                                      |\n| `tsx.ts`                 | tsx → `vpx` script migration, tsx dependency retention, and its prompt/warnings.                                                                                                                                        |\n| `tsconfig.ts`            | `tsconfig.json` cleanup and `types` rewriting.                                                                                                                                                                          |\n| `framework-shim.ts`      | Framework (Vue/Astro) shim detection and injection.                                                                                                                                                                     |\n| `git-hooks.ts`           | Vite+ hook defaults, project-owned hook preservation, and conservative detection of existing hook tools.                                                                                                                |\n| `setup.ts`               | `packageManager` pin, `setup-vp` GitHub Actions version, and Volta to `.node-version` migration.                                                                                                                        |\n| `core-finalization.ts`   | Finalizing an existing-Vite+ core migration (rules YAML, core package scripts).                                                                                                                                         |\n| `shared.ts`              | Cross-cutting constants, types, and tiny utilities used by **two or more** modules. The only module that other modules import from directly (see rules).                                                                |\n\n## Rules for adding / changing code\n\n1. **Add a function to the module that matches its category.** Keep it\n   `export`ed so the barrel surfaces it. If nothing fits, prefer extending an\n   existing module over creating a new one; if you do add a module, add a line\n   for it to `migration/migrator.ts`.\n\n2. **Importing another module's helper — use the barrel.** Reference\n   cross-module **functions** via the barrel:\n\n   ```ts\n   import { managedOverridePackages, rewritePackageJson } from '../migrator.ts';\n   ```\n\n   This creates an import cycle (the barrel re-exports your module), which is\n   **safe only because** these helpers are referenced _inside function bodies_\n   (at runtime), never at module-evaluation time. Preserve that invariant: do\n   not call a cross-module helper at the top level of a module.\n\n3. **`shared.ts` is the one exception — import it directly, never via the\n   barrel.** Things in `shared.ts` (e.g. `REMOVE_PACKAGES`,\n   `OPT_IN_BROWSER_PROVIDERS`, shared types) are referenced at _module-load_\n   time, so they must be imported from a fully-evaluated leaf:\n\n   ```ts\n   import { REMOVE_PACKAGES, type CatalogDependencyResolver } from './shared.ts';\n   ```\n\n   Keep `shared.ts` a **pure leaf**: it may import external packages and\n   `../utils/*` / `./report.ts` etc., but it must **not** import from any\n   sibling module or the barrel.\n\n4. **Where does a new shared thing go?** A constant / type / helper used by a\n   single module lives in that module. The moment a second module needs it,\n   move it to `shared.ts` and `export` it.\n\n5. **This split is structure only.** The barrel contains no logic; behavior\n   changes belong in the relevant module and need their unit test (and snap\n   test) updated, not the barrel.\n\n## When to add a new module\n\nDefault to extending an existing module. Add a new file only when one of these\nholds:\n\n- **A new self-contained category appears** — usually a new tool/format being\n  migrated (mirrors `eslint.ts`, `prettier.ts`, `yarn.ts`, `git-hooks.ts`).\n  Test: you can name its single responsibility without saying \"and\".\n- **An existing module outgrows readability _and_ has a clean seam** — a\n  cohesive, loosely-coupled sub-cluster. Rough trigger: **>~900–1,000 lines plus\n  a natural split point** (e.g. `catalog.ts` could become `catalog.ts` +\n  `pnpm-workspace.ts`). Size alone is not enough.\n- **Scattered helpers form a coherent theme** and consolidating them aids\n  discoverability.\n\nDo **not** add a file when: the function fits an existing category (extend it);\nit's one small helper (owning module, or `shared.ts` if shared); it would be 1–2\nfunctions with no theme (fragmentation is as bad as a monolith); or it would\nhave a tight two-way dependency with another module (they belong together).\n\nWhen you do add one: give it a single-responsibility name, add its `export *`\nline to `migration/migrator.ts`, add a row to the module table above, and decide\nits layer — if siblings reference it at **load time** it must be a pure leaf (or\nthose pieces go in `shared.ts`); helpers used only inside function bodies may\nimport from the barrel.\n\n## Validating a change\n\nFrom the repo root:\n\n```bash\nvp check                                             # format + lint + type-check\npnpm -F vite-plus exec vitest run src/migration      # migration unit tests\njust snapshot-test migration                         # migration CLI snapshot tests\n```\n\nReview any changed `.md` snapshots like code. A pure reorganization must not\nchange type-check results, the unit-test count, or snapshots. A behavior change\nshould be accompanied by the matching unit or snapshot test updates.\n","packages/cli/templates/generator/README.md":"# Vite+ Code Generator Starter\n\nA starter for creating a Vite+ code generator.\n\n## Usage\n\nFrom monorepo root:\n\n```bash\n# run and select the generator\nvp create\n```\n\nFor automation, provide the directory and every required template option:\n\n```bash\nvp create <generator-name> --no-interactive -- --directory new-package --name new-package\n```\n\nVite+ sets `VP_CREATE_INTERACTIVE=0` for non-interactive local Bingo generators.\nThis starter then validates the arguments and runs Bingo's programmatic API.\nMissing options and existing directories fail before any files are generated.\nExisting generators are copied project files and are not updated by upgrading Vite+.\nTo adopt this behavior, update their entrypoint to match this starter.\nDirect invocation uses the interactive CLI unless this variable is set to `0`.\nWhen adding template options, also add their CLI types in `bin/index.ts`.\n\n## Development\n\n```bash\n# Edit the template\ncode src/template.ts\n\n# Test the generator CLI\nvp run dev\n\n# Run tests\nvp run test\n```\n\n## Customization\n\nEdit `src/template.ts` to customize:\n\n- Options schema (using Zod)\n- File generation logic\n- Scripts and suggestions\n\nMore information about the [Bingo Templates](https://create.bingo/) can be found [here](https://create.bingo/build/concepts/creations).\n","packages/cli/templates/monorepo/README.md":"# Vite+ Monorepo Starter\n\nA starter for creating a Vite+ monorepo.\n\n## Development\n\n- Check everything is ready:\n\n```bash\nvp run ready\n```\n\n- Run the tests:\n\n```bash\nvp run -r test\n```\n\n- Build the monorepo:\n\n```bash\nvp run -r build\n```\n\n- Run the development server:\n\n```bash\nvp run dev\n```\n","packages/create-vite-plus/README.md":"# create-vite-plus\n\nCreate a new [Vite+](https://viteplus.dev) project without installing the global `vp` CLI.\n\n```bash\npnpm create vite-plus\nnpm create vite-plus@latest\nyarn create vite-plus\nbun create vite-plus\n```\n\nThis runs [`vp create`](https://viteplus.dev/guide/create) from the `vite-plus` package and uses the package manager that ran it, unless you pass `--package-manager` or create the project inside an existing workspace. Arguments are forwarded unchanged, so the first argument is the template:\n\n```bash\npnpm create vite-plus vite:application --directory my-app\npnpm create vite-plus vite -- --template react-ts\n```\n\nnpm needs `--` before options meant for `vp create`, for example `npm create vite-plus@latest -- vite:library --no-interactive`.\n","packages/prompts/README.md":"# @voidzero-dev/vite-plus-prompts\n\nTerminal prompts shared by Vite+ and tools that use its visual identity.\n\n```ts\nimport { isCancel, log, note, select } from '@voidzero-dev/vite-plus-prompts';\n\nconst framework = await select({\n  message: 'Choose a framework:',\n  options: [\n    { value: 'react', label: 'React', hint: 'TSX Pages' },\n    { value: 'vue', label: 'Vue', hint: 'Vue SFC Pages' },\n  ],\n});\n\nif (!isCancel(framework)) {\n  log.success(`Selected ${framework}`);\n  note('Run vp dev to start developing.', 'Next steps');\n}\n```\n\nBlue `›` pointers identify focused choices and active prompts. Completed steps\nand rounded box borders use gray; warnings use yellow and errors use red.\nInactive choices are dimmed, and descriptive hints appear on the focused choice.\nLogs retain severity symbols without guide bars and use compact spacing.\nNotes have complete borders and readable content by default.\n\nGuides are off by default. Pass `withGuide: true` for one call, or use\n`updateSettings({ withGuide: true })` to enable them globally. This controls the\nprompt gutter, not box borders. Logs accept `spacing` for explicit blank rows;\nnotes accept `format` and boxes accept `formatBorder` and `rounded`.\n\nSelect and multiselect prompts show compact keyboard instructions. Pass\n`showInstructions: false` to hide them. Text, password, path, and autocomplete\nvalidation supports synchronous or asynchronous functions and Standard Schema.\nPasswords accept a placeholder and always mask entered values. Path prompts\nsupport directory navigation and Tab completion; general autocomplete can opt\nin with `completeOnTab: true`.\n\n`accessible: true`, `updateSettings({ accessible: true })`, or `ACCESSIBLE=1`\nenables static prompt output for screen readers. Spinners use static output in\naccessible mode, CI, and non-TTY streams. Their `pause()` and `resume()` methods\npreserve elapsed time while excluding pauses. `CANCEL_SYMBOL` is exported for\ncallers that need the exact cancellation sentinel; `isCancel()` narrows results.\n\nThe interaction engine is `@clack/core` 1.5.1 or newer. Rendering and the shared\nvisual defaults are maintained in this package, so consumers can use the same\nappearance without implementing their own prompt renderers.\n\n## Verification\n\nFrom the repository root:\n\n```sh\nvp check packages/prompts/src\nvp test run packages/prompts/src/__tests__\npnpm --filter @voidzero-dev/vite-plus-prompts build\n```\n","packages/tools/README.md":"# tools for internal development use\n\n## Bins\n\n- json-edit: A CLI tool to edit JSON files such as package.json, used by the release workflow to stamp build versions\n\n## `tool` subcommands\n\nRun with `tool <name>`:\n\n- sync-remote: Sync upstream dependency sources and catalog versions from `.upstream-versions.json`\n- install-global-cli: Install the local build of the global `vp` CLI. The tool\n  reuses an existing `~/.vite-plus` install. Otherwise, it uses the split\n  platform data directory.\n- brand-vite: Apply Vite+ branding patches to the synced vite source (also runs at the end of sync-remote)\n- local-npm-registry: Serve locally packed checkout packages behind a real registry HTTP interface for snapshot tests, ecosystem e2e, and local `vp migrate`/`vp create` iteration\n\n## Local npm registry\n\nSee the [contributing guide](../../CONTRIBUTING.md#test-vp-migrate--vp-create-through-a-local-npm-registry) for local build and usage commands.\n\n[`src/local-npm-registry.ts`](src/local-npm-registry.ts) packs the checkout, serves its tarballs through a registry HTTP interface, and proxies other packages upstream.\n\n- Served versions use an old publish time so package-manager minimum-release-age checks allow local builds immediately.\n- Wrapped commands use temporary Yarn Berry and bun caches to avoid reusing stale local builds with the same package version.\n- The server also backs [PTY snapshot cases](../../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) and [ecosystem e2e tests](../../ecosystem-ci/patch-project.ts). Snapshots use recorded test packages and a run-scoped response cache. `local-registry = true` additionally serves the packed checkout; ecosystem and standalone runs retain upstream version merging.\n- Refresh the small snapshot registry fixtures explicitly with `node packages/tools/src/record-snapshot-registry.ts`. See the [fixture documentation](../../crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md).\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# Vite+\n\nVite+ is the web toolchain behind `vp`: Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, Vite Task, runtime and package-manager workflows, and project creation/migration.\n\nThis guide applies to this repository; `CLAUDE.md` points here. For changes to the guidance shipped to user projects, use `packages/cli/AGENTS.md` and `packages/cli/src/utils/agent.ts`. Leave those files unchanged for root-guide tasks.\n\n## Context by Task\n\n- **CLI routing**: `packages/cli/src/bin.ts` dispatches JS commands; `packages/cli/binding/src/lib.rs` and `packages/cli/binding/src/cli/mod.rs` handle local NAPI commands. Global routing starts at `crates/vp_global_cli/src/main.rs` and `crates/vp_global_cli/src/cli.rs`. Compare global and local paths for routing bugs.\n- **Package managers and runtimes**: use `crates/vp_pm_cli/` and `crates/vp_js_runtime/`. The Windows shim trampoline lives in `crates/vp_trampoline/`, outside the Cargo workspace.\n- **Config loading**: compare the [static extractor](crates/vp_static_config/README.md) with the JS fallback in `packages/cli/src/resolve-vite-config.ts`.\n- **Migration**: follow the [migrator instructions](packages/cli/src/migration/migrator/README.md) when changing `vp migrate`; use [migration rules](docs/guide/migrate-rules.md) for expected behavior.\n- **Bundled tools and exports**: use [CLI bundling](packages/cli/BUNDLING.md) and [core bundling](packages/core/BUNDLING.md). Test resolution starts at `packages/cli/src/resolve-test.ts`; `packages/cli/build.ts` generates test API shims.\n- **Testing a local build in a project**: follow [CONTRIBUTING.md](CONTRIBUTING.md) for linking and installation. For `vp migrate` / `vp create`, use the local registry in `packages/tools/src/local-npm-registry.ts`, which also serves PTY fixtures and ecosystem tests.\n- **Product documentation**: start at `README.md` and the VitePress site in `docs/guide/` and `docs/config/`.\n- **Setup and build commands**: use `CONTRIBUTING.md`, `justfile`, and `package.json`. Start with `just init` for initial setup. Repository lint, format, and test configuration lives in `vite.config.ts`; TypeScript settings live in `tsconfig.json`.\n\n## Repository Constraints\n\n- Distinguish built-ins from tasks: `vp test` executes upstream Vitest; `vp run test` executes a script or configured task named `test`. `vpr` is shorthand for `vp run`. Inspect command surfaces with `vp help` / `vp <command> --help`, and bundled versions with `vp --version`.\n- Keep task configuration under `run` in `vite.config.ts`; do not introduce `vite-task.json`. Existing `package.json` scripts are `vp run` targets without caching by default. Use `run.tasks` for explicit command configuration, default caching, dependencies, or input/environment tracking. A task name must occur in only one of these files. See the [run guide](docs/guide/run.md) and [run configuration](docs/config/run.md).\n- Keep public test imports on `vite-plus/test*`, which wraps upstream `vitest` and `@vitest/browser*`. Do not recreate `packages/test` or `@voidzero-dev/vite-plus-test`.\n- Vite Task crates are git dependencies in `Cargo.toml`; there is no local `crates/vt`. Do not run `cargo test -p vt` here.\n- Use `vp_shared::VpDirs` for Vite+ directory roots. See `crates/vp_shared/src/dirs.rs` and `crates/vp_shared/src/dirs/resolution.rs`. Call sites must not construct category paths or read `VP_HOME` or `XDG_*` directly.\n- Follow [`.clippy.toml`](.clippy.toml) for Rust restrictions and replacements. Use `crates/vp_shared/src/output.rs` for user-facing output; command output that can be piped must use `print_and_flush` or another broken-pipe-safe helper instead of `print!` or `println!`. Enable `clippy::print_stdout` in migrated modules so CI prevents direct stdout macros from returning. Use `crates/vp_shared/src/env_config.rs` for test-scoped environment configuration. See `crates/vp_command/src/lib.rs` for `vt_path` usage.\n- For TypeScript CLI output, use `packages/cli/src/utils/terminal.ts` and match the surrounding command style.\n- Keep changes scoped to the task and leave unrelated tracked and untracked files alone. Prefer source references over duplicated instructions in this guide.\n\n## Checks and Tests\n\nPrefer local fixtures or mock servers for tests that depend on external services. Use real network requests only when they are necessary to verify the behavior under test, and explain that need in the test. Reuse existing test infrastructure, preserve behavioral coverage, and keep mutable state isolated between cases.\n\nFor behavior changes, find nearby tests before editing and add or update coverage in the same area. Choose checks for the changed layer:\n\n| Change                         | Validation                                                                                                                         |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |\n| Documentation / agent guidance | Verify referenced paths, commands, and links; `git diff --check -- <files>`                                                        |\n| TypeScript / JS CLI            | `vp check`, `pnpm test:unit`, and focused package tests                                                                            |\n| Rust CLI / crates              | `just check`, `just test`, `just lint`                                                                                             |\n| CLI output / interactions      | Focused tests and `just snapshot-test <filter>`; review snapshot diffs                                                             |\n| Global CLI                     | `pnpm bootstrap-cli` and `vp --version` for installed end-to-end checks; `just snapshot-test-global <filter>` for global snapshots |\n| Release / build                | `just build`                                                                                                                       |\n| Full pre-merge check           | `pnpm bootstrap-cli && pnpm test && git status`                                                                                    |\n\nValidate `vp check`, lint, format, and type-check changes end-to-end: bundled-tool routing can hide config drift. Use `vp check --fix` only when you intend to apply formatting or lint fixes. Documentation-only changes need no unrelated test suites.\n\n### CLI Snapshots\n\nWrite new CLI tests, including interactive flows, in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`. Read the [snapshot runner instructions](crates/vp_cli_snapshots/tests/cli_snapshots/README.md) before adding cases; they define `snapshots.toml`, CLI flavors, `vpt` helpers, and prompt milestones. See [the RFC](rfcs/interactive-snapshot-tests.md) for design rationale.\n\n- Steps are argv arrays without an implicit shell. Use `vpt` helpers instead of coreutils for portable file operations.\n- For package-manager cases, prefer the runner's local registry, [shared registry fixtures](crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md), and response cache over new public-registry requests. Keep package-manager caches isolated per case, and refresh recorded fixtures only through the explicit maintenance command.\n- For changes to network use or scheduling, compare request counts and elapsed time with the same cases, worker count, and initial cache state. Do not remove assertions or share mutable case state to improve speed.\n- Local-flavor cases require a fresh `packages/cli/dist`. Use `just snapshot-test-global <filter>` when no JS build is available.\n- For intended snapshot changes, run `UPDATE_SNAPSHOTS=1 just snapshot-test <filter>`. Review and commit the recorded `.md` files with the fixture; mismatches fail the run and produce `.md.new` files.\n\n## Pull Requests\n\nPrefer a stack of small PRs for work with separable layers. Use the `gh-stack` extension or github.com; stacks require branches in this repository, so use standalone PRs from forks. Follow [CONTRIBUTING.md](CONTRIBUTING.md#submitting-pull-requests) for submission and commit-signing requirements.\n","isInternal":false,"tokens":1728,"sizeBytes":8143},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"@AGENTS.md\n","isInternal":false,"tokens":5,"sizeBytes":11},{"name":"SKILL.md","path":".claude/skills/add-ecosystem-ci/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/add-ecosystem-ci/SKILL.md","title":"add-ecosystem-ci","category":"anthropic-skill","format":"markdown","content":"---\nname: add-ecosystem-ci\ndescription: Add a new ecosystem-ci test case for testing real-world projects against vite-plus\nallowed-tools: Bash, Read, Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Add Ecosystem-CI Test Case\n\nAdd a new ecosystem-ci test case following this process:\n\n## Step 1: Get Repository Information\n\nAsk the user for the GitHub repository URL if not provided as argument: $ARGUMENTS\n\nUse GitHub CLI to get repository info:\n\n```bash\ngh api repos/OWNER/REPO --jq '.default_branch'\ngh api repos/OWNER/REPO/commits/BRANCH --jq '.sha'\n```\n\n## Step 2: Auto-detect Project Configuration\n\n### 2.1 Check for Subdirectory\n\nFetch the repository's root to check if the main package.json is in a subdirectory (like `web/`, `app/`, `frontend/`).\n\n### 2.2 Check if Project Already Uses Vite-Plus\n\nCheck the project's root `package.json` for `vite-plus` in `dependencies` or `devDependencies`. If the project already uses vite-plus, set `forceFreshMigration: true` in `repo.json`. This tells `patch-project.ts` to set `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping with \"already using Vite+\".\n\n### 2.3 Auto-detect Commands from GitHub Workflows\n\nFetch the project's GitHub workflow files to detect available commands:\n\n```bash\n# List workflow files\ngh api repos/OWNER/REPO/contents/.github/workflows --jq '.[].name'\n\n# Fetch workflow content (for each .yml/.yaml file)\ngh api repos/OWNER/REPO/contents/.github/workflows/ci.yml --jq '.content' | base64 -d\n```\n\nLook for common patterns in workflow files:\n\n- `pnpm run <command>` / `npm run <command>` / `yarn <command>`\n- Commands like: `lint`, `build`, `test`, `type-check`, `typecheck`, `format`, `format:check`\n- Map detected commands to `vp` equivalents: `vp run lint`, `vp run build`, etc.\n\n### 2.4 Ask User to Confirm\n\nPresent the auto-detected configuration and ask user to confirm or modify:\n\n- Which directory contains the main package.json? (auto-detected or manual)\n- What Node.js version to use? (22 or 24, try to detect from workflow)\n- Which commands to run? (show detected commands as multi-select options)\n- Which OS to run on? (both, ubuntu-only, windows-only) - default: both\n\n## Step 3: Update Files\n\n1. **Add to `ecosystem-ci/repo.json`**:\n\n   ```json\n   {\n     \"project-name\": {\n       \"repository\": \"https://github.com/owner/repo.git\",\n       \"branch\": \"main\",\n       \"hash\": \"full-commit-sha\",\n       \"forceFreshMigration\": true // only if project already uses vite-plus\n     }\n   }\n   ```\n\n2. **Add to `.github/workflows/e2e-test.yml`** matrix:\n   ```yaml\n   - name: project-name\n     node-version: 24\n     directory: web # only if subdirectory is needed\n     command: |\n       vp run lint\n       vp run build\n   ```\n\n## Step 4: Verify\n\n### 4.1 Build fresh tgz packages\n\nAlways rebuild tgz packages from latest source to avoid using stale cached versions:\n\n```bash\n# Rebuild the global CLI first (includes Rust binary + NAPI binding)\npnpm bootstrap-cli\n\n# Pack fresh tgz files into tmp/tgz/\nrm -rf tmp/tgz && mkdir -p tmp/tgz\ncd packages/core && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/test && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/cli && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\nls -la tmp/tgz\n```\n\n### 4.2 Clone and test locally\n\n```bash\nnode ecosystem-ci/clone.ts project-name\n```\n\n### 4.3 Patch and run commands\n\n```bash\n# Run from the ecosystem-ci temp directory\ncd $(node -e \"const os=require('os'); console.log(os.tmpdir() + '/vite-plus-ecosystem-ci')\")\n\n# Migrate the project (uses tgz files from tmp/tgz/)\nnode /path/to/vite-plus/ecosystem-ci/patch-project.ts project-name\n\n# Run the configured commands\ncd project-name\nvp run build\n```\n\n3. **Add OS exclusion to `.github/workflows/e2e-test.yml`** (if not running on both):\n\n   For ubuntu-only:\n\n   ```yaml\n   exclude:\n     - os: windows-latest\n       project:\n         name: project-name\n   ```\n\n   For windows-only:\n\n   ```yaml\n   exclude:\n     - os: ubuntu-latest\n       project:\n         name: project-name\n   ```\n\n## Important Notes\n\n- The workflow matrix `directory` field is optional - only add it if the project commands must run in a subdirectory (like a workspace member)\n- `patch-project.ts` always runs `vp migrate` and `vp install` at the clone root; `vp migrate` rejects workspace member targets\n- `forceFreshMigration` is required for projects that already have `vite-plus` in their package.json — it sets `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping\n- OS exclusions are added to the existing `exclude` section in the workflow matrix\n","frontmatter":{"name":"add-ecosystem-ci","description":"Add a new ecosystem-ci test case for testing real-world projects against vite-plus","allowed-tools":"Bash, Read, Edit, Write, WebFetch, AskUserQuestion","allowedTools":["Bash,","Read,","Edit,","Write,","WebFetch,","AskUserQuestion"]},"allowedTools":["Bash,","Read,","Edit,","Write,","WebFetch,","AskUserQuestion"],"isInternal":false,"tokens":1215,"sizeBytes":4646},{"name":"SKILL.md","path":".claude/skills/bump-vite-task/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/bump-vite-task/SKILL.md","title":"bump-vite-task","category":"anthropic-skill","format":"markdown","content":"---\nname: bump-vite-task\ndescription: Bump vite-task git dependency to the latest main commit. Use when you need to update the vite-task git-dependency crates (vt, fspy, vt_pty_terminal_test, and friends; the authoritative set lives in Cargo.toml) in vite-plus.\nallowed-tools: Read, Grep, Glob, Edit, Bash, Agent, WebFetch\n---\n\n# Bump vite-task to Latest Main\n\nUpdate the vite-task git dependency in `Cargo.toml` to the latest commit on the vite-task main branch, fix any breaking changes, and create a PR.\n\n## Steps\n\n### 1. Get current and target commits\n\n- Read `Cargo.toml` and find the current `rev = \"...\"` for any vite-task git dependency; enumerate the full set with `grep 'voidzero-dev/vite-task' Cargo.toml` instead of trusting a hardcoded list. They all share the same revision.\n- Get the latest commit hash on vite-task's main branch:\n  ```bash\n  git ls-remote https://github.com/voidzero-dev/vite-task.git refs/heads/main\n  ```\n\n### 2. Update Cargo.toml\n\n- Replace **all** occurrences of the old commit hash with the new one in `Cargo.toml`. The set of crates sharing the vite-task revision changes over time; the grep from step 1 is the authoritative list, so update every entry it returns.\n- The commented `[patch.\"https://github.com/voidzero-dev/vite-task.git\"]` section near the bottom of `Cargo.toml` mirrors the same crates for local vite-task development; keep it in sync when crates are added or removed (a plain rev bump does not touch it).\n\n### 3. Ensure upstream dependencies are cloned\n\n- `cargo check` requires the `./rolldown` and `./vite` directories to exist (many workspace path dependencies point to `./rolldown/crates/...`).\n- Locally, clone them using the commit hashes from `packages/tools/.upstream-versions.json`.\n- CI handles this automatically via the `.github/actions/clone` action.\n\n### 4. Verify compilation\n\n- Run `cargo check` to ensure the new vite-task compiles without errors.\n- If there are compilation errors, these are **breaking changes** from vite-task. Fix them in the vite-plus codebase (the consuming side), not in vite-task.\n- Common breaking changes include: renamed functions/methods, changed function signatures, new required fields in structs, removed public APIs.\n\n### 5. Run tests\n\n- Run `cargo test -p vp_command -p vp_error -p vp_pm_cli -p vp_js_runtime -p vp_migration -p vp_shared -p vp_static_config -p vite-plus-cli -p vp_global_cli` to run the vite-plus crate tests.\n- Note: Some tests require network access (e.g., `vp_pm_cli::package_manager` tests, `vp_global_cli::commands::env` tests). These may fail in sandboxed environments. Verify they also fail on the main branch before dismissing them.\n- Note: `cargo test -p vt` will NOT work because vt is a git dependency, not a workspace member.\n- The PTY snapshot suite (`crates/vp_cli_snapshots`) is excluded from `just test`; it is covered in step 6.\n\n### 6. Update snapshot tests\n\nvite-task changes often affect CLI output, which means snapshot tests need updating. Common output changes:\n\n- **Status icons**: e.g., cache hit/miss indicators may change\n- **New CLI options**: e.g., new flags added to `vp run` that show up in help output\n- **Cache behavior messages**: e.g., new summary lines about cache status\n- **Task output formatting**: e.g., step numbering, separator lines\n\n**PTY snapshot suite (`crates/vp_cli_snapshots`):**\n\n- A bump can break it two ways: runner compilation (it consumes vite-task's `vt_pty_terminal_test`, `vt_pty_terminal_test_client`, and `vt_snapshot_test` crates directly, so their API changes surface here; fix in the runner) and recorded CLI output.\n- Update output locally with real assertions: `UPDATE_SNAPSHOTS=1 just snapshot-test`, then review the `.md` diffs like code. Without a built `packages/cli/dist`, run the global flavor only: `VP_SNAP_SKIP_FLAVORS=local UPDATE_SNAPSHOTS=1 just snapshot-test`.\n- Windows runs in the `CLI snapshot test (Windows)` CI job via a cross-compiled nextest archive; snapshots are OS-shared, so a Windows-only diff there means a redaction gap, not a re-record.\n- Reference: `crates/vp_cli_snapshots/tests/cli_snapshots/README.md`.\n\n### 7. Review changelog and update docs\n\n- Fetch the vite-task `CHANGELOG.md` diff between old and new commits to see what changed:\n  ```\n  https://raw.githubusercontent.com/voidzero-dev/vite-task/<new-hash>/CHANGELOG.md\n  ```\n- Review each changelog entry and determine if it affects user-facing behavior: new CLI options, changed defaults, new config fields, removed features, etc.\n- The changelog contains links to the corresponding vite-task PRs. For complex changes, check the PR description and code diff (especially any docs changes in the PR) to understand the full scope of the change.\n- If user-facing changes are found, update the relevant docs in `docs/` (e.g., `docs/guide/`, `docs/config/`).\n- Common doc updates include:\n  - **New CLI flags/options**: Update the relevant config doc (e.g., `docs/config/run.md`, `docs/config/build.md`)\n  - **New features or commands**: Add or update the relevant guide page (e.g., `docs/guide/cache.md`)\n  - **Changed defaults or behavior**: Update any docs that describe the old behavior\n  - **Removed/deprecated options**: Remove or mark as deprecated in the relevant docs\n- If no user-facing changes are found, skip this step.\n\n### 8. Create the PR\n\n- Commit message: `chore: bump vite-task to <short-hash>`\n- PR title: `chore: bump vite-task to <short-hash>`\n- PR body: Link to vite-task CHANGELOG.md diff between old and new commits:\n  ```\n  https://github.com/voidzero-dev/vite-task/compare/<old-hash>...<new-hash>#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed\n  ```\n\n### 9. Verify CI\n\nAfter creating the PR, automatically watch CI without asking the user first. Ensure the `done` check passes. Key checks to monitor:\n\n- **Lint**: Clippy and format checks\n- **Test** (Linux, Mac, Windows): Rust unit tests\n- **CLI snapshot test** (Linux, Mac, Windows): the PTY snapshot suite - most likely to fail on a vite-task bump (runner compiles against vite-task crates AND asserts CLI output)\n- **Run task**: Task runner integration tests\n- **Security Analysis**: Fix any `cargo deny` advisories. It runs only when `Cargo.lock` changes, so a green `main` does not mean the lockfile is clean.\n\nThe only **required** status check for merging is `done`, which aggregates the other checks (excluding Security Analysis).\n\n## Notes\n\n- Building the full CLI locally (`pnpm bootstrap-cli`) requires the rolldown Node.js package to be built first, which is complex. Prefer the global-only snapshot command above or CI when no local CLI build is available.\n- `Cargo.lock` is automatically updated by cargo when you change the revision in `Cargo.toml`.\n","frontmatter":{"name":"bump-vite-task","description":"Bump vite-task git dependency to the latest main commit. Use when you need to update the vite-task git-dependency crates (vt, fspy, vt_pty_terminal_test, and friends; the authoritative set lives in Cargo.toml) in vite-plus.","allowed-tools":"Read, Grep, Glob, Edit, Bash, Agent, WebFetch","allowedTools":["Read,","Grep,","Glob,","Edit,","Bash,","Agent,","WebFetch"]},"allowedTools":["Read,","Grep,","Glob,","Edit,","Bash,","Agent,","WebFetch"],"isInternal":false,"tokens":1639,"sizeBytes":6737},{"name":"SKILL.md","path":".claude/skills/release-manager/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/release-manager/SKILL.md","title":"release-manager","category":"anthropic-skill","format":"markdown","content":"---\nname: release-manager\ndescription: Run the standard vite-plus release process end-to-end as the release manager. Covers preparing the release PR, syncing the NAPI binding version, writing the categorized changelog, smoke-testing via a preview build, handling release-branch CI failures, merging, and post-release verification and announcements. Use when asked to cut, prepare, or manage a vite-plus release (e.g. \"release v0.2.3\", \"act as release manager\").\nallowed-tools: Bash, Read, Edit, Write, Grep, Glob, WebFetch\n---\n\n# Vite+ Release Manager\n\nRun a standard vite-plus release from version bump to published announcement. Any maintainer with repo write access can follow this; the only extra privilege needed is approval rights on the `release` GitHub environment (step 7).\n\n## Usage\n\n```\n/release-manager                    # start a new release: ask for the target version, begin at step 1\n/release-manager X.Y.Z              # start a new release for that version\n/release-manager <PR URL or #N>     # take over an in-flight release\n```\n\nWhen given a release PR (URL or number), do not start from step 1. First audit the release's current state, then continue from the earliest unfinished step:\n\n- Is the binding version synced? (step 2: `grep -c \"'<prev>'\" packages/cli/binding/index.cjs` on the release branch)\n- Do the release version examples in the migration guide and the setup, migration, and upgrade prompts match `packages/cli/package.json`? (step 2)\n- Is the PR description still the `prepare_release` boilerplate, or already a categorized changelog? (step 3)\n- Is a preview build present and for the current head? (step 4)\n- Does `main` have commits the release branch lacks? (`git log origin/release/vX.Y.Z..origin/main`, step 5)\n- What is CI status? (`gh pr checks <PR#>`, step 5)\n- Already merged? Check the Release workflow (`gh run list --workflow Release --repo voidzero-dev/vite-plus`) and whether the GitHub release body is still the generated stub, then continue at step 7 or 8.\n\nReport the detected state before making changes, so the previous release manager's work is not redone or overwritten.\n\nBefore post-release work, fetch `origin/main` and read its copy of this skill (`git show origin/main:.claude/skills/release-manager/SKILL.md`). A checkout left behind after the merge can contain superseded release or announcement instructions.\n\n## Pipeline overview\n\n1. `Prepare Release` workflow bumps versions and opens the release PR (`release/vX.Y.Z` -> `main`).\n2. Release manager: sync `binding/index.cjs` and the release versions in the documentation and prompts, write the changelog PR description, offer the preview-build smoke test (recommend it when the release has more than 10 commits since the previous tag), get CI green.\n3. Merging the PR pushes a `packages/cli/package.json` change to `main`, which triggers `release.yml`: build, manual approval gate, npm publish, GitHub release, Docker image, Discord notification.\n4. Release manager: polish the GitHub release notes, verify installs, announce.\n\nCanonical sources: `.github/workflows/prepare_release.yml`, `.github/workflows/release.yml`, `.github/workflows/publish-preview.yml`.\n\n## 1. Start the release\n\n```bash\ngh workflow run prepare_release.yml --repo voidzero-dev/vite-plus -f version=X.Y.Z\n```\n\nThe workflow bumps `packages/cli/package.json`, `packages/core/package.json`, `packages/cli/binding/Cargo.toml`, and `crates/vp_global_cli/Cargo.toml`, refreshes `Cargo.lock`, and opens a PR titled `release: vX.Y.Z` from branch `release/vX.Y.Z`. The PR body ends with `Merging this PR will trigger the release workflow.` and that line must survive every later edit.\n\n## 2. Sync release versions (required every release)\n\n### NAPI binding\n\nNAPI bakes the package version into version checks in `packages/cli/binding/index.cjs` (26+ sites). `prepare_release` bumps `package.json` but does not regenerate this file, so CI's `Ensure no unexpected file changes after build` step in the `CLI E2E test` job fails until it is synced. Do this immediately; do not wait for CI to fail.\n\n```bash\ngit fetch origin release/vX.Y.Z && git checkout release/vX.Y.Z\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs          # non-zero means sync needed\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs\n```\n\nApply two whole-file text replacements (`<prev>` is the previous release version, e.g. `0.2.1`):\n\n1. `'<prev>'` -> `'<curr>'`\n2. `expected <prev> but got` -> `expected <curr> but got`\n\nThen confirm the replacement took; both counts must now be zero:\n\n```bash\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs                 # 0\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs  # 0\n```\n\nDo not regenerate via a full build; the text replace is deterministic and byte-identical to what `napi build` would produce for a version-only bump. Commit with this exact message shape (it makes `git log --grep` find the sync across releases) and push:\n\n```\nchore(release): sync binding/index.cjs version to <curr>\n\nNAPI bakes the package.json version into binding/index.cjs version\nchecks. The prepare_release workflow bumps package.json but does not\nregenerate this file, so the CI build's regeneration step produces a\ndiff that the post-build no-unexpected-changes guard rejects.\n```\n\n### Documentation and prompts\n\nUse the version in the release branch's `packages/cli/package.json` as the target release version in these files:\n\n- `docs/guide/migrate.md`: pnpm and npm migration command examples and matching release prose.\n- `docs/.vitepress/theme/data/migration-prompts.ts`: `setupPrompt`, `migrationPrompt`, `upgradePrompt`, and their shared instructions, including command examples and matching release prose. `CopyPrompt` uses `setupPrompt` on both the homepage and Getting Started guide.\n\nUpdate every `--package=vite-plus@<curr>` pin and the corresponding `For the <curr> release` and `Replace <curr>` text. Keep an exact version; do not replace it with a placeholder, a major range, or `latest`.\n\nPreserve historical versions such as the migration's source version and the release that introduced a breaking change. Leave Node.js requirements, bundled tool versions, and preview-registry instructions unchanged unless their requirements change.\n\nCommit these updates on the release branch with the binding sync or in a separate release-version sync commit. Recheck both files and the binding after a target-version change or a merge from `main`. Before merging, confirm that the guide and all three prompts use the target release in both package-manager commands and their matching prose, then run `git diff --check`.\n\nOnly these release-version sync commits go directly on the release branch. Everything else goes through `main` (see step 5).\n\n## 3. Write the release PR description\n\nThe release tag does not exist yet, so read release files from the PR head branch and generate notes against `main`:\n\n```bash\ngit fetch --tags && git tag --sort=-version:refname | head -5   # find <prev>\ngit log --oneline v<prev>..origin/main\ngh api repos/voidzero-dev/vite-plus/releases/generate-notes \\\n  -f tag_name=v<curr> -f previous_tag_name=v<prev> -f target_commitish=main\n\ngit show origin/release/v<curr>:packages/core/package.json          # bundledVersions: vite, rolldown, tsdown\ngit show origin/release/v<curr>:packages/tools/.upstream-versions.json  # vite/rolldown commit hashes\ngit show origin/release/v<curr>:pnpm-workspace.yaml                 # vitest/oxlint/oxlint-tsgolint/oxfmt catalog pins\n```\n\n### Structure\n\n```markdown\n<One or two sentences on the release theme. Do not repeat the PR title as an opener line; GitHub renders the title directly above the body, and step 8 would only strip it again. When a blog post accompanies the release, read it first (via its preview URL if not yet deployed), align the theme with it, and link the final URL here even if that URL is not live yet.>\n\n### Breaking Changes\n\n### Features\n\n### Fixes & Enhancements\n\n### Refactor\n\n### Docs\n\n### Chore\n\n### Bundled Versions\n\n### Upgrade\n\n### New Contributors\n\n**Full Changelog**: https://github.com/voidzero-dev/vite-plus/compare/v<prev>...v<curr>\n\n---\n\nMerging this PR will trigger the release workflow.\n```\n\n### Categorization rules\n\n- Every PR from `generate-notes` appears exactly once, except fully reverted changes described below and bot-authored PRs that carry nothing for a user to read or act on (a docs stats refresh, a badge update). Keep bot PRs that do change what users get, such as the upstream dependency upgrades. Record each omitted PR and its reason when reporting validation counts.\n- **Breaking Changes goes first, above Features, and only when the release has one.** A rename is breaking only when the old name stops working; if a deprecated alias is retained it is not breaking, so keep the two in different sections rather than merging them into one entry. Give each breaking entry an old -> new table when several names change, plus one line telling readers where to update (shell profile, CI job, Dockerfile). Do not editorialize about the version number.\n- When several breaking changes affect different workflows, group them under short `####` headings. Explain the changed behavior and required action before each table; keep automatic migration steps separate from changes users must make manually.\n- **Describe the net change between the two released versions, not intra-cycle churn.** When several PRs touch the same area within one release (one narrows a behavior, a later one broadens it back), the reader only sees the delta from `v<prev>` to `v<curr>`; describe that once, listing every PR number, and do not narrate a regression that was introduced and then fixed inside the cycle. Apply this to the intro/theme sentence too.\n- If a change and its complete revert are both unreleased, omit both when they leave no net change. Remove sections with no remaining entries. A revert of behavior in the previous release still needs an entry.\n- `feat` -> Features, `fix` -> Fixes & Enhancements, `refactor` and `revert` -> Refactor (never Chore), `docs` -> Docs, `test` / `ci` / `chore` -> Chore.\n- **Place an entry by its user impact when the prefix disagrees.** A `fix` that gives an existing command new observable behavior (for example, starting to set environment variables for child processes) belongs in Features. A fix that makes a command reject input it used to drop silently stays in Fixes & Enhancements, not Breaking Changes.\n- `feat(docs)` goes in Docs when the user-facing surface is the docs site.\n- **Docs means the published docs site, not contributor files.** A `docs` commit that changes an RFC, `AGENTS.md`, the repo map, or a skill under `.claude/` belongs in Chore: a vite-plus user never reads those. Docs should hold only entries a reader could go and look at on the site or in the README.\n- **Describe behaviour, not resolution logic.** An entry states what a user now observes. Rules the implementation follows internally (target-selection signals, config precedence, detection order) belong in the RFC or the PR, not the changelog. If an entry needs a nested list to explain how a decision is reached, cut it down to the outcome.\n- **A breaking change needs its migration path.** State what existing installs or projects do by default, then how to move to the new behaviour deliberately, then what that costs. Link the guide rather than restating it, and say plainly when doing nothing is a valid choice.\n- There is no separate Highlights section. Lead Features with up to 5 new capabilities a vite-plus user will notice, plus security fixes, then the remaining features; the upstream upgrade entry stays last. Bug fixes go in Fixes & Enhancements even when they are prominent. Skip developer-tooling-only conveniences.\n- Entry format: `Description ([#N](https://github.com/voidzero-dev/vite-plus/pull/N)), by @author`. Describe the user-visible behavior, not the implementation. Group supporting implementation PRs under the user-visible change they enable instead of giving them separate entries. Never include defensive edge cases or internal mechanics unless users need them to use or understand the feature; use concrete behavior instead of internal UI taxonomy that needs extra context. For a fix to an intermittent failure, say that it was rare and when it happened, so the entry does not read as if it always failed. When a PR carries over a superseded PR from another author (its body says so, or `gh pr view N --json commits` lists their commits), credit both authors.\n- **Upstream dependency upgrade PRs** (`feat(deps): upgrade upstream dependencies`): consolidate all of them into one Features entry with net oldest-to-latest version changes (e.g. `vite 8.0.16 -> 8.1.2`), listing every PR number. Check the upgraded range for security fixes (search the upstream changelog for CVE/GHSA); if present, add a dedicated security entry quoting severity and linking the advisory. Cite only published advisories: a draft advisory that maintainer access lets you see is still embargoed, so keep it and its fix's security framing out of the notes and the announcement. When oxfmt or oxlint changed version, add one clause telling users the new versions can flag code that passed before, so they should run `vp fmt` after upgrading if their CI runs `vp check`; in ecosystem testing this is reliably the largest single class of post-upgrade CI failures. Put this entry last in Features as a parent bullet carrying the PR links and credits, with one sub-item per upgraded tool and one per user-facing consequence (new formatting or lint behavior, migration changes, compatibility notes); a single long line is hard to read.\n- **vite-task bumps** (`bump vite-task to <commit>`): expand the full rev range (compare `Cargo.toml` at `v<prev>` vs the release branch), run `git log <old>..<new>` in the local vite-task checkout, and read vite-task's `CHANGELOG.md` at the new commit for wording. Promote user-visible upstream changes into Features / Fixes with `[vite-task#N](https://github.com/voidzero-dev/vite-task/pull/N)` links, crediting the upstream PR author (`gh pr view N --repo voidzero-dev/vite-task --json author`). Cross-repo link format is `[vite-task#N]` / `[vite#N]`, not `[owner/repo#N]`.\n- New Contributors: copy from `generate-notes`, exclude bots (`renovate[bot]`, `voidzero-guard[bot]`, `github-actions[bot]`), list as inline `@mentions`. `generate-notes` only covers vite-plus, so also add first-time contributors from the expanded vite-task range: an author is new when `gh api -X GET search/issues -f q='repo:voidzero-dev/vite-task is:pr is:merged author:<login> merged:<<date of the range start>' -q .total_count` is 0. List them with the others, without a repository label, in the release notes and the Discord thanks line.\n\n### Bundled Versions table\n\n| Tool            | Version | Source                                                                  | Changelog                                                              |\n| --------------- | ------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| vite            | `X.Y.Z` | [`<short-sha>`](https://github.com/vitejs/vite/commit/<full-sha>)       | [X.Y.Z](https://github.com/vitejs/vite/releases/tag/vX.Y.Z)            |\n| rolldown        | `X.Y.Z` | [`<short-sha>`](https://github.com/rolldown/rolldown/commit/<full-sha>) | [X.Y.Z](https://github.com/rolldown/rolldown/releases/tag/vX.Y.Z)      |\n| tsdown          | `X.Y.Z` | [npm](https://npmx.dev/package/tsdown/v/X.Y.Z)                          | [X.Y.Z](https://github.com/rolldown/tsdown/releases/tag/vX.Y.Z)        |\n| vitest          | `X.Y.Z` | [npm](https://npmx.dev/package/vitest/v/X.Y.Z)                          | [X.Y.Z](https://github.com/vitest-dev/vitest/releases/tag/vX.Y.Z)      |\n| oxlint          | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint/v/X.Y.Z)                          | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxlint_vX.Y.Z) |\n| oxlint-tsgolint | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint-tsgolint/v/X.Y.Z)                 | [X.Y.Z](https://github.com/oxc-project/tsgolint/releases/tag/vX.Y.Z)   |\n| oxfmt           | `X.Y.Z` | [npm](https://npmx.dev/package/oxfmt/v/X.Y.Z)                           | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxfmt_vX.Y.Z)  |\n\nvite and rolldown are built from pinned commits, so link the commit. The npm-installed tools link to npmx.dev. The Changelog column links the upstream release notes for every version in the upgraded range, comma-separated (for example the two patch releases between the previous and new pin); write `unchanged` when the version did not move.\n\n### Style rules\n\n- No em dashes or en dashes anywhere in the title or body. Use commas, colons, or parentheses.\n- Keep the PR title plain (`release: vX.Y.Z`) and the GitHub release title plain (`vite-plus vX.Y.Z`); the theme goes in the opening sentence. Lead the opening theme with the most important user-visible behavior. Avoid vague benefit-only wording that the body must explain.\n- When naming a package version in prose or a heading, use one inline literal, `package@version`. Keep separate version columns in tables.\n- Link unfamiliar technical abbreviations to the relevant documentation section; keep the short abbreviation as the link text.\n- The Upgrade section is a `vp upgrade` code block.\n- Apply via a temp file, never a heredoc (heredoc quoting can escape backticks inside the table and break rendering):\n\n```bash\ngh pr edit <PR#> --repo voidzero-dev/vite-plus --title \"release: vX.Y.Z\" --body-file /tmp/pr-body.md\n```\n\n### Validate before finishing\n\n```bash\nBODY=$(gh pr view <PR#> --repo voidzero-dev/vite-plus --json body -q '.body')\n# every generate-notes PR present (minus documented omissions), none duplicated:\necho \"$BODY\" | grep -oE 'voidzero-dev/vite-plus/pull/[0-9]+' | sort -u | wc -l\necho \"$BODY\" | grep -oE '(vite-plus|vite-task)/pull/[0-9]+' | sort | uniq -d   # must be empty\necho \"$BODY\" | grep -nE '[—–]'                                                  # must be empty\necho \"$BODY\" | grep -c '\\\\`'                                                    # must be 0 (escaped backticks)\necho \"$BODY\" | tail -1                                                          # boilerplate closing line intact\n```\n\nDiff the body's PR numbers against `generate-notes` rather than only counting them: a count alone hides one missing entry offsetting one extra. Every number in the missing list must have a documented reason for omission.\n\n## 4. Preview build smoke test (before merging)\n\nThis step runs **after the changelog (step 3) is complete and before merging (step 6)**. **Always ask the release manager whether to run it, and ask about both levels explicitly** (the local `vp migrate` sweep, and the fork-PR CI validation below); never silently skip either, and do not add the label on your own.\n\nDecide what to recommend before you ask, by counting the commits the release actually contains:\n\n```bash\ngit rev-list --count v<prev>..origin/main\n```\n\n**Recommend running the smoke test when that count is above 10**, or when the release touches migrate/create behavior, package-manager or install-path handling, or the native bindings, whatever the count. Recommend skipping only for a release that is both small (10 commits or fewer) and clear of those areas. State the count and your recommendation in the question so the release manager can overrule it, and say roughly what it costs, since the full catalog runs for hours.\n\nIf the release manager approves, **read and follow [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md) first**, then validate against the **full ecosystem-ci catalog** (every runnable fork), not a single project.\n\nIf the release manager says yes:\n\n1. Add the `preview-build` label to the release PR to publish installable `0.0.0-commit.<head-sha>` builds through the registry bridge:\n\n   ```bash\n   gh pr edit <PR#> --repo voidzero-dev/vite-plus --add-label \"preview-build\"\n   ```\n\n2. Wait for the `Publish preview build` workflow run on the release branch to succeed (it packs the built package directories and registers the commit with the registry bridge, then comments the build info on the PR).\n3. Verify the build against every runnable project in the catalog with the `test-pkg-pr-new-migrate` skill: it runs `vp migrate` from the preview commit against each local checkout, with dependencies resolved through the registry bridge. Report the outcome to the release manager before moving on.\n\n**The catalog.** The smoke-test catalog and the local-setup rules live in the ecosystem-ci org: [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md), with the machine-readable list in [`ecosystem.json`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/ecosystem.json) (each fork's upstream, tracked branch, and package manager). Run every runnable fork; filter `ecosystem.json` with `jq` to skip non-JS `other` repos and any the release manager says to ignore. Forks pinned to the immediately previous release exercise a real upgrade rather than a no-op.\n\n> **Mandatory: open any test PR against the `vite-plus-ecosystem-ci` fork, never the upstream repo.** `gh pr create` inside a fork defaults its base repo to the parent (upstream), so pass `--repo vite-plus-ecosystem-ci/<repo>` (or run `gh repo set-default vite-plus-ecosystem-ci/<repo>` first). See TESTING.md.\n\n`test-pkg-pr-new-migrate` needs a **local** checkout on the fork's **tracked branch** (often not the default branch, e.g. `vue-core` tracks `minor`). Clone under one directory so the whole test environment cleans up in one step:\n\n```bash\nrepo=<repo>; branch=<tracked-branch>   # from ecosystem.json\ngit clone git@github.com:vite-plus-ecosystem-ci/$repo.git ~/git/github.com/vite-plus-ecosystem-ci/$repo\ngit -C ~/git/github.com/vite-plus-ecosystem-ci/$repo checkout \"$branch\"\n# ... run the harness against ~/git/github.com/vite-plus-ecosystem-ci/$repo ...\n# cleanup after the release: rm -rf ~/git/github.com/vite-plus-ecosystem-ci\n```\n\nThe `.github` repo also ships `scripts/setup-local.sh <repo>` (or `--all`), which does the clone, tracked-branch checkout, remotes, and fork base-repo pinning from the manifest in one step.\n\n**Sync every fork to upstream before you test anything.** The forks drift, often by hundreds of commits, so a checkout straight from `origin` validates stale code and any PR you open against it carries all that drift instead of just the upgrade. From the `.github` checkout, run the safe sync command before the local sweep:\n\n```bash\nscripts/sync-forks.sh --all\n```\n\nThe command fast-forwards only forks with no fork-only commits. Exit code `2` means that at least one fork needs a sync or manual work. Resolve or explicitly skip each reported fork. Never overwrite a divergent tracked branch.\n\nRun `scripts/sync-forks.sh \"$repo\"` again immediately before you open each fork PR. If it prints `REOPEN`, close and reopen the current release PR so GitHub calculates a new merge base. Close superseded PRs and delete their branches. TESTING.md carries the full procedure.\n\n**Validate in the project's own CI.** Beyond the local `vp migrate`, exercise the prerelease in the fork's real CI by opening a draft PR on the fork, following \"Smoke-test via a fork PR\" in TESTING.md: branch `update-vite-plus-prerelease-test-<version>` synced from `source`, apply the upgrade, open a **draft** PR on the fork (never upstream) **assigned to the release manager**, then watch its checks for upgrade-related failures. Offer this alongside the local sweep rather than treating it as an afterthought; it is the only level that exercises each project's own build and tests. Some projects' CIs install with a non-standard tool that cannot resolve preview builds through the bridge `.npmrc` (e.g. cnpmcore's `utoo`), so check the install step before trusting fork-CI results.\n\nThe workflow triggers only on the `labeled` event, not on new pushes. To rebuild after the head moves (e.g. after a step 5 merge from `main`), remove and re-add the label (this cancels an in-flight build for the branch). A stale build whose diff to the new head is test-only is still valid for smoke testing; ask before re-triggering.\n\nRecord each project's base commit, starting version, resolved preview commit, fork PR head, and CI run. After a new preview build, identify which projects ran again and which retain earlier evidence. Do not report a partial repeat as a full-catalog run on the new preview.\n\n### Example (v0.2.2, PR #2016)\n\nChangelog complete, CI green, release manager approved the smoke test. A build existed for head `06708538`; the head had since moved by a test-only merge from `main`, so that build was still valid and was not re-triggered.\n\nHere the target was vibe-dashboard `main` (a `vite-plus-ecosystem-ci` fork, pnpm monorepo on `vite-plus 0.2.1`, i.e. the previous release), so the run exercised the common upgrade path. Pass the release PR number; the harness resolves it through the bridge to the latest published immutable commit and prints the resolved SHA (confirm it matches the build you expect). Pass a full commit SHA instead only to pin a specific build when several have been published:\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh 2016 ~/git/github.com/vite-plus-ecosystem-ci/vibe-dashboard --no-interactive\n```\n\nA passing run looks like:\n\n```\n◇ Updated . to Vite+ 0.0.0-commit.06708538...\n• Dependencies:\n    vite-plus  0.2.1 → 0.0.0-commit.06708538...\n    vite             → 8.1.2\n✓ Dependencies installed in 5.7s\n\nMigration worktree changes (.npmrc force-staged so it survives .gitignore):\nA  .npmrc                      # bridge registry written by vp migrate\n M package.json / pnpm-workspace.yaml / pnpm-lock.yaml\n\nFound 1 version of @voidzero-dev/vite-plus-core\nFound 1 version of vite-plus\nFound 1 version of vitest\n```\n\nPass criteria: the upgrade lands on the `0.0.0-commit.<sha>` build, the install succeeds through the bridge registry, and each of `@voidzero-dev/vite-plus-core`, `vite-plus`, and `vitest` resolves to exactly ONE version (`vitest` at the bundled upstream version). Multiple or stale versions mean the migration or install is broken: stop and treat it as a release blocker. Report the outcome to the release manager either way.\n\nOne exception to rule out first: `0.0.0-commit.<sha>` is a prerelease, and a `*` range does not match prereleases. A package that declares an optional `vite-plus: '*'` peer (oxlint and oxfmt do) can therefore keep a stale `vite-plus` from the old lockfile under pnpm, so the check reports two versions. If the previous-release control resolves to one version and the extra copy hangs only off such a peer, the duplicate is a preview-build artifact.\n\n### Triaging failures across the catalog\n\nAcross the full catalog most failures are not regressions, and reporting them as \"N failed\" without triage is useless to the release manager. Sort every failure into one of these before drawing any conclusion:\n\n- **Registry-bridge fetch flakes.** Check these first, because they are common and they masquerade as something far worse. The bridge drops tarball requests under load: pnpm logs `error (23). Will retry`, or the install dies with `ECONNRESET  aborted`. The dangerous case is a platform binding, because `@voidzero-dev/vite-plus-<platform>` is an **optional** dependency: when its download exhausts the retries, the installer skips it and still reports success, and the job then fails much later at the first command that loads the binding, with `Cannot find native binding` / `Cannot find module '@voidzero-dev/vite-plus-linux-x64-gnu'`. That is a local `node_modules` resolution failure and says nothing about the registry, but NAPI's loader appends generic \"npm has a bug related to optional dependencies\" boilerplate that reads like a publishing problem. Do not conclude the addon was unpublished; the bridge publishes every platform package for every commit build. Confirm, then re-run:\n\n  ```bash\n  curl -s \"https://registry-bridge.viteplus.dev/@voidzero-dev%2fvite-plus-linux-x64-gnu\" \\\n    | python3 -c \"import json,sys; print('0.0.0-commit.<sha>' in json.load(sys.stdin)['versions'])\"\n  gh run rerun <run-id> --failed --repo <owner>/<repo>\n  ```\n\n  Grep every failing log for `error (23)` and `ECONNRESET` before classifying it as anything else. In one release this single cause accounted for 8 fork failures, all of which passed on re-run.\n\n- **Local TLS inspection.** If every package fails locally with `ERR_PNPM_META_FETCH_FAIL ... fetch failed` while `curl` to the bridge succeeds, a TLS-inspecting client (such as a corporate zero-trust agent) may be re-signing `registry-bridge.viteplus.dev` with a root that the OS trusts but Node does not. `node -e \"fetch('https://registry-bridge.viteplus.dev/vite-plus')\"` then fails with `SELF_SIGNED_CERT_IN_CHAIN`. Export that root certificate and set `NODE_EXTRA_CA_CERTS` for the harness and every control run. A follow-up \"latest release is ...\" message comes from pnpm's stale metadata cache and is a symptom, not the cause.\n\n- **Preview-build artifacts.** These are caused by the `0.0.0-commit.<sha>` version string itself and cannot happen for a real npm release, so they are never blockers. The recurring ones: pnpm `ERR_PNPM_TRUST_DOWNGRADE` (\"possible package takeover\"), npm `ETARGET` from a `before`/min-release-age policy, bun `minimum release age`, `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` when a project declares `vite` as a peer (migrate writes the `npm:@voidzero-dev/vite-plus-core@...` alias there), `ERR_PNPM_TARBALL_URL_MISMATCH` or a failed supply-chain policy check against the bridge tarball URLs, Docker builds whose context does not carry the bridge `.npmrc`, project scripts that query `vite@*` or `vite-plus@*` and find nothing (the prerelease does not match `*`), and npm 10 failing with `Unable to resolve reference $vite-plus` on a nested `overrides` entry. Confirm the last two by swapping the commit version for a real release in a scratch copy.\n- **Pre-existing failures.** Prove it rather than asserting it, with whichever control is cheaper: install the previous release into an isolated home and re-run the same command, or check whether the fork's base branch CI already fails. The isolated-home control is the highest-value technique in this step, since it converts a scary-looking failure into a one-line fact:\n\n  ```bash\n  VP_HOME=$HOME/.cache/vp-control-<prev> VP_VERSION=<prev> VP_NODE_MANAGER=no \\\n    VP_SELF_SETUP_NO_MODIFY_PATH=1 bash packages/cli/install.sh\n  cd <project> && VP_HOME=$HOME/.cache/vp-control-<prev> VP_NODE_MANAGER=no \\\n    PATH=\"$HOME/.cache/vp-control-<prev>/bin:$PATH\" vp migrate <project> --no-interactive\n  ```\n\n  `VP_SELF_SETUP_NO_MODIFY_PATH=1` keeps the control installation out of the user's shell profiles; without it, every new shell sets `VP_HOME` to the control.\n\n  **Run the control from inside the project directory.** Launching it from the vite-plus checkout makes `vp` delegate to that checkout's `packages/cli/dist` instead of the pinned release, which silently invalidates the comparison (it fails with an unrelated error such as `Fail to parse yaml as RuleConfig`).\n\n  Run candidate and control in isolated checkouts of the same project base, starting with the same lockfile and no `node_modules`. Compare any lockfile changes made by migration so unrelated dependency versions do not invalidate the control. When the control reproduces the failure, report the evidence (same exit code and error class).\n\n- **Stale pins: weight the forks that were actually on the previous release.** A fork pinned several releases back does not test the release under review at all; `vp migrate` performs a multi-release jump, and any resulting type errors are evidence about that jump. Before drawing a conclusion, work out what each fork was on and judge the release primarily on the forks upgrading from the immediately previous release. Derive the pin from the upgrade commit itself, not from `HEAD`, since later commits on the test branch hide it:\n\n  ```bash\n  sha=$(git -C <dir> log --format=%H --grep='^test: upgrade vite-plus to prerelease' -n 1)\n  git -C <dir> show \"$sha\" | grep -E '^-.*vite-plus'\n  ```\n\n  Parse every YAML document when auditing pnpm lockfiles. Importers and package resolutions can be in separate documents; a single-document parser can miss the resolved versions.\n\n  Report that subset separately; \"2 of the 7 forks on the previous release pass, the other 5 fail on fork infrastructure\" is a far stronger statement than a headline pass rate over the whole catalog.\n\n- **Project-side and infra failures.** Dependency conflicts between the project's own packages, missing fork secrets, third-party GitHub Apps not installed on the fork, network timeouts. Retry once before classifying anything as a network failure; they pass on retry. Two recurring shapes worth naming: a package that imports a dependency it never declared and only ever resolved through hoisting (`Cannot find package 'oxfmt'`) breaks as soon as the harness regenerates the lockfile; and a project whose own dependency has no `main`/`module`/`exports` cannot load its config under any vite-plus version.\n- **Dependency drift during migration.** Regenerating a lockfile can move unrelated floating or nightly dependencies to incompatible versions. Compare with the base lockfile before blaming the candidate. On the test branch, retain the original versions and their dependency graph, then verify a frozen install and rerun the failing command.\n- **Custom quality checks.** Check that project wrappers still load their plugins and recognize migrated test imports. Preserve existing lint diagnostic coverage when repairing migration issues; a smaller baseline can mean that checks stopped running.\n- **Release-age gates on fresh dependencies.** A project's `minimumReleaseAge` can reject packages that Vite+ itself pins and that were published shortly before the release. That is the project's policy, not a vite-plus bug: add the package to `minimumReleaseAgeExclude` on the test branch, and do not propose extending migrate's exemption list. Real installs of the release hit the same gate until those packages age past the project's window.\n- **Harness artifacts.** Failures your own test setup caused, such as a lockfile the harness deleted and the install never regenerated. Fix these and re-run rather than reporting them.\n\nReport the tally by cause, not just pass/fail, and state plainly which failures you controlled for and which you classified from the error text alone. Only a failure that reproduces on the candidate but not on the previous release is a regression.\n\nBefore filing an upstream issue for a finding, search every repository that could own it, including closed issues and open PRs. For a type-aware oxlint diagnostic that means both `oxc-project/oxc` and `oxc-project/tsgolint`.\n\nWhen repairing timing-sensitive smoke tests, keep their assertions and make readiness or timing deterministic. Use a negative control when changing how a test observes behavior: temporarily remove or break that behavior, confirm the test fails, and restore it before committing.\n\nTwo long-run mechanics worth knowing: `vp migrate` installs Vite+ git hooks in the project, so any later `git commit`/`git push` there needs `--no-verify`; and macOS has no GNU `timeout`, so a driver script that time-boxes runs needs its own watchdog. If that driver runs projects in parallel, kill the whole process tree on timeout, not just the wrapper: an orphaned `pnpm install` holds the store lock and the next project then hangs at 0% CPU with no output, which reads like a vite-plus hang and is not one. Concurrent installs that share one store can stall the same way without any orphan, with one process idle while holding the store's `index.db`; rerun the stuck project alone before treating it as a hang.\n\nTwo fork-CI blockers are worth fixing rather than reporting, both on the **test branch only** so the tracked branch stays clean against upstream. A fork whose workflows never trigger on `pull_request` reports \"no checks\" and proves nothing: add a minimal workflow that runs `vp run build` through whatever setup the project already uses. A fork whose workflows target third-party runners (self-hosted labels such as `blacksmith-*`) queues every job forever, because those labels only resolve for the upstream org: map them to GitHub-hosted equivalents, replacing the longest label first so an `-arm` suffix is not left half-rewritten. Runner-specific _actions_ need more than a label swap and are usually not worth fixing. The same applies to `depot-*` and `namespace-profile-*` labels. GitHub-hosted runners are smaller than most of these, so heavy suites can start timing out after the swap; classify those timeouts as fork infrastructure.\n\nClosing a cycle's PRs with `--delete-branch` removes their test branches, but each closed PR's commits stay reachable at `refs/pull/<N>/head`. Find the previous cycle's PR per repository (`gh pr list --repo vite-plus-ecosystem-ci/<repo> --state all`); an org-wide `gh search` stops at 100 results and silently misses forks. Before a new sweep, list the non-upgrade commits on the previous cycle's PRs (supply-chain exemptions, runner-label maps, added workflows), fetch them, and cherry-pick them onto the new test branches. Re-apply by hand when upstream drift makes a cherry-pick conflict. Commits made after the upgrade commit (formatting of migrate-written files, regenerated lockfiles, restored dependencies) only apply after migrating: retry them after the new upgrade commit, take the new lockfile when only lockfiles conflict, and drop a formatting commit that would add migrate-generated `package.json` or workspace keys before migrate runs.\n\n## 5. Release-branch CI\n\nMatch checks to the current PR head and the latest applicable workflow runs. Superseded canceled runs can leave failed aggregate checks in the PR rollup. Check required statuses with `gh pr checks <PR#> --required`, and report required reviewer approval separately from technical CI readiness.\n\nFixes for CI failures go through a **separate PR to `main`**, never as commits on the release branch (the release-version syncs in step 2 are the exceptions). After the fix PR merges:\n\n```bash\ngit checkout release/vX.Y.Z && git merge origin/main --no-edit && git push origin release/vX.Y.Z\n```\n\nDo not assume the merge brought in only the fix PR: `main` may have accumulated several. Before merging, list everything that will come in with `git log origin/release/vX.Y.Z..origin/main --oneline`, then add a changelog entry for **every** newly included PR and rerun the step 3 validation (its missing/extra diff against `generate-notes` catches any entry you missed). When the merged commits change bundled tool versions or `vp migrate` behavior, rebuild the preview (step 4) and repeat the smoke test on the new build: start each fork from its current test branch so earlier fixes carry over, open new fork PRs, close the superseded ones, and report results per build.\n\nKnown release-branch-only failure modes:\n\n- **Binding version drift**: CI's no-unexpected-changes guard reports a diff flipping version strings in `binding/index.cjs`. Fix: step 2.\n- **Registry flakes**: registry-bound fixtures can time out (about 50s) and look like regressions. Rerun before diagnosing, and never commit a `[timeout]` snapshot.\n- **New RustSec advisories**: Security Analysis runs cargo-deny only when `Cargo.lock` changes, and the version bump always changes it. An advisory published since `main` last touched `Cargo.lock` therefore fails first on the release PR. The check is not required; trace which dependency pulls the crate in and let the release manager decide whether to fix it before or after the release. Before opening a fix PR, check open dependency-update PRs (Renovate): one may already bump the crate and clear the check.\n\n## 6. Merge\n\nMerging the release PR is the release trigger. Before merging confirm: CI green, changelog validated, binding and documentation versions synced (including all three prompts), and (if used) the preview build verified.\n\nWhen the release publishes an npm package for the first time, confirm before merging that the package's npm settings list a trusted publisher for `voidzero-dev/vite-plus`, workflow `release.yml`, environment `release`. A missing or mismatched entry fails that publish with `ERR_PNPM_AUTH_TOKEN_EXCHANGE ... (status code 404)` after `vite-plus` is already on `latest`, and the GitHub release, Docker image, docs deployment, and Discord notification are skipped until the job is rerun.\n\nAuto-merge being enabled is not a completed merge. Confirm `mergedAt` and the merge commit, then follow the Release run for that commit; older successful runs can have skipped publishing because the version did not change.\n\n## 7. Automated release pipeline (what happens after merge)\n\n`release.yml` runs on the `main` push because `packages/cli/package.json` changed:\n\n1. `check`: compares the local version against `unpkg.com/vite-plus@latest`; everything below is skipped unless it changed.\n2. `build-rust`: full multi-platform build.\n3. `request-approval`: posts an approval request to the releases Discord channel, and the `Release` job waits on the `release` GitHub environment. **A person with environment approval rights must approve the run in the Actions UI.** The environment sets `prevent_self_review: true`, so whoever merged the release PR triggered the run and cannot approve it: a _different_ reviewer must. Check who can, and tell the release manager rather than leaving them waiting on themselves:\n\n   ```bash\n   gh api repos/voidzero-dev/vite-plus/actions/runs/<run-id>/pending_deployments \\\n     -q '.[] | \"\\(.environment.name) can_approve=\\(.current_user_can_approve) reviewers=\\([.reviewers[]?.reviewer.login] | join(\",\"))\"'\n   ```\n\n4. `Release`: publishes the NAPI bindings (`@voidzero-dev/vite-plus-<platform>`) and standalone CLI packages (`@voidzero-dev/vite-plus-cli-<platform>`, via `packages/cli/publish-native-addons.ts`), then `@voidzero-dev/vite-plus-core`, `vite-plus`, and `create-vite-plus` to npm (`--tag latest`). Each dependency tier waits for npm propagation before publication advances. It then creates the `vX.Y.Z` GitHub release (draft, with installer/binary assets, then undrafted). The generated body has only Published Packages and Installation sections.\n5. `publish-docker`: multi-arch toolchain image to `ghcr.io/voidzero-dev/vite-plus`, after npm publish (the image installs vp from npm).\n6. `deploy-docs`: deploys the production docs after a stable release is published. It is skipped for prereleases. When a prerelease is published to `latest` and its notes or CLI messages link to docs that production does not serve yet, ask the release manager whether to run `gh workflow run deploy-docs.yml --ref <ref>` once the `Release` job is publishing. Use the `vX.Y.Z` tag, or `main` while it still points at the release commit, and confirm the run's head SHA. The `vp` version that builds the docs does not need to match the release: the site and its install scripts come from the checked-out commit. The job authenticates with the `VOID_TOKEN` repository secret. When that token has expired, the job fails with `` `VOID_TOKEN` is invalid or expired `` and `discord-notify` is skipped. Ask someone with access to rotate the secret, then run `gh run rerun <run-id> --failed` to rerun the docs deployment and the Discord notification.\n7. `discord-notify`: announces to Discord after Docker publishing and docs deployment succeed (docs are skipped for prereleases).\n\n**When the `Release` job fails partway, fix the cause, then use \"Re-run failed jobs\", never \"Re-run all jobs\".** A full rerun repeats `check`, which now sees the new version on `unpkg.com/vite-plus@latest` and skips the whole release. A failed-jobs rerun reuses `check` and the build artifacts, and every npm publish step is idempotent: `.github/scripts/publish-npm-package.ts` skips versions the registry already serves (`<pkg>@X.Y.Z is already published; skipping upload.`), so only the unpublished packages upload. Confirm that from the rerun log. The rerun waits on the `release` environment again, and whoever started the rerun cannot approve it.\n\n**A successful publish command does not mean the packages are installable.** `pnpm publish` prints `✅ Published package <name>@X.Y.Z` as soon as the registry accepts the request, and the registry can then take tens of minutes to actually serve that version. This has shipped a broken release: `vite-plus@X.Y.Z` went live on `latest` with an exact dependency on `@voidzero-dev/vite-plus-core@X.Y.Z` that was invisible for about 35 minutes, so every `npm install vite-plus` failed with `ETARGET` and both `publish-docker` and `Deploy docs` failed on `ERR_PNPM_NO_MATCHING_VERSION`. The downstream job failures are the symptom, not the cause; do not re-run them until the registry has the package.\n\nCheck visibility directly, not through `npm view`, which caches:\n\n```bash\nfor pkg in '@voidzero-dev%2Fvite-plus-core' 'vite-plus'; do\n  curl -s -H 'Cache-Control: no-cache' \"https://registry.npmjs.org/$pkg?t=$(date +%s)\" |\n    python3 -c \"import json,sys;d=json.load(sys.stdin);print('$pkg', d['dist-tags'].get('latest'), 'X.Y.Z' in d['versions'])\"\ndone\n```\n\nBoth must report `True` before you trust the release. A stale `modified` timestamp on the packument is the giveaway that nothing landed. If `vite-plus` is visible and `core` is not, the release is broken **right now** for every new install: tell the release manager immediately and offer to move the tag back (`npm dist-tag add vite-plus@<prev> latest`) while the publish is sorted out. Confirm the fix with a real install in a temp directory, not just a registry read:\n\n```bash\nd=$(mktemp -d); cd \"$d\" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z --no-audit --no-fund\n```\n\nThe full package document can update before npm's separately cached installation metadata. The workflow's `.github/scripts/wait-for-npm-packages.ts` checks that installation metadata and the referenced tarball, then waits for settlement. Use that script to reproduce its availability check; local visibility does not prove the workflow runner sees the same state.\n\nLocal clients can also mislead in the other direction. npm and pnpm cache a lookup made before the version landed, so pass `--prefer-online` when a just-published version reports `ETARGET`/`notarget` locally. pnpm 11 and later default to a one-day `minimumReleaseAge`, so `pnpm create vite-plus` (an unpinned `pnpm dlx create-vite-plus`) resolves to the previous `create-vite-plus` until the new version is a day old; verify it with `--config.minimumReleaseAge=0`.\n\n## 8. Post-release\n\n1. **Polish the GitHub release notes** (ask first): the auto-created release body has only Published Packages and Installation. Build the polished notes from the final release PR body:\n   - Drop the closing `---` / `Merging this PR ...` boilerplate.\n   - Preserve every changelog section through **Full Changelog**, including any later revisions requested by the release manager.\n   - Append the generated Published Packages and Installation sections, omit the redundant `View the full commit` line, and end Installation with a Docker usage block (keep the explanation to one short sentence):\n\n     ````markdown\n     **Docker:**\n\n     ```bash\n     docker run --rm -it -v \"$PWD:/app\" -w /app ghcr.io/voidzero-dev/vite-plus:X.Y.Z vp build\n     ```\n\n     Run any `vp` command without installing it; see the [Docker guide](https://viteplus.dev/guide/docker) for more.\n     ````\n\n   - After Installation, add a **Create a New App** section with the pnpm example only:\n\n     ````markdown\n     ### Create a New App\n\n     Start a project without installing the global `vp`: `create-vite-plus` runs `vp create` through your package manager.\n\n     ```bash\n     pnpm create vite-plus vite:application --directory my-app\n     cd my-app && pnpm exec vp dev\n     ```\n\n     Run the command without arguments to pick a template interactively, and see the [create guide](https://viteplus.dev/guide/create#without-the-global-cli) for other package managers, templates, and options.\n     ````\n\n   - **Present the draft to the release manager and apply only after approval.** Before review, write the complete draft to a temporary Markdown file with the proposed release title at the top and the full body below it. Update that file after every requested revision; do not treat chat excerpts as the canonical draft. After approval, use a body-only notes file (without the review title) to retitle the release and apply the notes:\n\n     ```bash\n     gh release edit vX.Y.Z --repo voidzero-dev/vite-plus \\\n       --title \"vite-plus vX.Y.Z\" --notes-file /tmp/release-notes.md\n     ```\n\n   - Keep the review draft, body-only notes file, and live release aligned after requested edits. Read back the live title and body to verify the update. Normalize CRLF and LF before comparing the approved file with the live body, because GitHub can change line endings. Re-run the step 3 validation greps, plus `grep -c 'Merging this PR'` (must be 0).\n   - For a SemVer prerelease, confirm that GitHub marks the release as a prerelease. The automated release can create a prerelease tag without setting that flag. Add `--prerelease` when applying the approved notes, then verify `isPrerelease` with `gh release view vX.Y.Z --json isPrerelease`.\n\n2. **Verify**:\n\n   ```bash\n   npm view vite-plus version                       # X.Y.Z\n   npm view @voidzero-dev/vite-plus-core version    # X.Y.Z\n   npm view create-vite-plus version                # X.Y.Z\n   npm vi\n\n<!-- [TRUNCATED AT 50KB] -->","frontmatter":{"name":"release-manager","description":"Run the standard vite-plus release process end-to-end as the release manager. Covers preparing the release PR, syncing the NAPI binding version, writing the categorized changelog, smoke-testing via a preview build, handling release-branch CI failures, merging, and post-release verification and announcements. Use when asked to cut, prepare, or manage a vite-plus release (e.g. \"release v0.2.3\", \"act as release manager\").","allowed-tools":"Bash, Read, Edit, Write, Grep, Glob, WebFetch","allowedTools":["Bash,","Read,","Edit,","Write,","Grep,","Glob,","WebFetch"]},"allowedTools":["Bash,","Read,","Edit,","Write,","Grep,","Glob,","WebFetch"],"isInternal":false,"tokens":11567,"sizeBytes":50046},{"name":"SKILL.md","path":".claude/skills/spawn-process/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/spawn-process/SKILL.md","title":"spawn-process","category":"anthropic-skill","format":"markdown","content":"---\nname: spawn-process\ndescription: Guide for writing subprocess execution code using the vp_command crate\nallowed-tools: Read, Grep, Glob, Edit, Write, Bash\n---\n\n# Add Subprocess Execution Code\n\nWhen writing Rust code that needs to spawn subprocesses (resolve binaries, build commands, execute programs), always use the `vp_command` crate. Never use `which`, `tokio::process::Command::new`, or `std::process::Command::new` directly.\n\n## Available APIs\n\n### `vp_command::resolve_bin(name, path_env, cwd)` — Resolve a binary name to an absolute path\n\nHandles PATHEXT (`.cmd`/`.bat`) on Windows. Pass `None` for `path_env` to search the current process PATH.\n\n```rust\n// Resolve using current PATH\nlet bin = vp_command::resolve_bin(\"node\", None, &cwd)?;\n\n// Resolve using a custom PATH\nlet custom_path = std::ffi::OsString::from(&path_env_str);\nlet bin = vp_command::resolve_bin(\"eslint\", Some(&custom_path), &cwd)?;\n```\n\n### `vp_command::build_command(bin_path, cwd)` — Build a command for a pre-resolved binary\n\nReturns `tokio::process::Command` with cwd, inherited stdio, and `fix_stdio_streams` on Unix already configured. Add args, envs, or override stdio as needed.\n\n```rust\nlet bin = vp_command::resolve_bin(\"eslint\", None, &cwd)?;\nlet mut cmd = vp_command::build_command(&bin, &cwd);\ncmd.args(&[\".\", \"--fix\"]);\ncmd.env(\"NODE_ENV\", \"production\");\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::build_shell_command(shell_cmd, cwd)` — Build a shell command\n\nUses `/bin/sh -c` on Unix, `cmd.exe /C` on Windows. Same stdio and `fix_stdio_streams` setup as `build_command`.\n\n```rust\nlet mut cmd = vp_command::build_shell_command(\"echo hello && ls\", &cwd);\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::run_command(bin_name, args, envs, cwd)` — Resolve + build + run in one call\n\nCombines resolve_bin, build_command, and status().await. The `envs` HashMap must include `\"PATH\"` if you want custom PATH resolution.\n\n```rust\nlet envs = HashMap::from([(\"PATH\".to_string(), path_value)]);\nlet status = vp_command::run_command(\"node\", &[\"--version\"], &envs, &cwd).await?;\n```\n\n## Dependency Setup\n\nAdd `vp_command` to the crate's `Cargo.toml`:\n\n```toml\n[dependencies]\nvp_command = { workspace = true }\n```\n\nDo NOT add `which` as a direct dependency — binary resolution goes through `vp_command::resolve_bin`.\n\n## Exception\n\n`crates/vp_global_cli/src/shim/exec.rs` uses synchronous `std::process::Command` with Unix `exec()` for process replacement. This is the only place that bypasses `vp_command`.\n","frontmatter":{"name":"spawn-process","description":"Guide for writing subprocess execution code using the vp_command crate","allowed-tools":"Read, Grep, Glob, Edit, Write, Bash","allowedTools":["Read,","Grep,","Glob,","Edit,","Write,","Bash"]},"allowedTools":["Read,","Grep,","Glob,","Edit,","Write,","Bash"],"isInternal":false,"tokens":661,"sizeBytes":2584},{"name":"SKILL.md","path":".claude/skills/sync-tsdown-cli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/sync-tsdown-cli/SKILL.md","title":"sync-tsdown-cli","category":"anthropic-skill","format":"markdown","content":"---\nname: sync-tsdown-cli\ndescription: Sync tsdown runtime CLI options with vp pack after a tsdown upgrade. Use for option forwarding changes; use sync-upstream-cli-help for static help wording.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync tsdown CLI\n\nRuntime options live in `packages/cli/src/pack-bin.ts`; static help lives in\n`packages/cli/src/help.ts`.\n\n1. Run `npx tsdown --help` from `packages/cli/` and compare it with `pack-bin.ts`.\n2. Add new forwarded options using the existing cac `.option()` pattern. For removed\n   options, add `// NOTE: removed from tsdown CLI in vX.Y.Z` for reviewer follow-up.\n3. Preserve runtime differences: `-c, --config` stays disabled because Vite+ uses\n   `vite.config.ts`, and `--env-prefix` keeps the `['VITE_PACK_', 'TSDOWN_']` default.\n4. For static labels and descriptions, follow `sync-upstream-cli-help`; do not adapt\n   upstream wording to explain runtime differences.\n5. Run `pnpm --filter vite-plus build-ts` and `vp pack -h`. Add a focused PTY snapshot\n   case under `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/` when a new runtime\n   option is exposed.\n","frontmatter":{"name":"sync-tsdown-cli","description":"Sync tsdown runtime CLI options with vp pack after a tsdown upgrade. Use for option forwarding changes; use sync-upstream-cli-help for static help wording.","allowed-tools":"Read, Grep, Glob, Edit, Bash","allowedTools":["Read,","Grep,","Glob,","Edit,","Bash"]},"allowedTools":["Read,","Grep,","Glob,","Edit,","Bash"],"isInternal":false,"tokens":288,"sizeBytes":1126},{"name":"SKILL.md","path":".claude/skills/sync-upstream-cli-help/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/sync-upstream-cli-help/SKILL.md","title":"sync-upstream-cli-help","category":"anthropic-skill","format":"markdown","content":"---\nname: sync-upstream-cli-help\ndescription: Sync Vite+'s static CLI help with Vite, Vitest, Oxlint, Oxfmt, and tsdown while preserving intentional omissions. Use when an upstream dependency upgrade changes CLI help.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream CLI help\n\n## Input and target\n\n- Read the diff from `$CLI_HELP_DIFF_REPORT`; first run\n  `test -r \"$CLI_HELP_DIFF_REPORT\"`. Act only when `$CLI_HELP_DIFF_CHANGED` is\n  `true`.\n- Treat the upgraded tool's `--help` output as the source of truth. The report locates\n  changes and versions; rerun the exact version when its diff is truncated.\n- Edit `commandHelpDocs` in `packages/cli/src/help.ts`.\n- Do not edit `packages/cli/src/utils/help.ts` for content drift. It owns terminal\n  wrapping, alignment, and the right margin.\n\n| Upstream help         | Document entry |\n| --------------------- | -------------- |\n| `vite --help`         | `dev`          |\n| `vite build --help`   | `build`        |\n| `vite preview --help` | `preview`      |\n| `vitest --help`       | `test`         |\n| `oxlint --help`       | `lint`         |\n| `oxfmt --help`        | `fmt`          |\n| `tsdown --help`       | `pack`         |\n\n## Change\n\n- For items Vite+ exposes, copy upstream labels, descriptions, section titles, and\n  section guidance exactly. Preserve intentional lines and lists, but not terminal\n  padding, automatic wrapping, ANSI color, or version banners.\n- Remove an upstream item only after confirming Vite+ no longer supports or\n  deliberately retains it.\n\n## Do not change\n\n- Keep Vite+-owned usage, summaries, examples, and documentation URLs.\n- Keep config selectors/loaders hidden, including `--config`, `--configLoader`, and\n  `--disable-nested-config`.\n- Do not add standalone modes such as `--init`, `--migrate`, or `--lsp`, top-level\n  `--version`, or options Vite+ does not forward.\n- Do not change runtime forwarding or rewrite upstream wording for a Vite+-specific\n  runtime default. Use `sync-tsdown-cli` for tsdown runtime changes.\n\nRe-record the affected CLI help snapshots and inspect their diffs. Do not modify help\ndocuments when the report contains no actionable exposed change.\n","frontmatter":{"name":"sync-upstream-cli-help","description":"Sync Vite+'s static CLI help with Vite, Vitest, Oxlint, Oxfmt, and tsdown while preserving intentional omissions. Use when an upstream dependency upgrade changes CLI help.","allowed-tools":"Read, Grep, Glob, Edit, Bash","allowedTools":["Read,","Grep,","Glob,","Edit,","Bash"]},"allowedTools":["Read,","Grep,","Glob,","Edit,","Bash"],"isInternal":false,"tokens":538,"sizeBytes":2179},{"name":"SKILL.md","path":".claude/skills/sync-upstream-dependency-docs/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/sync-upstream-dependency-docs/SKILL.md","title":"sync-upstream-dependency-docs","category":"anthropic-skill","format":"markdown","content":"---\nname: sync-upstream-dependency-docs\ndescription: Sync exact upstream dependency versions embedded in Vite+ documentation after the automated dependency upgrade. Use when bundled Vite, Vitest, Oxlint, Oxfmt, tsdown, Rolldown, or related package versions change.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream dependency docs\n\n1. Read `$UPGRADE_DEPS_META_DIR/versions.json` and consider only entries whose `old`\n   and `new` values differ.\n2. Search `README.md`, `packages/*/README.md`, and `docs/**/*.md` for each changed\n   package name and its old exact version. Do not treat changelogs, RFC examples,\n   snapshots, or broad ranges such as `vitest@4.x` as current-version references.\n3. Update references that promise to match Vite+'s currently bundled version.\n   Manual-installation examples use placeholders for the `vite-plus` and Vitest\n   versions, so they do not need updates.\n4. Preserve the surrounding wording and formatting. Do not rewrite examples whose\n   version is intentionally historical or illustrative.\n5. Re-run the searches for the changed packages and inspect the focused diff. No\n   stale exact version may remain in documentation that describes the current bundle.\n","frontmatter":{"name":"sync-upstream-dependency-docs","description":"Sync exact upstream dependency versions embedded in Vite+ documentation after the automated dependency upgrade. Use when bundled Vite, Vitest, Oxlint, Oxfmt, tsdown, Rolldown, or related package versions change.","allowed-tools":"Read, Grep, Glob, Edit, Bash","allowedTools":["Read,","Grep,","Glob,","Edit,","Bash"]},"allowedTools":["Read,","Grep,","Glob,","Edit,","Bash"],"isInternal":false,"tokens":272,"sizeBytes":1212},{"name":"SKILL.md","path":".claude/skills/test-pkg-pr-new-migrate/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/test-pkg-pr-new-migrate/SKILL.md","title":"test-pkg-pr-new-migrate","category":"anthropic-skill","format":"markdown","content":"---\nname: test-pkg-pr-new-migrate\ndescription: Verify a preview (registry bridge) build of vite-plus against a real project before release — run `vp migrate` from the preview commit against a local project, deps resolved through the registry bridge. Use when asked to verify/e2e-test a preview or pkg-pr-new build against a project, \"test PR #<N> on <project>\", or check a prerelease against a repo.\nallowed-tools: Bash, Read\n---\n\n# Verify a preview build against one project\n\nInstalls an isolated global `vp` built from a registry bridge commit build and runs `vp migrate` on a specified local project. The global CLI and the migrated project both pin `vite-plus`/`vite` to the clearly-defined `0.0.0-commit.<sha>` build. `vp migrate` itself writes the bridge registry into the project's `.npmrc` (or `.yarnrc.yml` for Yarn Berry) so the deps resolve, during this run and in the project's own CI; this script only force-stages that file past `.gitignore`.\n\nRequired inputs: a `<PR-or-SHA>` (the build to verify) and a `<project-path>`. If either is missing from the request, **ask the user** for it — never guess the PR/SHA, as the build under test is the user's choice.\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh <PR-or-SHA> <project-path> [migrate-options...]\n# e.g.\n.github/scripts/test-pkg-pr-new-migrate.sh 1891 /path/to/npmx.dev --no-interactive\n```\n\n- First arg is a PR number or commit SHA; the script resolves the immutable commit via the bridge `x-commit-key` header and verifies the bridge serves it (the preview publish workflow, triggered by the `preview-build` label, registers each commit).\n- Never touches `~/.vite-plus`; installs the project's committed dependencies before migrating, because `vp migrate` reads the original Vitest version from the lockfile or `node_modules`; refuses a dirty worktree unless `ALLOW_DIRTY=1`; prints the project's `git status`/`diff` at the end — inspect that to confirm the migration result.\n\n**The build under test must include the \"migrate writes the bridge registry\" feature** (this session's work / current branch head onward). The harness no longer writes the registry itself — it relies on `vp migrate` doing it. Testing an older build with this harness would leave the project with no bridge registry, so its deps resolve from npmjs (`ERR_PNPM_NO_MATCHING_VERSION` on the `0.0.0-commit.<sha>` version). Always verify a fresh build of the branch, not a stale published commit.\n\nThe script prints the resolved versions at the end, querying one package at a time (npm/yarn/bun `why` only accept a single package; `-r` is pnpm-only). Each must resolve to exactly ONE version: `vite-plus` and `vite` at the expected `0.0.0-commit.<sha>` (vite via the `@voidzero-dev/vite-plus-core` alias), `vitest` at the bundled upstream version. Multiple versions, or a stale/wrong version, means the migration or install is broken. To re-check by hand:\n\n```bash\ncd <project-path>\n# pnpm: one call, recursive across workspaces\nvp why -r vite-plus vite vitest\n# npm / yarn / bun: one package per call, no -r\nvp why vite-plus && vp why vite && vp why vitest\n```\n","frontmatter":{"name":"test-pkg-pr-new-migrate","description":"Verify a preview (registry bridge) build of vite-plus against a real project before release — run `vp migrate` from the preview commit against a local project, deps resolved through the registry bridge. Use when asked to verify/e2e-test a preview or pkg-pr-new build against a project, \"test PR","allowed-tools":"Bash, Read","allowedTools":["Bash,","Read"]},"allowedTools":["Bash,","Read"],"isInternal":false,"tokens":766,"sizeBytes":3117},{"name":"SKILL.md","path":".claude/skills/verify-interactive-cli/SKILL.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/skills/verify-interactive-cli/SKILL.md","title":"verify-interactive-cli","category":"anthropic-skill","format":"markdown","content":"---\nname: verify-interactive-cli\ndescription: Drive and capture vp's interactive (clack) prompts in a tmux session to manually verify interactive UX and catch spinner-over-prompt bugs. Use when asked to test/verify/capture an interactive vp command's prompts (vp migrate, vp create, ...), reproduce a prompt-rendering bug, or show the real interactive CLI output for a PR.\nallowed-tools: Bash, Read\n---\n\n# Verify vp's interactive CLI prompts\n\nThe PTY snapshot suite covers scripted prompt flows. This skill complements it by driving an installed CLI in tmux, capturing clean output, and checking a prompt over several seconds for a spinner animating underneath it (a real, recurring UX bug class, e.g. the \"Preparing migration\" and \"Checking Node.js version support\" spinners).\n\n## Prerequisites\n\n- `tmux` (macOS has none by default): `brew install tmux`.\n- The global `vp` must contain the code under test. To exercise working-tree changes, rebuild first: `pnpm bootstrap-cli` (~5 min), then confirm with `vp --version`.\n\n## Driver\n\n`interactive-cli-tmux-driver.sh` is bundled in this skill's directory.\n\n```bash\n# Run to completion, auto-accepting every prompt's DEFAULT; prints a clean transcript.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\"\n\n# STOP at a specific prompt (do NOT answer it) and check for a spinner animating under it.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\" \"Upgrade Node.js\"\n```\n\nHow it works:\n\n- Runs the command in a detached tmux session; `tmux capture-pane -p` yields clean text (clack's in-place redraws overwrite, so only resolved lines remain), far cleaner than `expect`'s raw ANSI capture.\n- Auto-accepts each prompt's default by sending Enter when the pane goes STABLE. A waiting prompt is static; animating spinners keep the pane changing, so Enter never fires mid-work.\n- End-detection uses `; echo \"$M1$M2 exit=$?\"` where M1/M2 are split vars, so the literal end marker is not in the typed command line (otherwise a grep matches the echoed command, not the program output).\n- With a STOP_AT regex it halts at the target prompt and captures twice ~3s apart: identical captures = static prompt (OK); differing captures (or a `Checking ... (Xs)` line) = a spinner is animating under the prompt = a UX bug.\n\n## Setting up a target project\n\nCreate a throwaway project that triggers the prompts you want to see, e.g. a fresh Vite app whose `.node-version` is below the supported range to exercise the Node-upgrade confirm:\n\n```bash\nmkdir -p /tmp/vp-demo && cd /tmp/vp-demo\nprintf '{\\n  \"name\":\"d\",\"private\":true,\"type\":\"module\",\"packageManager\":\"pnpm@10.18.0\",\\n  \"scripts\":{\"build\":\"vite build\",\"test\":\"vitest run\"},\\n  \"devDependencies\":{\"vite\":\"^8.0.0\",\"vitest\":\"^4.1.0\"}\\n}\\n' > package.json\necho \"24.3.0\" > .node-version\nprintf 'import { defineConfig } from \"vite\";\\nexport default defineConfig({});\\n' > vite.config.ts\ngit init -q && git add -A && git -c user.email=x@x -c user.name=x commit -qm init\n```\n\n## When you find a spinner-over-prompt bug\n\nFix it test-first: the prompt's code must pause the migration progress spinner before the confirm renders. Assert the call order is `['pause', 'confirm']`. Reference fix: `upgradeUnsupportedNodeVersions` (in `packages/cli/src/migration/migrator/setup.ts`) takes a `pauseProgress` callback that `bin.ts` wires to `clearMigrationProgress`, called right before `prompts.confirm`.\n\n## Gotchas\n\n- clack's active-prompt marker here is `›` (not always `◆`); detect a waiting prompt by pane stability, not a specific glyph.\n- `VP_SKIP_INSTALL=1` skips the dependency install but breaks steps that load `vite.config.ts` (e.g. the prettier auto-migration) because `vite` isn't installed; use it only when you stop before the install step.\n- A `&` inside a background task detaches the script, so the task reports \"completed\" early while it keeps running; poll a status file or `capture-pane` to track real progress.\n","frontmatter":{"name":"verify-interactive-cli","description":"Drive and capture vp's interactive (clack) prompts in a tmux session to manually verify interactive UX and catch spinner-over-prompt bugs. Use when asked to test/verify/capture an interactive vp command's prompts (vp migrate, vp create, ...), reproduce a prompt-rendering bug, or show the real interactive CLI output for a PR.","allowed-tools":"Bash, Read","allowedTools":["Bash,","Read"]},"allowedTools":["Bash,","Read"],"isInternal":false,"tokens":1003,"sizeBytes":4000},{"name":"AGENTS.md","path":"packages/cli/AGENTS.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/cli/AGENTS.md","title":"Cli Skill","category":"anthropic-skill","format":"markdown","content":"<!--VITE PLUS START-->\n\n# Using Vite+, the Unified Toolchain for the Web\n\nThis project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, and it invokes Vite through `vp dev` and `vp build`. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.\n\nDocs are local at `node_modules/vite-plus/docs` or online at https://viteplus.dev/guide/.\n\n## Built-in Commands vs Scripts\n\n`vp <name>` runs a built-in command. `vp run <name>` runs a `package.json` script or a `vite.config.ts` task. Scripts cannot overwrite built-ins, so `vp dev` and `vp run dev` may do different things. Check `package.json` and `vite.config.ts` first, and run `vp run <name>` when the project defines a script or task with that name.\n\n## Running Scripts\n\nRun TypeScript and JavaScript files with `vpx <file>`, for example `vpx ./scripts/seed.ts --dry-run`. It handles enums, decorators, JSX, and tsconfig `paths`, so do not add `tsx` or `ts-node` to run scripts. `vpx --watch <file>` reruns the file when a file it uses changes, in place of `tsx watch`. `vp node <file>` runs plain Node.js.\n\n## Tool Versions\n\nRun `vp toolchain` to show versions and relationships in the active Vite+\nrelease. Add a tool name to select part of the graph. For example, run\n`vp toolchain vite`. Use `--global` to ignore the local `vite-plus` package. Use\n`vp why <package>` to show the package-manager dependency graph.\n\n## Review Checklist\n\n- [ ] Run `vp install` after pulling remote changes and before getting started.\n- [ ] Run `vp check` and `vp test` to format, lint, type check and test changes.\n- [ ] Check if there are `vite.config.ts` tasks or `package.json` scripts necessary for validation, run via `vp run <script>`.\n- [ ] If setup, runtime, or package-manager behavior looks wrong, run `vp env doctor` and include its output when asking for help.\n\n<!--VITE PLUS END-->\n","isInternal":false,"tokens":543,"sizeBytes":2088},{"name":"cargo-workspace-merger.md","path":".claude/agents/cargo-workspace-merger.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/agents/cargo-workspace-merger.md","title":"cargo-workspace-merger","category":"subagent-persona","format":"markdown","content":"---\nname: cargo-workspace-merger\ndescription: \"Use this agent when you need to merge one Cargo workspace into another, specifically when integrating a subproject's crates and dependencies into a root workspace. This includes tasks like: adding crate path references to workspace members, merging workspace dependency definitions while avoiding duplicates, and ensuring only production dependencies (not unnecessary dev dependencies) are included.\\\\n\\\\n<example>\\\\nContext: The user wants to integrate the rolldown project into their existing Cargo workspace.\\\\nuser: \\\"I need to merge the rolldown Cargo workspace into our root workspace\\\"\\\\nassistant: \\\"I'll use the cargo-workspace-merger agent to handle this integration. This involves analyzing both Cargo.toml files, identifying the crates to add, and merging the necessary dependencies.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\\\\n\\\\n<example>\\\\nContext: The user has cloned a Rust project as a subdirectory and wants to integrate it.\\\\nuser: \\\"Can you add all the crates from ./external-lib into our workspace?\\\"\\\\nassistant: \\\"I'll launch the cargo-workspace-merger agent to analyze the external library's workspace structure and merge it into your root Cargo.toml.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\"\nmodel: opus\ncolor: yellow\n---\n\nYou are an expert Rust build system engineer specializing in Cargo workspace management and dependency resolution. You have deep knowledge of Cargo.toml structure, workspace inheritance, and dependency deduplication strategies.\n\n## Your Primary Mission\n\nMerge a child Cargo workspace (located in a subdirectory) into a parent root Cargo workspace. This involves two main tasks:\n\n1. **Adding crate references**: Add all crates from the child workspace to the root workspace's `[workspace.dependencies]` section with proper path references.\n\n2. **Merging workspace dependencies**: Combine the child workspace's `[workspace.dependencies]` with the root's dependencies, ensuring no duplicates and only including dependencies actually used by the crates being merged.\n\n## Step-by-Step Process\n\n### Step 1: Analyze the Child Workspace\n\n- Read the child workspace's `Cargo.toml` (e.g., `./rolldown/Cargo.toml`)\n- Identify all workspace members from the `[workspace.members]` section\n- Extract all `[workspace.dependencies]` definitions\n\n### Step 2: Identify Crates to Add\n\n- For each workspace member, locate its `Cargo.toml`\n- Extract the crate name from `[package].name`\n- Build a list of path references in the format: `crate_name = { path = \"./child/crates/crate_name\" }`\n\n### Step 3: Analyze Dependency Usage\n\n- For each crate in the child workspace, read its `Cargo.toml`\n- Collect all dependencies from `[dependencies]`, `[dev-dependencies]`, and `[build-dependencies]`\n- Focus on dependencies that reference `workspace = true` - these need the workspace-level definition\n- Create a set of actually-used workspace dependencies\n\n### Step 4: Filter and Merge Dependencies\n\n- From the child's `[workspace.dependencies]`, only include those that are actually used by the crates\n- Check for conflicts with existing root workspace dependencies:\n  - Same dependency, same version: Skip (already exists)\n  - Same dependency, different version: Flag for manual resolution and suggest keeping the newer version\n- Exclude dev-only dependencies that aren't needed for the merged crates\n\n### Step 5: Update Root Cargo.toml\n\n- Add all crate path references to `[workspace.dependencies]`\n- Add filtered workspace dependencies to `[workspace.dependencies]`\n- Maintain alphabetical ordering within sections for cleanliness\n- Preserve any existing comments and formatting\n\n## Output Format\n\nProvide:\n\n1. A summary of crates being added\n2. A summary of dependencies being merged\n3. Any conflicts or issues requiring manual attention\n4. The exact additions to make to the root `Cargo.toml`\n\n## Quality Checks\n\n- Verify all paths exist before adding references\n- Ensure no duplicate entries are created\n- Validate that merged dependencies don't break existing crates\n- After modifications, suggest running `cargo check --workspace` to verify the merge\n- Use highest compatible semver versions (if not pinned) and merge features in crates\n\n## Important Considerations\n\n- Use `vt_path` types for path operations as per project conventions\n- Dependencies with `path` references in the child workspace may need path adjustments\n- Feature flags on dependencies must be preserved\n- Optional dependencies must maintain their optional status\n- If a dependency exists in both workspaces with different features, merge the feature lists\n\n### Workspace Package Inheritance\n\nChild crates may inherit fields from `[workspace.package]` using `field.workspace = true`. Common inherited fields include:\n\n- `homepage`\n- `repository`\n- `license`\n- `edition`\n- `authors`\n- `rust-version`\n\n**Important**: If the child workspace's `[workspace.package]` defines fields that the root workspace does not, you must add those fields to the root workspace's `[workspace.package]` section. Otherwise, crates that inherit these fields will fail to build with errors like:\n\n```\nerror inheriting `homepage` from workspace root manifest's `workspace.package.homepage`\nCaused by: `workspace.package.homepage` was not defined\n```\n\n**Steps to handle this**:\n\n1. Read the child workspace's `[workspace.package]` section\n2. Compare with the root workspace's `[workspace.package]` section\n3. Add any missing fields to the root workspace (use the root project's own values, not the child's)\n\n## Error Handling\n\n- If a crate path doesn't exist, report it clearly and skip\n- If Cargo.toml parsing fails, provide the specific error\n- If version conflicts exist, list all conflicts before proceeding and ask for guidance\n\n### Crates with Compile-Time Environment Variables\n\nSome crates use `env!()` macros that require compile-time environment variables set via `.cargo/config.toml`. These crates often have `relative = true` paths that only work when building from their original workspace root.\n\n**Example**: `rolldown_workspace` uses `env!(\"WORKSPACE_DIR\")` which is set in `rolldown/.cargo/config.toml`.\n\n**How to handle**:\n\n1. Check child workspace's `.cargo/config.toml` for `[env]` section\n2. If crates use these env vars with `relative = true`, copy those env vars to root `.cargo/config.toml` with paths adjusted to point to the child workspace directory\n3. Example: If child has `WORKSPACE_DIR = { value = \"\", relative = true }`, root should have `WORKSPACE_DIR = { value = \"child-dir\", relative = true }`\n","frontmatter":{"name":"cargo-workspace-merger","description":"Use this agent when you need to merge one Cargo workspace into another, specifically when integrating a subproject's crates and dependencies into a root workspace. This includes tasks like: adding crate path references to workspace members, merging workspace dependency definitions while avoiding duplicates, and ensuring only production dependencies (not unnecessary dev dependencies) are included.\\n\\n<example>\\nContext: The user wants to integrate the rolldown project into their existing Cargo workspace.\\nuser: \"I need to merge the rolldown Cargo workspace into our root workspace\"\\nassistant: \"I'll use the cargo-workspace-merger agent to handle this integration. This involves analyzing both Cargo.toml files, identifying the crates to add, and merging the necessary dependencies.\"\\n<Task tool call to launch cargo-workspace-merger agent>\\n</example>\\n\\n<example>\\nContext: The user has cloned a Rust project as a subdirectory and wants to integrate it.\\nuser: \"Can you add all the crates from ./external-lib into our workspace?\"\\nassistant: \"I'll launch the cargo-workspace-merger agent to analyze the external library's workspace structure and merge it into your root Cargo.toml.\"\\n<Task tool call to launch cargo-workspace-merger agent>\\n</example>","model":"opus","color":"yellow"},"isInternal":false,"tokens":1405,"sizeBytes":6612},{"name":"monorepo-architect.md","path":".claude/agents/monorepo-architect.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/.claude/agents/monorepo-architect.md","title":"Subagent: monorepo-architect","category":"subagent-persona","format":"markdown","content":"---\nname: monorepo-architect\ndescription: Use this agent when you need architectural guidance for monorepo tooling, particularly for reviewing code organization, module boundaries, and ensuring proper separation of concerns in Rust/Node.js projects. This agent should be invoked after implementing new features or refactoring existing code to validate architectural decisions and placement of functionality.\\n\\nExamples:\\n- <example>\\n  Context: The user has just implemented a new caching mechanism for the monorepo task runner.\\n  user: \"I've added a new caching system to handle task outputs\"\\n  assistant: \"I'll use the monorepo-architect agent to review the architectural decisions and ensure the caching logic is properly placed within the module structure.\"\\n  <commentary>\\n  Since new functionality was added, use the monorepo-architect agent to review the code architecture and module boundaries.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is refactoring the task dependency resolution system.\\n  user: \"I've refactored how we resolve task dependencies across packages\"\\n  assistant: \"Let me invoke the monorepo-architect agent to review the refactored code and ensure proper separation of concerns.\"\\n  <commentary>\\n  After refactoring core functionality, use the monorepo-architect agent to validate architectural decisions.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is adding cross-package communication features.\\n  user: \"I've implemented a new IPC mechanism for packages to communicate during builds\"\\n  assistant: \"I'll use the monorepo-architect agent to review where this IPC logic lives and ensure it doesn't create inappropriate cross-module dependencies.\"\\n  <commentary>\\n  When adding features that span multiple modules, use the monorepo-architect agent to prevent architectural violations.\\n  </commentary>\\n</example>\nmodel: opus\ncolor: purple\n---\n\nYou are a senior software architect with deep expertise in Rust and Node.js ecosystems, specializing in monorepo tooling and build systems. You have extensively studied and analyzed the architectures of nx, Turborepo, Rush, and Lage, understanding their design decisions, trade-offs, and implementation patterns.\n\nYour primary responsibility is to review code architecture and ensure that functionality is properly organized within the codebase. You focus on:\n\n**Core Architectural Principles:**\n\n- Single Responsibility: Each module, file, and function should have one clear purpose\n- Separation of Concerns: Business logic, I/O operations, and configuration should be clearly separated\n- Module Boundaries: Enforce clean interfaces between modules, preventing tight coupling\n- Dependency Direction: Dependencies should flow in one direction, typically from high-level to low-level modules\n\n**When reviewing code, you will:**\n\n1. **Analyze Module Structure**: Examine where new functionality has been placed and determine if it belongs there based on the module's responsibility. Look for code that crosses logical boundaries or mixes concerns.\n\n2. **Identify Architectural Violations**:\n   - Cross-module responsibilities where one module is doing work that belongs to another\n   - Circular dependencies or bidirectional coupling\n   - Business logic mixed with I/O operations\n   - Configuration logic scattered across multiple modules\n   - Violation of the dependency inversion principle\n\n3. **Suggest Proper Placement**: When you identify misplaced functionality, provide specific recommendations:\n   - Identify the correct module/file where the code should reside\n   - Explain why the current placement violates architectural principles\n   - Suggest how to refactor without breaking existing functionality\n   - Consider the impact on testing and maintainability\n\n4. **Reference Industry Standards**: Draw from your knowledge of nx, Turborepo, Rush, and Lage to:\n   - Compare architectural decisions with proven patterns from these tools\n   - Highlight when a different approach might be more scalable or maintainable\n   - Suggest battle-tested patterns for common monorepo challenges\n\n5. **Focus on Rust/Node.js Best Practices**:\n   - In Rust: Ensure proper use of ownership, traits for abstraction, and module organization\n   - In Node.js: Validate CommonJS/ESM module patterns, async patterns, and package boundaries\n   - For interop: Review FFI boundaries and data serialization approaches\n\n**Review Methodology:**\n\n1. Start by understanding the intent of the recent changes\n2. Map out the affected modules and their responsibilities\n3. Identify any code that seems out of place or creates inappropriate coupling\n4. Provide a prioritized list of architectural concerns (critical, important, minor)\n5. For each concern, explain the principle being violated and suggest a concrete fix\n\n**Output Format:**\n\nStructure your review as:\n\n- **Summary**: Brief overview of architectural health\n- **Critical Issues**: Must-fix architectural violations that will cause problems\n- **Recommendations**: Suggested improvements with rationale\n- **Positive Patterns**: Acknowledge well-architected decisions\n- **Comparison Notes**: When relevant, note how similar problems are solved in nx/Turborepo/Rush/Lage\n\nYou are pragmatic and understand that perfect architecture must be balanced with delivery speed. Focus on issues that will genuinely impact maintainability, testability, or scalability. Avoid nitpicking and recognize when 'good enough' is appropriate for the current stage of the project.\n\nWhen you lack context about the broader system, ask clarifying questions rather than making assumptions. Your goal is to ensure the codebase remains maintainable and follows established architectural patterns while evolving to meet new requirements.\n","isInternal":false,"tokens":1156,"sizeBytes":5760},{"name":"README.md","path":"packages/cli/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/cli/README.md","title":"cli Documentation","category":"plugin-manifest","format":"markdown","content":"# VITE+(⚡︎) Local CLI\n\n**The Unified Toolchain for the Web**\n_runtime and package management, create, dev, check, test, build, pack, and monorepo task caching in a single dependency_\n\nThis package provides the project-local version of Vite+. The global `vp` command automatically delegates to this package for all project-specific tasks.\n\n---\n\nVite+ is the unified entry point for local web development. It combines [Vite](https://vite.dev/), [Vitest](https://vitest.dev/), [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [Rolldown](https://rolldown.rs/), [tsdown](https://tsdown.dev/), and [Vite Task](https://github.com/voidzero-dev/vite-task) into one zero-config toolchain that also manages runtime and package manager workflows:\n\n- **`vp env`:** Manage Node.js and package managers globally and per project\n- **`vp install`:** Install dependencies with automatic package manager detection\n- **`vp dev`:** Run Vite's fast native ESM dev server with instant HMR\n- **`vp check`:** Run formatting, linting, and type checks in one command\n- **`vp test`:** Run tests through bundled Vitest\n- **`vp build`:** Build applications for production with Vite + Rolldown\n- **`vp run`:** Run `package.json` scripts and monorepo tasks with caching and dependency-aware scheduling\n- **`vp pack`:** Build libraries for npm publishing or standalone app binaries\n- **`vp toolchain`:** Show the versions of Vite+, Vite, Rolldown, Oxc, and other tools\n- **`vp create` / `vp migrate`:** Scaffold new projects and migrate existing ones\n\nAll of this is configured from your project root and works across Vite's framework ecosystem.\nVite+ is fully open-source under the MIT license.\n\n## Getting Started\n\nInstall Vite+ globally as `vp`:\n\nFor Linux or macOS:\n\n```bash\ncurl -fsSL https://vite.plus | bash\n```\n\nFor Windows:\n\n```bash\nirm https://viteplus.dev/install.ps1 | iex\n```\n\n`vp` handles the full development lifecycle such as package management, development servers, linting, formatting, testing and building for production.\n\n## Configuring Vite+\n\nVite+ can be configured using a single `vite.config.ts` at the root of your project:\n\n```ts\nimport { defineConfig } from 'vite-plus';\n\nexport default defineConfig({\n  // Standard Vite configuration for dev/build/preview.\n  plugins: [],\n\n  // Vitest configuration.\n  test: {\n    include: ['src/**/*.test.ts'],\n  },\n\n  // Oxlint configuration.\n  lint: {\n    ignorePatterns: ['dist/**'],\n  },\n\n  // Oxfmt configuration.\n  fmt: {\n    semi: true,\n    singleQuote: true,\n  },\n\n  // Vite Task configuration.\n  run: {\n    tasks: {\n      'generate:icons': {\n        command: 'node scripts/generate-icons.js',\n        cache: {\n          env: ['ICON_THEME'],\n        },\n      },\n    },\n  },\n\n  // `vp staged` configuration.\n  staged: {\n    '*': 'vp check --fix',\n  },\n});\n```\n\nThis lets you keep the configuration for your development server, build, test, lint, format, task runner, and staged-file workflow in one place with type-safe config and shared defaults.\n\nUse `vp migrate` to migrate to Vite+. It merges tool-specific config files such as `.oxlintrc*`, `.oxfmtrc*`, and lint-staged config into `vite.config.ts`.\n\n### CLI Workflows (`vp help`)\n\n#### Start\n\n- **create** - Create a new project from a template\n- **migrate** - Migrate an existing project to Vite+\n- **config** - Configure hooks and agent integration\n- **hooks** - Manage the Git hook dispatcher\n- **staged** - Run linters on staged files\n- **install** (`i`) - Install dependencies\n- **env** - Manage Node.js and package managers\n\n#### Develop\n\n- **dev** - Run the development server\n- **check** - Run format, lint, and type checks\n- **lint** - Lint code\n- **fmt** - Format code\n- **test** - Run tests\n\n#### Execute\n\n- **run** - Run monorepo tasks\n- **exec** - Execute a command from local `node_modules/.bin`\n- **node** - Run a Node.js script with the resolved Vite+ environment; use `vpx <file>` for TypeScript\n- **dlx** - Execute a package binary without installing it as a dependency\n- **cache** - Manage the task cache\n\n#### Build\n\n- **build** - Build for production\n- **pack** - Build libraries\n- **preview** - Preview production build\n\n#### Manage Dependencies\n\nVite+ automatically wraps your package manager (pnpm, npm, Yarn, or Bun) based on `packageManager` and lockfiles:\n\n- **add** - Add packages to dependencies\n- **remove** (`rm`, `un`, `uninstall`) - Remove packages from dependencies\n- **update** (`up`) - Update packages to latest versions\n- **dedupe** - Deduplicate dependencies\n- **outdated** - Check outdated packages\n- **list** (`ls`) - List installed packages\n- **why** (`explain`) - Show why a package is installed\n- **info** (`view`, `show`) - View package metadata from the registry\n- **link** (`ln`) / **unlink** - Manage local package links\n- **rebuild** - Rebuild native modules\n- **pm** - Forward a command to the package manager\n\n#### Maintain\n\n- **toolchain** - Show Vite+ tool versions and their relationships\n- **upgrade** - Update `vp` itself to the latest version\n- **implode** - Remove `vp` and all related data\n\n### Scaffolding your first Vite+ project\n\nUse `vp create` to create a new project:\n\n```bash\nvp create\n```\n\nYou can run `vp create` inside of a project to add new apps or libraries to your project.\n\nOrganizations can expose a curated set of templates under their npm scope by\npublishing `@org/create` with a `createConfig.templates` manifest in its `package.json`.\nOnce published, `vp create @org` opens an interactive picker over those\ntemplates, and setting `create: { defaultTemplate: '@org' }` in\n`vite.config.ts` makes it the default for bare `vp create`. See the\n[Organization Templates guide](https://viteplus.dev/guide/create#organization-templates)\nfor the authoring workflow and\n[`create.defaultTemplate`](https://viteplus.dev/config/create) for the\nconfig reference.\n\n### Migrating an existing project\n\nYou can migrate an existing project to Vite+:\n\n```bash\nvp migrate\n```\n\n### GitHub Actions\n\nUse the official [`setup-vp`](https://github.com/voidzero-dev/setup-vp) action to install Vite+ in GitHub Actions:\n\n```yaml\n- uses: voidzero-dev/setup-vp@<setup-vp-version>\n  with:\n    node-version: '22'\n    cache: true\n```\n\nSet `<setup-vp-version>` to an exact version from the [`setup-vp` releases page](https://github.com/voidzero-dev/setup-vp/releases). You can use a commit SHA instead. Do not use the `v1` tag. The `v1` tag no longer receives updates.\n\nSee [Automatic Version Updates](https://viteplus.dev/guide/ci#automatic-version-updates) to configure Dependabot or Renovate.\n\n#### Manual Installation & Migration\n\nIf you are manually migrating a project to Vite+, install these dev dependencies first:\n\n```bash\nvp install -D vite-plus\n```\n\nAdd package-manager overrides so that other packages use the Vite+ versions. Alias `vite` to `@voidzero-dev/vite-plus-core`, setting `<vite-plus-version>` to the installed `vite-plus` version. Pin `vitest` to `<vitest-version>`, using the bundled version shown by `vp toolchain vitest`. The project and `vp test` then use the same Vitest copy. Without the pin, a dependency or workspace package can install a different Vitest version. The two versions can use separate mocks, `expect` functions, and runner states:\n\n```json\n\"overrides\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\nIf you are using `pnpm`, add this to your `pnpm-workspace.yaml`:\n\n```yaml\noverrides:\n  vite@*: npm:@voidzero-dev/vite-plus-core@<vite-plus-version> # Match the installed vite-plus version\n  vitest@*: <vitest-version> # Match the version from vp toolchain vitest\n```\n\nOr, if you are using Yarn:\n\n```json\n\"resolutions\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\n## Sponsors\n\nThanks to [namespace.so](https://namespace.so) for powering our CI/CD pipelines with fast, free macOS, Linux, and Windows runners.\n","isInternal":false,"tokens":2049,"sizeBytes":7973},{"name":"README.md","path":"packages/cli/src/migration/migrator/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/cli/src/migration/migrator/README.md","title":"migrator Documentation","category":"plugin-manifest","format":"markdown","content":"# `migrator/` — migration logic, split by category\n\nThe `vp migrate` implementation used to live in a single ~7,300-line\n`packages/cli/src/migration/migrator.ts`. It is now split into category\nmodules in this directory. `migration/migrator.ts` is a **barrel** that only\nre-exports them:\n\n```ts\n// migration/migrator.ts\nexport * from './migrator/shared.ts';\nexport * from './migrator/eslint.ts';\n// ...one line per module\n```\n\nSo **external code keeps importing from `./migrator.ts`** (the barrel) and\nnothing outside this directory had to change.\n\n## Modules\n\nPick the file by what a function _does_, not by where it happens to be called.\n\n| File                     | Owns                                                                                                                                                                                                                    |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `orchestrators.ts`       | Top-level entry points that wire everything together: `rewriteStandaloneProject`, `rewriteMonorepo`, `rewriteMonorepoProject`.                                                                                          |\n| `package-json.ts`        | `rewritePackageJson` and the direct dependency-spec rewriting it does.                                                                                                                                                  |\n| `vite-plus-bootstrap.ts` | \"Already on Vite+\" detection and the bootstrap/reconcile path: `detectVitePlusBootstrapPending`, `ensureVitePlusBootstrap`, `reconcileVitePlusBootstrapPackage`, the `ensure*`/`*Pending`/`*SatisfiesVitePlus` helpers. |\n| `catalog.ts`             | `pnpm-workspace.yaml` / catalog / overrides / build-allowance writers and catalog-dependency resolvers; bun catalog; `rewriteRootWorkspacePackageJson`; pnpm workspace-settings migration.                              |\n| `vitest-ecosystem.ts`    | Detecting direct vitest usage, the override-key (\"dependency selector\") parsing/dropping logic, managed-override sets, ecosystem alignment, legacy `vite-plus-test` wrapper-alias pruning.                              |\n| `vitest-v5.ts`           | Versioned Vitest v5 preflight, compatibility edits, and review findings. Pure AST and command transforms live under `../vitest-v5/`.                                                                                    |\n| `yarn.ts`                | `.yarnrc.yml`, Yarn PnP detection, workspace-hoisting fix, webdriverio detection.                                                                                                                                       |\n| `source-scan.ts`         | Scanning a project's source tree for signals (browser-mode, opt-in providers, `@nuxt/test-utils`, retained upstream-vitest references).                                                                                 |\n| `vite-config.ts`         | `vite.config.ts` merging, default-config injection, staged-config merge, lazy-plugin wrapping, import rewriting (`rewriteAllImports`), migrated-oxlint-config sanitization, lint-staged removal.                        |\n| `oxlint-config.ts`       | Oxlint JSON inheritance graphs and shared TypeScript config generation.                                                                                                                                                 |\n| `eslint.ts`              | ESLint → Oxlint migration, oxlint JS-plugin namespace handling, ESLint prompts/warnings.                                                                                                                                |\n| `prettier.ts`            | Prettier → Oxfmt migration and its prompts/warnings.                                                                                                                                                                    |\n| `tsup.ts`                | tsup → tsdown (`vp pack`) migration via `tsdown-migrate` and its prompts/warnings.                                                                                                                                      |\n| `tsx.ts`                 | tsx → `vpx` script migration, tsx dependency retention, and its prompt/warnings.                                                                                                                                        |\n| `tsconfig.ts`            | `tsconfig.json` cleanup and `types` rewriting.                                                                                                                                                                          |\n| `framework-shim.ts`      | Framework (Vue/Astro) shim detection and injection.                                                                                                                                                                     |\n| `git-hooks.ts`           | Vite+ hook defaults, project-owned hook preservation, and conservative detection of existing hook tools.                                                                                                                |\n| `setup.ts`               | `packageManager` pin, `setup-vp` GitHub Actions version, and Volta to `.node-version` migration.                                                                                                                        |\n| `core-finalization.ts`   | Finalizing an existing-Vite+ core migration (rules YAML, core package scripts).                                                                                                                                         |\n| `shared.ts`              | Cross-cutting constants, types, and tiny utilities used by **two or more** modules. The only module that other modules import from directly (see rules).                                                                |\n\n## Rules for adding / changing code\n\n1. **Add a function to the module that matches its category.** Keep it\n   `export`ed so the barrel surfaces it. If nothing fits, prefer extending an\n   existing module over creating a new one; if you do add a module, add a line\n   for it to `migration/migrator.ts`.\n\n2. **Importing another module's helper — use the barrel.** Reference\n   cross-module **functions** via the barrel:\n\n   ```ts\n   import { managedOverridePackages, rewritePackageJson } from '../migrator.ts';\n   ```\n\n   This creates an import cycle (the barrel re-exports your module), which is\n   **safe only because** these helpers are referenced _inside function bodies_\n   (at runtime), never at module-evaluation time. Preserve that invariant: do\n   not call a cross-module helper at the top level of a module.\n\n3. **`shared.ts` is the one exception — import it directly, never via the\n   barrel.** Things in `shared.ts` (e.g. `REMOVE_PACKAGES`,\n   `OPT_IN_BROWSER_PROVIDERS`, shared types) are referenced at _module-load_\n   time, so they must be imported from a fully-evaluated leaf:\n\n   ```ts\n   import { REMOVE_PACKAGES, type CatalogDependencyResolver } from './shared.ts';\n   ```\n\n   Keep `shared.ts` a **pure leaf**: it may import external packages and\n   `../utils/*` / `./report.ts` etc., but it must **not** import from any\n   sibling module or the barrel.\n\n4. **Where does a new shared thing go?** A constant / type / helper used by a\n   single module lives in that module. The moment a second module needs it,\n   move it to `shared.ts` and `export` it.\n\n5. **This split is structure only.** The barrel contains no logic; behavior\n   changes belong in the relevant module and need their unit test (and snap\n   test) updated, not the barrel.\n\n## When to add a new module\n\nDefault to extending an existing module. Add a new file only when one of these\nholds:\n\n- **A new self-contained category appears** — usually a new tool/format being\n  migrated (mirrors `eslint.ts`, `prettier.ts`, `yarn.ts`, `git-hooks.ts`).\n  Test: you can name its single responsibility without saying \"and\".\n- **An existing module outgrows readability _and_ has a clean seam** — a\n  cohesive, loosely-coupled sub-cluster. Rough trigger: **>~900–1,000 lines plus\n  a natural split point** (e.g. `catalog.ts` could become `catalog.ts` +\n  `pnpm-workspace.ts`). Size alone is not enough.\n- **Scattered helpers form a coherent theme** and consolidating them aids\n  discoverability.\n\nDo **not** add a file when: the function fits an existing category (extend it);\nit's one small helper (owning module, or `shared.ts` if shared); it would be 1–2\nfunctions with no theme (fragmentation is as bad as a monolith); or it would\nhave a tight two-way dependency with another module (they belong together).\n\nWhen you do add one: give it a single-responsibility name, add its `export *`\nline to `migration/migrator.ts`, add a row to the module table above, and decide\nits layer — if siblings reference it at **load time** it must be a pure leaf (or\nthose pieces go in `shared.ts`); helpers used only inside function bodies may\nimport from the barrel.\n\n## Validating a change\n\nFrom the repo root:\n\n```bash\nvp check                                             # format + lint + type-check\npnpm -F vite-plus exec vitest run src/migration      # migration unit tests\njust snapshot-test migration                         # migration CLI snapshot tests\n```\n\nReview any changed `.md` snapshots like code. A pure reorganization must not\nchange type-check results, the unit-test count, or snapshots. A behavior change\nshould be accompanied by the matching unit or snapshot test updates.\n","isInternal":false,"tokens":1803,"sizeBytes":9751},{"name":"README.md","path":"packages/cli/templates/generator/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/cli/templates/generator/README.md","title":"generator Documentation","category":"plugin-manifest","format":"markdown","content":"# Vite+ Code Generator Starter\n\nA starter for creating a Vite+ code generator.\n\n## Usage\n\nFrom monorepo root:\n\n```bash\n# run and select the generator\nvp create\n```\n\nFor automation, provide the directory and every required template option:\n\n```bash\nvp create <generator-name> --no-interactive -- --directory new-package --name new-package\n```\n\nVite+ sets `VP_CREATE_INTERACTIVE=0` for non-interactive local Bingo generators.\nThis starter then validates the arguments and runs Bingo's programmatic API.\nMissing options and existing directories fail before any files are generated.\nExisting generators are copied project files and are not updated by upgrading Vite+.\nTo adopt this behavior, update their entrypoint to match this starter.\nDirect invocation uses the interactive CLI unless this variable is set to `0`.\nWhen adding template options, also add their CLI types in `bin/index.ts`.\n\n## Development\n\n```bash\n# Edit the template\ncode src/template.ts\n\n# Test the generator CLI\nvp run dev\n\n# Run tests\nvp run test\n```\n\n## Customization\n\nEdit `src/template.ts` to customize:\n\n- Options schema (using Zod)\n- File generation logic\n- Scripts and suggestions\n\nMore information about the [Bingo Templates](https://create.bingo/) can be found [here](https://create.bingo/build/concepts/creations).\n","isInternal":false,"tokens":287,"sizeBytes":1293},{"name":"README.md","path":"packages/cli/templates/monorepo/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/cli/templates/monorepo/README.md","title":"monorepo Documentation","category":"plugin-manifest","format":"markdown","content":"# Vite+ Monorepo Starter\n\nA starter for creating a Vite+ monorepo.\n\n## Development\n\n- Check everything is ready:\n\n```bash\nvp run ready\n```\n\n- Run the tests:\n\n```bash\nvp run -r test\n```\n\n- Build the monorepo:\n\n```bash\nvp run -r build\n```\n\n- Run the development server:\n\n```bash\nvp run dev\n```\n","isInternal":false,"tokens":88,"sizeBytes":292},{"name":"README.md","path":"packages/create-vite-plus/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/create-vite-plus/README.md","title":"create-vite-plus Documentation","category":"plugin-manifest","format":"markdown","content":"# create-vite-plus\n\nCreate a new [Vite+](https://viteplus.dev) project without installing the global `vp` CLI.\n\n```bash\npnpm create vite-plus\nnpm create vite-plus@latest\nyarn create vite-plus\nbun create vite-plus\n```\n\nThis runs [`vp create`](https://viteplus.dev/guide/create) from the `vite-plus` package and uses the package manager that ran it, unless you pass `--package-manager` or create the project inside an existing workspace. Arguments are forwarded unchanged, so the first argument is the template:\n\n```bash\npnpm create vite-plus vite:application --directory my-app\npnpm create vite-plus vite -- --template react-ts\n```\n\nnpm needs `--` before options meant for `vp create`, for example `npm create vite-plus@latest -- vite:library --no-interactive`.\n","isInternal":false,"tokens":185,"sizeBytes":761},{"name":"README.md","path":"packages/prompts/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/prompts/README.md","title":"prompts Documentation","category":"plugin-manifest","format":"markdown","content":"# @voidzero-dev/vite-plus-prompts\n\nTerminal prompts shared by Vite+ and tools that use its visual identity.\n\n```ts\nimport { isCancel, log, note, select } from '@voidzero-dev/vite-plus-prompts';\n\nconst framework = await select({\n  message: 'Choose a framework:',\n  options: [\n    { value: 'react', label: 'React', hint: 'TSX Pages' },\n    { value: 'vue', label: 'Vue', hint: 'Vue SFC Pages' },\n  ],\n});\n\nif (!isCancel(framework)) {\n  log.success(`Selected ${framework}`);\n  note('Run vp dev to start developing.', 'Next steps');\n}\n```\n\nBlue `›` pointers identify focused choices and active prompts. Completed steps\nand rounded box borders use gray; warnings use yellow and errors use red.\nInactive choices are dimmed, and descriptive hints appear on the focused choice.\nLogs retain severity symbols without guide bars and use compact spacing.\nNotes have complete borders and readable content by default.\n\nGuides are off by default. Pass `withGuide: true` for one call, or use\n`updateSettings({ withGuide: true })` to enable them globally. This controls the\nprompt gutter, not box borders. Logs accept `spacing` for explicit blank rows;\nnotes accept `format` and boxes accept `formatBorder` and `rounded`.\n\nSelect and multiselect prompts show compact keyboard instructions. Pass\n`showInstructions: false` to hide them. Text, password, path, and autocomplete\nvalidation supports synchronous or asynchronous functions and Standard Schema.\nPasswords accept a placeholder and always mask entered values. Path prompts\nsupport directory navigation and Tab completion; general autocomplete can opt\nin with `completeOnTab: true`.\n\n`accessible: true`, `updateSettings({ accessible: true })`, or `ACCESSIBLE=1`\nenables static prompt output for screen readers. Spinners use static output in\naccessible mode, CI, and non-TTY streams. Their `pause()` and `resume()` methods\npreserve elapsed time while excluding pauses. `CANCEL_SYMBOL` is exported for\ncallers that need the exact cancellation sentinel; `isCancel()` narrows results.\n\nThe interaction engine is `@clack/core` 1.5.1 or newer. Rendering and the shared\nvisual defaults are maintained in this package, so consumers can use the same\nappearance without implementing their own prompt renderers.\n\n## Verification\n\nFrom the repository root:\n\n```sh\nvp check packages/prompts/src\nvp test run packages/prompts/src/__tests__\npnpm --filter @voidzero-dev/vite-plus-prompts build\n```\n","isInternal":false,"tokens":544,"sizeBytes":2420},{"name":"README.md","path":"packages/tools/README.md","rawUrl":"https://raw.githubusercontent.com/voidzero-dev/vite-plus/HEAD/packages/tools/README.md","title":"tools Documentation","category":"plugin-manifest","format":"markdown","content":"# tools for internal development use\n\n## Bins\n\n- json-edit: A CLI tool to edit JSON files such as package.json, used by the release workflow to stamp build versions\n\n## `tool` subcommands\n\nRun with `tool <name>`:\n\n- sync-remote: Sync upstream dependency sources and catalog versions from `.upstream-versions.json`\n- install-global-cli: Install the local build of the global `vp` CLI. The tool\n  reuses an existing `~/.vite-plus` install. Otherwise, it uses the split\n  platform data directory.\n- brand-vite: Apply Vite+ branding patches to the synced vite source (also runs at the end of sync-remote)\n- local-npm-registry: Serve locally packed checkout packages behind a real registry HTTP interface for snapshot tests, ecosystem e2e, and local `vp migrate`/`vp create` iteration\n\n## Local npm registry\n\nSee the [contributing guide](../../CONTRIBUTING.md#test-vp-migrate--vp-create-through-a-local-npm-registry) for local build and usage commands.\n\n[`src/local-npm-registry.ts`](src/local-npm-registry.ts) packs the checkout, serves its tarballs through a registry HTTP interface, and proxies other packages upstream.\n\n- Served versions use an old publish time so package-manager minimum-release-age checks allow local builds immediately.\n- Wrapped commands use temporary Yarn Berry and bun caches to avoid reusing stale local builds with the same package version.\n- The server also backs [PTY snapshot cases](../../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) and [ecosystem e2e tests](../../ecosystem-ci/patch-project.ts). Snapshots use recorded test packages and a run-scoped response cache. `local-registry = true` additionally serves the packed checkout; ecosystem and standalone runs retain upstream version merging.\n- Refresh the small snapshot registry fixtures explicitly with `node packages/tools/src/record-snapshot-registry.ts`. See the [fixture documentation](../../crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md).\n","isInternal":false,"tokens":437,"sizeBytes":1954}],"systemPromptSnippet":"<yakaai_skills repo=\"voidzero-dev/vite-plus\">\n<!-- File: AGENTS.md (Tokens: ~1728 | Category: root-instruction) -->\n# Vite+\n\nVite+ is the web toolchain behind `vp`: Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, Vite Task, runtime and package-manager workflows, and project creation/migration.\n\nThis guide applies to this repository; `CLAUDE.md` points here. For changes to the guidance shipped to user projects, use `packages/cli/AGENTS.md` and `packages/cli/src/utils/agent.ts`. Leave those files unchanged for root-guide tasks.\n\n## Context by Task\n\n- **CLI routing**: `packages/cli/src/bin.ts` dispatches JS commands; `packages/cli/binding/src/lib.rs` and `packages/cli/binding/src/cli/mod.rs` handle local NAPI commands. Global routing starts at `crates/vp_global_cli/src/main.rs` and `crates/vp_global_cli/src/cli.rs`. Compare global and local paths for routing bugs.\n- **Package managers and runtimes**: use `crates/vp_pm_cli/` and `crates/vp_js_runtime/`. The Windows shim trampoline lives in `crates/vp_trampoline/`, outside the Cargo workspace.\n- **Config loading**: compare the [static extractor](crates/vp_static_config/README.md) with the JS fallback in `packages/cli/src/resolve-vite-config.ts`.\n- **Migration**: follow the [migrator instructions](packages/cli/src/migration/migrator/README.md) when changing `vp migrate`; use [migration rules](docs/guide/migrate-rules.md) for expected behavior.\n- **Bundled tools and exports**: use [CLI bundling](packages/cli/BUNDLING.md) and [core bundling](packages/core/BUNDLING.md). Test resolution starts at `packages/cli/src/resolve-test.ts`; `packages/cli/build.ts` generates test API shims.\n- **Testing a local build in a project**: follow [CONTRIBUTING.md](CONTRIBUTING.md) for linking and installation. For `vp migrate` / `vp create`, use the local registry in `packages/tools/src/local-npm-registry.ts`, which also serves PTY fixtures and ecosystem tests.\n- **Product documentation**: start at `README.md` and the VitePress site in `docs/guide/` and `docs/config/`.\n- **Setup and build commands**: use `CONTRIBUTING.md`, `justfile`, and `package.json`. Start with `just init` for initial setup. Repository lint, format, and test configuration lives in `vite.config.ts`; TypeScript settings live in `tsconfig.json`.\n\n## Repository Constraints\n\n- Distinguish built-ins from tasks: `vp test` executes upstream Vitest; `vp run test` executes a script or configured task named `test`. `vpr` is shorthand for `vp run`. Inspect command surfaces with `vp help` / `vp <command> --help`, and bundled versions with `vp --version`.\n- Keep task configuration under `run` in `vite.config.ts`; do not introduce `vite-task.json`. Existing `package.json` scripts are `vp run` targets without caching by default. Use `run.tasks` for explicit command configuration, default caching, dependencies, or input/environment tracking. A task name must occur in only one of these files. See the [run guide](docs/guide/run.md) and [run configuration](docs/config/run.md).\n- Keep public test imports on `vite-plus/test*`, which wraps upstream `vitest` and `@vitest/browser*`. Do not recreate `packages/test` or `@voidzero-dev/vite-plus-test`.\n- Vite Task crates are git dependencies in `Cargo.toml`; there is no local `crates/vt`. Do not run `cargo test -p vt` here.\n- Use `vp_shared::VpDirs` for Vite+ directory roots. See `crates/vp_shared/src/dirs.rs` and `crates/vp_shared/src/dirs/resolution.rs`. Call sites must not construct category paths or read `VP_HOME` or `XDG_*` directly.\n- Follow [`.clippy.toml`](.clippy.toml) for Rust restrictions and replacements. Use `crates/vp_shared/src/output.rs` for user-facing output; command output that can be piped must use `print_and_flush` or another broken-pipe-safe helper instead of `print!` or `println!`. Enable `clippy::print_stdout` in migrated modules so CI prevents direct stdout macros from returning. Use `crates/vp_shared/src/env_config.rs` for test-scoped environment configuration. See `crates/vp_command/src/lib.rs` for `vt_path` usage.\n- For TypeScript CLI output, use `packages/cli/src/utils/terminal.ts` and match the surrounding command style.\n- Keep changes scoped to the task and leave unrelated tracked and untracked files alone. Prefer source references over duplicated instructions in this guide.\n\n## Checks and Tests\n\nPrefer local fixtures or mock servers for tests that depend on external services. Use real network requests only when they are necessary to verify the behavior under test, and explain that need in the test. Reuse existing test infrastructure, preserve behavioral coverage, and keep mutable state isolated between cases.\n\nFor behavior changes, find nearby tests before editing and add or update coverage in the same area. Choose checks for the changed layer:\n\n| Change                         | Validation                                                                                                                         |\n| ------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------- |\n| Documentation / agent guidance | Verify referenced paths, commands, and links; `git diff --check -- <files>`                                                        |\n| TypeScript / JS CLI            | `vp check`, `pnpm test:unit`, and focused package tests                                                                            |\n| Rust CLI / crates              | `just check`, `just test`, `just lint`                                                                                             |\n| CLI output / interactions      | Focused tests and `just snapshot-test <filter>`; review snapshot diffs                                                             |\n| Global CLI                     | `pnpm bootstrap-cli` and `vp --version` for installed end-to-end checks; `just snapshot-test-global <filter>` for global snapshots |\n| Release / build                | `just build`                                                                                                                       |\n| Full pre-merge check           | `pnpm bootstrap-cli && pnpm test && git status`                                                                                    |\n\nValidate `vp check`, lint, format, and type-check changes end-to-end: bundled-tool routing can hide config drift. Use `vp check --fix` only when you intend to apply formatting or lint fixes. Documentation-only changes need no unrelated test suites.\n\n### CLI Snapshots\n\nWrite new CLI tests, including interactive flows, in `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/`. Read the [snapshot runner instructions](crates/vp_cli_snapshots/tests/cli_snapshots/README.md) before adding cases; they define `snapshots.toml`, CLI flavors, `vpt` helpers, and prompt milestones. See [the RFC](rfcs/interactive-snapshot-tests.md) for design rationale.\n\n- Steps are argv arrays without an implicit shell. Use `vpt` helpers instead of coreutils for portable file operations.\n- For package-manager cases, prefer the runner's local registry, [shared registry fixtures](crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md), and response cache over new public-registry requests. Keep package-manager caches isolated per case, and refresh recorded fixtures only through the explicit maintenance command.\n- For changes to network use or scheduling, compare request counts and elapsed time with the same cases, worker count, and initial cache state. Do not remove assertions or share mutable case state to improve speed.\n- Local-flavor cases require a fresh `packages/cli/dist`. Use `just snapshot-test-global <filter>` when no JS build is available.\n- For intended snapshot changes, run `UPDATE_SNAPSHOTS=1 just snapshot-test <filter>`. Review and commit the recorded `.md` files with the fixture; mismatches fail the run and produce `.md.new` files.\n\n## Pull Requests\n\nPrefer a stack of small PRs for work with separable layers. Use the `gh-stack` extension or github.com; stacks require branches in this repository, so use standalone PRs from forks. Follow [CONTRIBUTING.md](CONTRIBUTING.md#submitting-pull-requests) for submission and commit-signing requirements.\n\n<!-- File: CLAUDE.md (Tokens: ~5 | Category: claude-rule) -->\n@AGENTS.md\n\n<!-- File: .claude/skills/add-ecosystem-ci/SKILL.md (Tokens: ~1215 | Category: anthropic-skill) -->\n---\nname: add-ecosystem-ci\ndescription: Add a new ecosystem-ci test case for testing real-world projects against vite-plus\nallowed-tools: Bash, Read, Edit, Write, WebFetch, AskUserQuestion\n---\n\n# Add Ecosystem-CI Test Case\n\nAdd a new ecosystem-ci test case following this process:\n\n## Step 1: Get Repository Information\n\nAsk the user for the GitHub repository URL if not provided as argument: $ARGUMENTS\n\nUse GitHub CLI to get repository info:\n\n```bash\ngh api repos/OWNER/REPO --jq '.default_branch'\ngh api repos/OWNER/REPO/commits/BRANCH --jq '.sha'\n```\n\n## Step 2: Auto-detect Project Configuration\n\n### 2.1 Check for Subdirectory\n\nFetch the repository's root to check if the main package.json is in a subdirectory (like `web/`, `app/`, `frontend/`).\n\n### 2.2 Check if Project Already Uses Vite-Plus\n\nCheck the project's root `package.json` for `vite-plus` in `dependencies` or `devDependencies`. If the project already uses vite-plus, set `forceFreshMigration: true` in `repo.json`. This tells `patch-project.ts` to set `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping with \"already using Vite+\".\n\n### 2.3 Auto-detect Commands from GitHub Workflows\n\nFetch the project's GitHub workflow files to detect available commands:\n\n```bash\n# List workflow files\ngh api repos/OWNER/REPO/contents/.github/workflows --jq '.[].name'\n\n# Fetch workflow content (for each .yml/.yaml file)\ngh api repos/OWNER/REPO/contents/.github/workflows/ci.yml --jq '.content' | base64 -d\n```\n\nLook for common patterns in workflow files:\n\n- `pnpm run <command>` / `npm run <command>` / `yarn <command>`\n- Commands like: `lint`, `build`, `test`, `type-check`, `typecheck`, `format`, `format:check`\n- Map detected commands to `vp` equivalents: `vp run lint`, `vp run build`, etc.\n\n### 2.4 Ask User to Confirm\n\nPresent the auto-detected configuration and ask user to confirm or modify:\n\n- Which directory contains the main package.json? (auto-detected or manual)\n- What Node.js version to use? (22 or 24, try to detect from workflow)\n- Which commands to run? (show detected commands as multi-select options)\n- Which OS to run on? (both, ubuntu-only, windows-only) - default: both\n\n## Step 3: Update Files\n\n1. **Add to `ecosystem-ci/repo.json`**:\n\n   ```json\n   {\n     \"project-name\": {\n       \"repository\": \"https://github.com/owner/repo.git\",\n       \"branch\": \"main\",\n       \"hash\": \"full-commit-sha\",\n       \"forceFreshMigration\": true // only if project already uses vite-plus\n     }\n   }\n   ```\n\n2. **Add to `.github/workflows/e2e-test.yml`** matrix:\n   ```yaml\n   - name: project-name\n     node-version: 24\n     directory: web # only if subdirectory is needed\n     command: |\n       vp run lint\n       vp run build\n   ```\n\n## Step 4: Verify\n\n### 4.1 Build fresh tgz packages\n\nAlways rebuild tgz packages from latest source to avoid using stale cached versions:\n\n```bash\n# Rebuild the global CLI first (includes Rust binary + NAPI binding)\npnpm bootstrap-cli\n\n# Pack fresh tgz files into tmp/tgz/\nrm -rf tmp/tgz && mkdir -p tmp/tgz\ncd packages/core && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/test && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\ncd packages/cli && pnpm pack --pack-destination ../../tmp/tgz && cd ../..\nls -la tmp/tgz\n```\n\n### 4.2 Clone and test locally\n\n```bash\nnode ecosystem-ci/clone.ts project-name\n```\n\n### 4.3 Patch and run commands\n\n```bash\n# Run from the ecosystem-ci temp directory\ncd $(node -e \"const os=require('os'); console.log(os.tmpdir() + '/vite-plus-ecosystem-ci')\")\n\n# Migrate the project (uses tgz files from tmp/tgz/)\nnode /path/to/vite-plus/ecosystem-ci/patch-project.ts project-name\n\n# Run the configured commands\ncd project-name\nvp run build\n```\n\n3. **Add OS exclusion to `.github/workflows/e2e-test.yml`** (if not running on both):\n\n   For ubuntu-only:\n\n   ```yaml\n   exclude:\n     - os: windows-latest\n       project:\n         name: project-name\n   ```\n\n   For windows-only:\n\n   ```yaml\n   exclude:\n     - os: ubuntu-latest\n       project:\n         name: project-name\n   ```\n\n## Important Notes\n\n- The workflow matrix `directory` field is optional - only add it if the project commands must run in a subdirectory (like a workspace member)\n- `patch-project.ts` always runs `vp migrate` and `vp install` at the clone root; `vp migrate` rejects workspace member targets\n- `forceFreshMigration` is required for projects that already have `vite-plus` in their package.json — it sets `VP_FORCE_MIGRATE=1` so `vp migrate` forces full dependency rewriting instead of skipping\n- OS exclusions are added to the existing `exclude` section in the workflow matrix\n\n<!-- File: .claude/skills/bump-vite-task/SKILL.md (Tokens: ~1639 | Category: anthropic-skill) -->\n---\nname: bump-vite-task\ndescription: Bump vite-task git dependency to the latest main commit. Use when you need to update the vite-task git-dependency crates (vt, fspy, vt_pty_terminal_test, and friends; the authoritative set lives in Cargo.toml) in vite-plus.\nallowed-tools: Read, Grep, Glob, Edit, Bash, Agent, WebFetch\n---\n\n# Bump vite-task to Latest Main\n\nUpdate the vite-task git dependency in `Cargo.toml` to the latest commit on the vite-task main branch, fix any breaking changes, and create a PR.\n\n## Steps\n\n### 1. Get current and target commits\n\n- Read `Cargo.toml` and find the current `rev = \"...\"` for any vite-task git dependency; enumerate the full set with `grep 'voidzero-dev/vite-task' Cargo.toml` instead of trusting a hardcoded list. They all share the same revision.\n- Get the latest commit hash on vite-task's main branch:\n  ```bash\n  git ls-remote https://github.com/voidzero-dev/vite-task.git refs/heads/main\n  ```\n\n### 2. Update Cargo.toml\n\n- Replace **all** occurrences of the old commit hash with the new one in `Cargo.toml`. The set of crates sharing the vite-task revision changes over time; the grep from step 1 is the authoritative list, so update every entry it returns.\n- The commented `[patch.\"https://github.com/voidzero-dev/vite-task.git\"]` section near the bottom of `Cargo.toml` mirrors the same crates for local vite-task development; keep it in sync when crates are added or removed (a plain rev bump does not touch it).\n\n### 3. Ensure upstream dependencies are cloned\n\n- `cargo check` requires the `./rolldown` and `./vite` directories to exist (many workspace path dependencies point to `./rolldown/crates/...`).\n- Locally, clone them using the commit hashes from `packages/tools/.upstream-versions.json`.\n- CI handles this automatically via the `.github/actions/clone` action.\n\n### 4. Verify compilation\n\n- Run `cargo check` to ensure the new vite-task compiles without errors.\n- If there are compilation errors, these are **breaking changes** from vite-task. Fix them in the vite-plus codebase (the consuming side), not in vite-task.\n- Common breaking changes include: renamed functions/methods, changed function signatures, new required fields in structs, removed public APIs.\n\n### 5. Run tests\n\n- Run `cargo test -p vp_command -p vp_error -p vp_pm_cli -p vp_js_runtime -p vp_migration -p vp_shared -p vp_static_config -p vite-plus-cli -p vp_global_cli` to run the vite-plus crate tests.\n- Note: Some tests require network access (e.g., `vp_pm_cli::package_manager` tests, `vp_global_cli::commands::env` tests). These may fail in sandboxed environments. Verify they also fail on the main branch before dismissing them.\n- Note: `cargo test -p vt` will NOT work because vt is a git dependency, not a workspace member.\n- The PTY snapshot suite (`crates/vp_cli_snapshots`) is excluded from `just test`; it is covered in step 6.\n\n### 6. Update snapshot tests\n\nvite-task changes often affect CLI output, which means snapshot tests need updating. Common output changes:\n\n- **Status icons**: e.g., cache hit/miss indicators may change\n- **New CLI options**: e.g., new flags added to `vp run` that show up in help output\n- **Cache behavior messages**: e.g., new summary lines about cache status\n- **Task output formatting**: e.g., step numbering, separator lines\n\n**PTY snapshot suite (`crates/vp_cli_snapshots`):**\n\n- A bump can break it two ways: runner compilation (it consumes vite-task's `vt_pty_terminal_test`, `vt_pty_terminal_test_client`, and `vt_snapshot_test` crates directly, so their API changes surface here; fix in the runner) and recorded CLI output.\n- Update output locally with real assertions: `UPDATE_SNAPSHOTS=1 just snapshot-test`, then review the `.md` diffs like code. Without a built `packages/cli/dist`, run the global flavor only: `VP_SNAP_SKIP_FLAVORS=local UPDATE_SNAPSHOTS=1 just snapshot-test`.\n- Windows runs in the `CLI snapshot test (Windows)` CI job via a cross-compiled nextest archive; snapshots are OS-shared, so a Windows-only diff there means a redaction gap, not a re-record.\n- Reference: `crates/vp_cli_snapshots/tests/cli_snapshots/README.md`.\n\n### 7. Review changelog and update docs\n\n- Fetch the vite-task `CHANGELOG.md` diff between old and new commits to see what changed:\n  ```\n  https://raw.githubusercontent.com/voidzero-dev/vite-task/<new-hash>/CHANGELOG.md\n  ```\n- Review each changelog entry and determine if it affects user-facing behavior: new CLI options, changed defaults, new config fields, removed features, etc.\n- The changelog contains links to the corresponding vite-task PRs. For complex changes, check the PR description and code diff (especially any docs changes in the PR) to understand the full scope of the change.\n- If user-facing changes are found, update the relevant docs in `docs/` (e.g., `docs/guide/`, `docs/config/`).\n- Common doc updates include:\n  - **New CLI flags/options**: Update the relevant config doc (e.g., `docs/config/run.md`, `docs/config/build.md`)\n  - **New features or commands**: Add or update the relevant guide page (e.g., `docs/guide/cache.md`)\n  - **Changed defaults or behavior**: Update any docs that describe the old behavior\n  - **Removed/deprecated options**: Remove or mark as deprecated in the relevant docs\n- If no user-facing changes are found, skip this step.\n\n### 8. Create the PR\n\n- Commit message: `chore: bump vite-task to <short-hash>`\n- PR title: `chore: bump vite-task to <short-hash>`\n- PR body: Link to vite-task CHANGELOG.md diff between old and new commits:\n  ```\n  https://github.com/voidzero-dev/vite-task/compare/<old-hash>...<new-hash>#diff-06572a96a58dc510037d5efa622f9bec8519bc1beab13c9f251e97e657a9d4ed\n  ```\n\n### 9. Verify CI\n\nAfter creating the PR, automatically watch CI without asking the user first. Ensure the `done` check passes. Key checks to monitor:\n\n- **Lint**: Clippy and format checks\n- **Test** (Linux, Mac, Windows): Rust unit tests\n- **CLI snapshot test** (Linux, Mac, Windows): the PTY snapshot suite - most likely to fail on a vite-task bump (runner compiles against vite-task crates AND asserts CLI output)\n- **Run task**: Task runner integration tests\n- **Security Analysis**: Fix any `cargo deny` advisories. It runs only when `Cargo.lock` changes, so a green `main` does not mean the lockfile is clean.\n\nThe only **required** status check for merging is `done`, which aggregates the other checks (excluding Security Analysis).\n\n## Notes\n\n- Building the full CLI locally (`pnpm bootstrap-cli`) requires the rolldown Node.js package to be built first, which is complex. Prefer the global-only snapshot command above or CI when no local CLI build is available.\n- `Cargo.lock` is automatically updated by cargo when you change the revision in `Cargo.toml`.\n\n<!-- File: .claude/skills/release-manager/SKILL.md (Tokens: ~11567 | Category: anthropic-skill) -->\n---\nname: release-manager\ndescription: Run the standard vite-plus release process end-to-end as the release manager. Covers preparing the release PR, syncing the NAPI binding version, writing the categorized changelog, smoke-testing via a preview build, handling release-branch CI failures, merging, and post-release verification and announcements. Use when asked to cut, prepare, or manage a vite-plus release (e.g. \"release v0.2.3\", \"act as release manager\").\nallowed-tools: Bash, Read, Edit, Write, Grep, Glob, WebFetch\n---\n\n# Vite+ Release Manager\n\nRun a standard vite-plus release from version bump to published announcement. Any maintainer with repo write access can follow this; the only extra privilege needed is approval rights on the `release` GitHub environment (step 7).\n\n## Usage\n\n```\n/release-manager                    # start a new release: ask for the target version, begin at step 1\n/release-manager X.Y.Z              # start a new release for that version\n/release-manager <PR URL or #N>     # take over an in-flight release\n```\n\nWhen given a release PR (URL or number), do not start from step 1. First audit the release's current state, then continue from the earliest unfinished step:\n\n- Is the binding version synced? (step 2: `grep -c \"'<prev>'\" packages/cli/binding/index.cjs` on the release branch)\n- Do the release version examples in the migration guide and the setup, migration, and upgrade prompts match `packages/cli/package.json`? (step 2)\n- Is the PR description still the `prepare_release` boilerplate, or already a categorized changelog? (step 3)\n- Is a preview build present and for the current head? (step 4)\n- Does `main` have commits the release branch lacks? (`git log origin/release/vX.Y.Z..origin/main`, step 5)\n- What is CI status? (`gh pr checks <PR#>`, step 5)\n- Already merged? Check the Release workflow (`gh run list --workflow Release --repo voidzero-dev/vite-plus`) and whether the GitHub release body is still the generated stub, then continue at step 7 or 8.\n\nReport the detected state before making changes, so the previous release manager's work is not redone or overwritten.\n\nBefore post-release work, fetch `origin/main` and read its copy of this skill (`git show origin/main:.claude/skills/release-manager/SKILL.md`). A checkout left behind after the merge can contain superseded release or announcement instructions.\n\n## Pipeline overview\n\n1. `Prepare Release` workflow bumps versions and opens the release PR (`release/vX.Y.Z` -> `main`).\n2. Release manager: sync `binding/index.cjs` and the release versions in the documentation and prompts, write the changelog PR description, offer the preview-build smoke test (recommend it when the release has more than 10 commits since the previous tag), get CI green.\n3. Merging the PR pushes a `packages/cli/package.json` change to `main`, which triggers `release.yml`: build, manual approval gate, npm publish, GitHub release, Docker image, Discord notification.\n4. Release manager: polish the GitHub release notes, verify installs, announce.\n\nCanonical sources: `.github/workflows/prepare_release.yml`, `.github/workflows/release.yml`, `.github/workflows/publish-preview.yml`.\n\n## 1. Start the release\n\n```bash\ngh workflow run prepare_release.yml --repo voidzero-dev/vite-plus -f version=X.Y.Z\n```\n\nThe workflow bumps `packages/cli/package.json`, `packages/core/package.json`, `packages/cli/binding/Cargo.toml`, and `crates/vp_global_cli/Cargo.toml`, refreshes `Cargo.lock`, and opens a PR titled `release: vX.Y.Z` from branch `release/vX.Y.Z`. The PR body ends with `Merging this PR will trigger the release workflow.` and that line must survive every later edit.\n\n## 2. Sync release versions (required every release)\n\n### NAPI binding\n\nNAPI bakes the package version into version checks in `packages/cli/binding/index.cjs` (26+ sites). `prepare_release` bumps `package.json` but does not regenerate this file, so CI's `Ensure no unexpected file changes after build` step in the `CLI E2E test` job fails until it is synced. Do this immediately; do not wait for CI to fail.\n\n```bash\ngit fetch origin release/vX.Y.Z && git checkout release/vX.Y.Z\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs          # non-zero means sync needed\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs\n```\n\nApply two whole-file text replacements (`<prev>` is the previous release version, e.g. `0.2.1`):\n\n1. `'<prev>'` -> `'<curr>'`\n2. `expected <prev> but got` -> `expected <curr> but got`\n\nThen confirm the replacement took; both counts must now be zero:\n\n```bash\ngrep -c \"'<prev>'\" packages/cli/binding/index.cjs                 # 0\ngrep -c \"expected <prev> but got\" packages/cli/binding/index.cjs  # 0\n```\n\nDo not regenerate via a full build; the text replace is deterministic and byte-identical to what `napi build` would produce for a version-only bump. Commit with this exact message shape (it makes `git log --grep` find the sync across releases) and push:\n\n```\nchore(release): sync binding/index.cjs version to <curr>\n\nNAPI bakes the package.json version into binding/index.cjs version\nchecks. The prepare_release workflow bumps package.json but does not\nregenerate this file, so the CI build's regeneration step produces a\ndiff that the post-build no-unexpected-changes guard rejects.\n```\n\n### Documentation and prompts\n\nUse the version in the release branch's `packages/cli/package.json` as the target release version in these files:\n\n- `docs/guide/migrate.md`: pnpm and npm migration command examples and matching release prose.\n- `docs/.vitepress/theme/data/migration-prompts.ts`: `setupPrompt`, `migrationPrompt`, `upgradePrompt`, and their shared instructions, including command examples and matching release prose. `CopyPrompt` uses `setupPrompt` on both the homepage and Getting Started guide.\n\nUpdate every `--package=vite-plus@<curr>` pin and the corresponding `For the <curr> release` and `Replace <curr>` text. Keep an exact version; do not replace it with a placeholder, a major range, or `latest`.\n\nPreserve historical versions such as the migration's source version and the release that introduced a breaking change. Leave Node.js requirements, bundled tool versions, and preview-registry instructions unchanged unless their requirements change.\n\nCommit these updates on the release branch with the binding sync or in a separate release-version sync commit. Recheck both files and the binding after a target-version change or a merge from `main`. Before merging, confirm that the guide and all three prompts use the target release in both package-manager commands and their matching prose, then run `git diff --check`.\n\nOnly these release-version sync commits go directly on the release branch. Everything else goes through `main` (see step 5).\n\n## 3. Write the release PR description\n\nThe release tag does not exist yet, so read release files from the PR head branch and generate notes against `main`:\n\n```bash\ngit fetch --tags && git tag --sort=-version:refname | head -5   # find <prev>\ngit log --oneline v<prev>..origin/main\ngh api repos/voidzero-dev/vite-plus/releases/generate-notes \\\n  -f tag_name=v<curr> -f previous_tag_name=v<prev> -f target_commitish=main\n\ngit show origin/release/v<curr>:packages/core/package.json          # bundledVersions: vite, rolldown, tsdown\ngit show origin/release/v<curr>:packages/tools/.upstream-versions.json  # vite/rolldown commit hashes\ngit show origin/release/v<curr>:pnpm-workspace.yaml                 # vitest/oxlint/oxlint-tsgolint/oxfmt catalog pins\n```\n\n### Structure\n\n```markdown\n<One or two sentences on the release theme. Do not repeat the PR title as an opener line; GitHub renders the title directly above the body, and step 8 would only strip it again. When a blog post accompanies the release, read it first (via its preview URL if not yet deployed), align the theme with it, and link the final URL here even if that URL is not live yet.>\n\n### Breaking Changes\n\n### Features\n\n### Fixes & Enhancements\n\n### Refactor\n\n### Docs\n\n### Chore\n\n### Bundled Versions\n\n### Upgrade\n\n### New Contributors\n\n**Full Changelog**: https://github.com/voidzero-dev/vite-plus/compare/v<prev>...v<curr>\n\n---\n\nMerging this PR will trigger the release workflow.\n```\n\n### Categorization rules\n\n- Every PR from `generate-notes` appears exactly once, except fully reverted changes described below and bot-authored PRs that carry nothing for a user to read or act on (a docs stats refresh, a badge update). Keep bot PRs that do change what users get, such as the upstream dependency upgrades. Record each omitted PR and its reason when reporting validation counts.\n- **Breaking Changes goes first, above Features, and only when the release has one.** A rename is breaking only when the old name stops working; if a deprecated alias is retained it is not breaking, so keep the two in different sections rather than merging them into one entry. Give each breaking entry an old -> new table when several names change, plus one line telling readers where to update (shell profile, CI job, Dockerfile). Do not editorialize about the version number.\n- When several breaking changes affect different workflows, group them under short `####` headings. Explain the changed behavior and required action before each table; keep automatic migration steps separate from changes users must make manually.\n- **Describe the net change between the two released versions, not intra-cycle churn.** When several PRs touch the same area within one release (one narrows a behavior, a later one broadens it back), the reader only sees the delta from `v<prev>` to `v<curr>`; describe that once, listing every PR number, and do not narrate a regression that was introduced and then fixed inside the cycle. Apply this to the intro/theme sentence too.\n- If a change and its complete revert are both unreleased, omit both when they leave no net change. Remove sections with no remaining entries. A revert of behavior in the previous release still needs an entry.\n- `feat` -> Features, `fix` -> Fixes & Enhancements, `refactor` and `revert` -> Refactor (never Chore), `docs` -> Docs, `test` / `ci` / `chore` -> Chore.\n- **Place an entry by its user impact when the prefix disagrees.** A `fix` that gives an existing command new observable behavior (for example, starting to set environment variables for child processes) belongs in Features. A fix that makes a command reject input it used to drop silently stays in Fixes & Enhancements, not Breaking Changes.\n- `feat(docs)` goes in Docs when the user-facing surface is the docs site.\n- **Docs means the published docs site, not contributor files.** A `docs` commit that changes an RFC, `AGENTS.md`, the repo map, or a skill under `.claude/` belongs in Chore: a vite-plus user never reads those. Docs should hold only entries a reader could go and look at on the site or in the README.\n- **Describe behaviour, not resolution logic.** An entry states what a user now observes. Rules the implementation follows internally (target-selection signals, config precedence, detection order) belong in the RFC or the PR, not the changelog. If an entry needs a nested list to explain how a decision is reached, cut it down to the outcome.\n- **A breaking change needs its migration path.** State what existing installs or projects do by default, then how to move to the new behaviour deliberately, then what that costs. Link the guide rather than restating it, and say plainly when doing nothing is a valid choice.\n- There is no separate Highlights section. Lead Features with up to 5 new capabilities a vite-plus user will notice, plus security fixes, then the remaining features; the upstream upgrade entry stays last. Bug fixes go in Fixes & Enhancements even when they are prominent. Skip developer-tooling-only conveniences.\n- Entry format: `Description ([#N](https://github.com/voidzero-dev/vite-plus/pull/N)), by @author`. Describe the user-visible behavior, not the implementation. Group supporting implementation PRs under the user-visible change they enable instead of giving them separate entries. Never include defensive edge cases or internal mechanics unless users need them to use or understand the feature; use concrete behavior instead of internal UI taxonomy that needs extra context. For a fix to an intermittent failure, say that it was rare and when it happened, so the entry does not read as if it always failed. When a PR carries over a superseded PR from another author (its body says so, or `gh pr view N --json commits` lists their commits), credit both authors.\n- **Upstream dependency upgrade PRs** (`feat(deps): upgrade upstream dependencies`): consolidate all of them into one Features entry with net oldest-to-latest version changes (e.g. `vite 8.0.16 -> 8.1.2`), listing every PR number. Check the upgraded range for security fixes (search the upstream changelog for CVE/GHSA); if present, add a dedicated security entry quoting severity and linking the advisory. Cite only published advisories: a draft advisory that maintainer access lets you see is still embargoed, so keep it and its fix's security framing out of the notes and the announcement. When oxfmt or oxlint changed version, add one clause telling users the new versions can flag code that passed before, so they should run `vp fmt` after upgrading if their CI runs `vp check`; in ecosystem testing this is reliably the largest single class of post-upgrade CI failures. Put this entry last in Features as a parent bullet carrying the PR links and credits, with one sub-item per upgraded tool and one per user-facing consequence (new formatting or lint behavior, migration changes, compatibility notes); a single long line is hard to read.\n- **vite-task bumps** (`bump vite-task to <commit>`): expand the full rev range (compare `Cargo.toml` at `v<prev>` vs the release branch), run `git log <old>..<new>` in the local vite-task checkout, and read vite-task's `CHANGELOG.md` at the new commit for wording. Promote user-visible upstream changes into Features / Fixes with `[vite-task#N](https://github.com/voidzero-dev/vite-task/pull/N)` links, crediting the upstream PR author (`gh pr view N --repo voidzero-dev/vite-task --json author`). Cross-repo link format is `[vite-task#N]` / `[vite#N]`, not `[owner/repo#N]`.\n- New Contributors: copy from `generate-notes`, exclude bots (`renovate[bot]`, `voidzero-guard[bot]`, `github-actions[bot]`), list as inline `@mentions`. `generate-notes` only covers vite-plus, so also add first-time contributors from the expanded vite-task range: an author is new when `gh api -X GET search/issues -f q='repo:voidzero-dev/vite-task is:pr is:merged author:<login> merged:<<date of the range start>' -q .total_count` is 0. List them with the others, without a repository label, in the release notes and the Discord thanks line.\n\n### Bundled Versions table\n\n| Tool            | Version | Source                                                                  | Changelog                                                              |\n| --------------- | ------- | ----------------------------------------------------------------------- | ---------------------------------------------------------------------- |\n| vite            | `X.Y.Z` | [`<short-sha>`](https://github.com/vitejs/vite/commit/<full-sha>)       | [X.Y.Z](https://github.com/vitejs/vite/releases/tag/vX.Y.Z)            |\n| rolldown        | `X.Y.Z` | [`<short-sha>`](https://github.com/rolldown/rolldown/commit/<full-sha>) | [X.Y.Z](https://github.com/rolldown/rolldown/releases/tag/vX.Y.Z)      |\n| tsdown          | `X.Y.Z` | [npm](https://npmx.dev/package/tsdown/v/X.Y.Z)                          | [X.Y.Z](https://github.com/rolldown/tsdown/releases/tag/vX.Y.Z)        |\n| vitest          | `X.Y.Z` | [npm](https://npmx.dev/package/vitest/v/X.Y.Z)                          | [X.Y.Z](https://github.com/vitest-dev/vitest/releases/tag/vX.Y.Z)      |\n| oxlint          | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint/v/X.Y.Z)                          | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxlint_vX.Y.Z) |\n| oxlint-tsgolint | `X.Y.Z` | [npm](https://npmx.dev/package/oxlint-tsgolint/v/X.Y.Z)                 | [X.Y.Z](https://github.com/oxc-project/tsgolint/releases/tag/vX.Y.Z)   |\n| oxfmt           | `X.Y.Z` | [npm](https://npmx.dev/package/oxfmt/v/X.Y.Z)                           | [X.Y.Z](https://github.com/oxc-project/oxc/releases/tag/oxfmt_vX.Y.Z)  |\n\nvite and rolldown are built from pinned commits, so link the commit. The npm-installed tools link to npmx.dev. The Changelog column links the upstream release notes for every version in the upgraded range, comma-separated (for example the two patch releases between the previous and new pin); write `unchanged` when the version did not move.\n\n### Style rules\n\n- No em dashes or en dashes anywhere in the title or body. Use commas, colons, or parentheses.\n- Keep the PR title plain (`release: vX.Y.Z`) and the GitHub release title plain (`vite-plus vX.Y.Z`); the theme goes in the opening sentence. Lead the opening theme with the most important user-visible behavior. Avoid vague benefit-only wording that the body must explain.\n- When naming a package version in prose or a heading, use one inline literal, `package@version`. Keep separate version columns in tables.\n- Link unfamiliar technical abbreviations to the relevant documentation section; keep the short abbreviation as the link text.\n- The Upgrade section is a `vp upgrade` code block.\n- Apply via a temp file, never a heredoc (heredoc quoting can escape backticks inside the table and break rendering):\n\n```bash\ngh pr edit <PR#> --repo voidzero-dev/vite-plus --title \"release: vX.Y.Z\" --body-file /tmp/pr-body.md\n```\n\n### Validate before finishing\n\n```bash\nBODY=$(gh pr view <PR#> --repo voidzero-dev/vite-plus --json body -q '.body')\n# every generate-notes PR present (minus documented omissions), none duplicated:\necho \"$BODY\" | grep -oE 'voidzero-dev/vite-plus/pull/[0-9]+' | sort -u | wc -l\necho \"$BODY\" | grep -oE '(vite-plus|vite-task)/pull/[0-9]+' | sort | uniq -d   # must be empty\necho \"$BODY\" | grep -nE '[—–]'                                                  # must be empty\necho \"$BODY\" | grep -c '\\\\`'                                                    # must be 0 (escaped backticks)\necho \"$BODY\" | tail -1                                                          # boilerplate closing line intact\n```\n\nDiff the body's PR numbers against `generate-notes` rather than only counting them: a count alone hides one missing entry offsetting one extra. Every number in the missing list must have a documented reason for omission.\n\n## 4. Preview build smoke test (before merging)\n\nThis step runs **after the changelog (step 3) is complete and before merging (step 6)**. **Always ask the release manager whether to run it, and ask about both levels explicitly** (the local `vp migrate` sweep, and the fork-PR CI validation below); never silently skip either, and do not add the label on your own.\n\nDecide what to recommend before you ask, by counting the commits the release actually contains:\n\n```bash\ngit rev-list --count v<prev>..origin/main\n```\n\n**Recommend running the smoke test when that count is above 10**, or when the release touches migrate/create behavior, package-manager or install-path handling, or the native bindings, whatever the count. Recommend skipping only for a release that is both small (10 commits or fewer) and clear of those areas. State the count and your recommendation in the question so the release manager can overrule it, and say roughly what it costs, since the full catalog runs for hours.\n\nIf the release manager approves, **read and follow [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md) first**, then validate against the **full ecosystem-ci catalog** (every runnable fork), not a single project.\n\nIf the release manager says yes:\n\n1. Add the `preview-build` label to the release PR to publish installable `0.0.0-commit.<head-sha>` builds through the registry bridge:\n\n   ```bash\n   gh pr edit <PR#> --repo voidzero-dev/vite-plus --add-label \"preview-build\"\n   ```\n\n2. Wait for the `Publish preview build` workflow run on the release branch to succeed (it packs the built package directories and registers the commit with the registry bridge, then comments the build info on the PR).\n3. Verify the build against every runnable project in the catalog with the `test-pkg-pr-new-migrate` skill: it runs `vp migrate` from the preview commit against each local checkout, with dependencies resolved through the registry bridge. Report the outcome to the release manager before moving on.\n\n**The catalog.** The smoke-test catalog and the local-setup rules live in the ecosystem-ci org: [`vite-plus-ecosystem-ci/.github/TESTING.md`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/TESTING.md), with the machine-readable list in [`ecosystem.json`](https://github.com/vite-plus-ecosystem-ci/.github/blob/main/ecosystem.json) (each fork's upstream, tracked branch, and package manager). Run every runnable fork; filter `ecosystem.json` with `jq` to skip non-JS `other` repos and any the release manager says to ignore. Forks pinned to the immediately previous release exercise a real upgrade rather than a no-op.\n\n> **Mandatory: open any test PR against the `vite-plus-ecosystem-ci` fork, never the upstream repo.** `gh pr create` inside a fork defaults its base repo to the parent (upstream), so pass `--repo vite-plus-ecosystem-ci/<repo>` (or run `gh repo set-default vite-plus-ecosystem-ci/<repo>` first). See TESTING.md.\n\n`test-pkg-pr-new-migrate` needs a **local** checkout on the fork's **tracked branch** (often not the default branch, e.g. `vue-core` tracks `minor`). Clone under one directory so the whole test environment cleans up in one step:\n\n```bash\nrepo=<repo>; branch=<tracked-branch>   # from ecosystem.json\ngit clone git@github.com:vite-plus-ecosystem-ci/$repo.git ~/git/github.com/vite-plus-ecosystem-ci/$repo\ngit -C ~/git/github.com/vite-plus-ecosystem-ci/$repo checkout \"$branch\"\n# ... run the harness against ~/git/github.com/vite-plus-ecosystem-ci/$repo ...\n# cleanup after the release: rm -rf ~/git/github.com/vite-plus-ecosystem-ci\n```\n\nThe `.github` repo also ships `scripts/setup-local.sh <repo>` (or `--all`), which does the clone, tracked-branch checkout, remotes, and fork base-repo pinning from the manifest in one step.\n\n**Sync every fork to upstream before you test anything.** The forks drift, often by hundreds of commits, so a checkout straight from `origin` validates stale code and any PR you open against it carries all that drift instead of just the upgrade. From the `.github` checkout, run the safe sync command before the local sweep:\n\n```bash\nscripts/sync-forks.sh --all\n```\n\nThe command fast-forwards only forks with no fork-only commits. Exit code `2` means that at least one fork needs a sync or manual work. Resolve or explicitly skip each reported fork. Never overwrite a divergent tracked branch.\n\nRun `scripts/sync-forks.sh \"$repo\"` again immediately before you open each fork PR. If it prints `REOPEN`, close and reopen the current release PR so GitHub calculates a new merge base. Close superseded PRs and delete their branches. TESTING.md carries the full procedure.\n\n**Validate in the project's own CI.** Beyond the local `vp migrate`, exercise the prerelease in the fork's real CI by opening a draft PR on the fork, following \"Smoke-test via a fork PR\" in TESTING.md: branch `update-vite-plus-prerelease-test-<version>` synced from `source`, apply the upgrade, open a **draft** PR on the fork (never upstream) **assigned to the release manager**, then watch its checks for upgrade-related failures. Offer this alongside the local sweep rather than treating it as an afterthought; it is the only level that exercises each project's own build and tests. Some projects' CIs install with a non-standard tool that cannot resolve preview builds through the bridge `.npmrc` (e.g. cnpmcore's `utoo`), so check the install step before trusting fork-CI results.\n\nThe workflow triggers only on the `labeled` event, not on new pushes. To rebuild after the head moves (e.g. after a step 5 merge from `main`), remove and re-add the label (this cancels an in-flight build for the branch). A stale build whose diff to the new head is test-only is still valid for smoke testing; ask before re-triggering.\n\nRecord each project's base commit, starting version, resolved preview commit, fork PR head, and CI run. After a new preview build, identify which projects ran again and which retain earlier evidence. Do not report a partial repeat as a full-catalog run on the new preview.\n\n### Example (v0.2.2, PR #2016)\n\nChangelog complete, CI green, release manager approved the smoke test. A build existed for head `06708538`; the head had since moved by a test-only merge from `main`, so that build was still valid and was not re-triggered.\n\nHere the target was vibe-dashboard `main` (a `vite-plus-ecosystem-ci` fork, pnpm monorepo on `vite-plus 0.2.1`, i.e. the previous release), so the run exercised the common upgrade path. Pass the release PR number; the harness resolves it through the bridge to the latest published immutable commit and prints the resolved SHA (confirm it matches the build you expect). Pass a full commit SHA instead only to pin a specific build when several have been published:\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh 2016 ~/git/github.com/vite-plus-ecosystem-ci/vibe-dashboard --no-interactive\n```\n\nA passing run looks like:\n\n```\n◇ Updated . to Vite+ 0.0.0-commit.06708538...\n• Dependencies:\n    vite-plus  0.2.1 → 0.0.0-commit.06708538...\n    vite             → 8.1.2\n✓ Dependencies installed in 5.7s\n\nMigration worktree changes (.npmrc force-staged so it survives .gitignore):\nA  .npmrc                      # bridge registry written by vp migrate\n M package.json / pnpm-workspace.yaml / pnpm-lock.yaml\n\nFound 1 version of @voidzero-dev/vite-plus-core\nFound 1 version of vite-plus\nFound 1 version of vitest\n```\n\nPass criteria: the upgrade lands on the `0.0.0-commit.<sha>` build, the install succeeds through the bridge registry, and each of `@voidzero-dev/vite-plus-core`, `vite-plus`, and `vitest` resolves to exactly ONE version (`vitest` at the bundled upstream version). Multiple or stale versions mean the migration or install is broken: stop and treat it as a release blocker. Report the outcome to the release manager either way.\n\nOne exception to rule out first: `0.0.0-commit.<sha>` is a prerelease, and a `*` range does not match prereleases. A package that declares an optional `vite-plus: '*'` peer (oxlint and oxfmt do) can therefore keep a stale `vite-plus` from the old lockfile under pnpm, so the check reports two versions. If the previous-release control resolves to one version and the extra copy hangs only off such a peer, the duplicate is a preview-build artifact.\n\n### Triaging failures across the catalog\n\nAcross the full catalog most failures are not regressions, and reporting them as \"N failed\" without triage is useless to the release manager. Sort every failure into one of these before drawing any conclusion:\n\n- **Registry-bridge fetch flakes.** Check these first, because they are common and they masquerade as something far worse. The bridge drops tarball requests under load: pnpm logs `error (23). Will retry`, or the install dies with `ECONNRESET  aborted`. The dangerous case is a platform binding, because `@voidzero-dev/vite-plus-<platform>` is an **optional** dependency: when its download exhausts the retries, the installer skips it and still reports success, and the job then fails much later at the first command that loads the binding, with `Cannot find native binding` / `Cannot find module '@voidzero-dev/vite-plus-linux-x64-gnu'`. That is a local `node_modules` resolution failure and says nothing about the registry, but NAPI's loader appends generic \"npm has a bug related to optional dependencies\" boilerplate that reads like a publishing problem. Do not conclude the addon was unpublished; the bridge publishes every platform package for every commit build. Confirm, then re-run:\n\n  ```bash\n  curl -s \"https://registry-bridge.viteplus.dev/@voidzero-dev%2fvite-plus-linux-x64-gnu\" \\\n    | python3 -c \"import json,sys; print('0.0.0-commit.<sha>' in json.load(sys.stdin)['versions'])\"\n  gh run rerun <run-id> --failed --repo <owner>/<repo>\n  ```\n\n  Grep every failing log for `error (23)` and `ECONNRESET` before classifying it as anything else. In one release this single cause accounted for 8 fork failures, all of which passed on re-run.\n\n- **Local TLS inspection.** If every package fails locally with `ERR_PNPM_META_FETCH_FAIL ... fetch failed` while `curl` to the bridge succeeds, a TLS-inspecting client (such as a corporate zero-trust agent) may be re-signing `registry-bridge.viteplus.dev` with a root that the OS trusts but Node does not. `node -e \"fetch('https://registry-bridge.viteplus.dev/vite-plus')\"` then fails with `SELF_SIGNED_CERT_IN_CHAIN`. Export that root certificate and set `NODE_EXTRA_CA_CERTS` for the harness and every control run. A follow-up \"latest release is ...\" message comes from pnpm's stale metadata cache and is a symptom, not the cause.\n\n- **Preview-build artifacts.** These are caused by the `0.0.0-commit.<sha>` version string itself and cannot happen for a real npm release, so they are never blockers. The recurring ones: pnpm `ERR_PNPM_TRUST_DOWNGRADE` (\"possible package takeover\"), npm `ETARGET` from a `before`/min-release-age policy, bun `minimum release age`, `ERR_PNPM_INVALID_PEER_DEPENDENCY_SPECIFICATION` when a project declares `vite` as a peer (migrate writes the `npm:@voidzero-dev/vite-plus-core@...` alias there), `ERR_PNPM_TARBALL_URL_MISMATCH` or a failed supply-chain policy check against the bridge tarball URLs, Docker builds whose context does not carry the bridge `.npmrc`, project scripts that query `vite@*` or `vite-plus@*` and find nothing (the prerelease does not match `*`), and npm 10 failing with `Unable to resolve reference $vite-plus` on a nested `overrides` entry. Confirm the last two by swapping the commit version for a real release in a scratch copy.\n- **Pre-existing failures.** Prove it rather than asserting it, with whichever control is cheaper: install the previous release into an isolated home and re-run the same command, or check whether the fork's base branch CI already fails. The isolated-home control is the highest-value technique in this step, since it converts a scary-looking failure into a one-line fact:\n\n  ```bash\n  VP_HOME=$HOME/.cache/vp-control-<prev> VP_VERSION=<prev> VP_NODE_MANAGER=no \\\n    VP_SELF_SETUP_NO_MODIFY_PATH=1 bash packages/cli/install.sh\n  cd <project> && VP_HOME=$HOME/.cache/vp-control-<prev> VP_NODE_MANAGER=no \\\n    PATH=\"$HOME/.cache/vp-control-<prev>/bin:$PATH\" vp migrate <project> --no-interactive\n  ```\n\n  `VP_SELF_SETUP_NO_MODIFY_PATH=1` keeps the control installation out of the user's shell profiles; without it, every new shell sets `VP_HOME` to the control.\n\n  **Run the control from inside the project directory.** Launching it from the vite-plus checkout makes `vp` delegate to that checkout's `packages/cli/dist` instead of the pinned release, which silently invalidates the comparison (it fails with an unrelated error such as `Fail to parse yaml as RuleConfig`).\n\n  Run candidate and control in isolated checkouts of the same project base, starting with the same lockfile and no `node_modules`. Compare any lockfile changes made by migration so unrelated dependency versions do not invalidate the control. When the control reproduces the failure, report the evidence (same exit code and error class).\n\n- **Stale pins: weight the forks that were actually on the previous release.** A fork pinned several releases back does not test the release under review at all; `vp migrate` performs a multi-release jump, and any resulting type errors are evidence about that jump. Before drawing a conclusion, work out what each fork was on and judge the release primarily on the forks upgrading from the immediately previous release. Derive the pin from the upgrade commit itself, not from `HEAD`, since later commits on the test branch hide it:\n\n  ```bash\n  sha=$(git -C <dir> log --format=%H --grep='^test: upgrade vite-plus to prerelease' -n 1)\n  git -C <dir> show \"$sha\" | grep -E '^-.*vite-plus'\n  ```\n\n  Parse every YAML document when auditing pnpm lockfiles. Importers and package resolutions can be in separate documents; a single-document parser can miss the resolved versions.\n\n  Report that subset separately; \"2 of the 7 forks on the previous release pass, the other 5 fail on fork infrastructure\" is a far stronger statement than a headline pass rate over the whole catalog.\n\n- **Project-side and infra failures.** Dependency conflicts between the project's own packages, missing fork secrets, third-party GitHub Apps not installed on the fork, network timeouts. Retry once before classifying anything as a network failure; they pass on retry. Two recurring shapes worth naming: a package that imports a dependency it never declared and only ever resolved through hoisting (`Cannot find package 'oxfmt'`) breaks as soon as the harness regenerates the lockfile; and a project whose own dependency has no `main`/`module`/`exports` cannot load its config under any vite-plus version.\n- **Dependency drift during migration.** Regenerating a lockfile can move unrelated floating or nightly dependencies to incompatible versions. Compare with the base lockfile before blaming the candidate. On the test branch, retain the original versions and their dependency graph, then verify a frozen install and rerun the failing command.\n- **Custom quality checks.** Check that project wrappers still load their plugins and recognize migrated test imports. Preserve existing lint diagnostic coverage when repairing migration issues; a smaller baseline can mean that checks stopped running.\n- **Release-age gates on fresh dependencies.** A project's `minimumReleaseAge` can reject packages that Vite+ itself pins and that were published shortly before the release. That is the project's policy, not a vite-plus bug: add the package to `minimumReleaseAgeExclude` on the test branch, and do not propose extending migrate's exemption list. Real installs of the release hit the same gate until those packages age past the project's window.\n- **Harness artifacts.** Failures your own test setup caused, such as a lockfile the harness deleted and the install never regenerated. Fix these and re-run rather than reporting them.\n\nReport the tally by cause, not just pass/fail, and state plainly which failures you controlled for and which you classified from the error text alone. Only a failure that reproduces on the candidate but not on the previous release is a regression.\n\nBefore filing an upstream issue for a finding, search every repository that could own it, including closed issues and open PRs. For a type-aware oxlint diagnostic that means both `oxc-project/oxc` and `oxc-project/tsgolint`.\n\nWhen repairing timing-sensitive smoke tests, keep their assertions and make readiness or timing deterministic. Use a negative control when changing how a test observes behavior: temporarily remove or break that behavior, confirm the test fails, and restore it before committing.\n\nTwo long-run mechanics worth knowing: `vp migrate` installs Vite+ git hooks in the project, so any later `git commit`/`git push` there needs `--no-verify`; and macOS has no GNU `timeout`, so a driver script that time-boxes runs needs its own watchdog. If that driver runs projects in parallel, kill the whole process tree on timeout, not just the wrapper: an orphaned `pnpm install` holds the store lock and the next project then hangs at 0% CPU with no output, which reads like a vite-plus hang and is not one. Concurrent installs that share one store can stall the same way without any orphan, with one process idle while holding the store's `index.db`; rerun the stuck project alone before treating it as a hang.\n\nTwo fork-CI blockers are worth fixing rather than reporting, both on the **test branch only** so the tracked branch stays clean against upstream. A fork whose workflows never trigger on `pull_request` reports \"no checks\" and proves nothing: add a minimal workflow that runs `vp run build` through whatever setup the project already uses. A fork whose workflows target third-party runners (self-hosted labels such as `blacksmith-*`) queues every job forever, because those labels only resolve for the upstream org: map them to GitHub-hosted equivalents, replacing the longest label first so an `-arm` suffix is not left half-rewritten. Runner-specific _actions_ need more than a label swap and are usually not worth fixing. The same applies to `depot-*` and `namespace-profile-*` labels. GitHub-hosted runners are smaller than most of these, so heavy suites can start timing out after the swap; classify those timeouts as fork infrastructure.\n\nClosing a cycle's PRs with `--delete-branch` removes their test branches, but each closed PR's commits stay reachable at `refs/pull/<N>/head`. Find the previous cycle's PR per repository (`gh pr list --repo vite-plus-ecosystem-ci/<repo> --state all`); an org-wide `gh search` stops at 100 results and silently misses forks. Before a new sweep, list the non-upgrade commits on the previous cycle's PRs (supply-chain exemptions, runner-label maps, added workflows), fetch them, and cherry-pick them onto the new test branches. Re-apply by hand when upstream drift makes a cherry-pick conflict. Commits made after the upgrade commit (formatting of migrate-written files, regenerated lockfiles, restored dependencies) only apply after migrating: retry them after the new upgrade commit, take the new lockfile when only lockfiles conflict, and drop a formatting commit that would add migrate-generated `package.json` or workspace keys before migrate runs.\n\n## 5. Release-branch CI\n\nMatch checks to the current PR head and the latest applicable workflow runs. Superseded canceled runs can leave failed aggregate checks in the PR rollup. Check required statuses with `gh pr checks <PR#> --required`, and report required reviewer approval separately from technical CI readiness.\n\nFixes for CI failures go through a **separate PR to `main`**, never as commits on the release branch (the release-version syncs in step 2 are the exceptions). After the fix PR merges:\n\n```bash\ngit checkout release/vX.Y.Z && git merge origin/main --no-edit && git push origin release/vX.Y.Z\n```\n\nDo not assume the merge brought in only the fix PR: `main` may have accumulated several. Before merging, list everything that will come in with `git log origin/release/vX.Y.Z..origin/main --oneline`, then add a changelog entry for **every** newly included PR and rerun the step 3 validation (its missing/extra diff against `generate-notes` catches any entry you missed). When the merged commits change bundled tool versions or `vp migrate` behavior, rebuild the preview (step 4) and repeat the smoke test on the new build: start each fork from its current test branch so earlier fixes carry over, open new fork PRs, close the superseded ones, and report results per build.\n\nKnown release-branch-only failure modes:\n\n- **Binding version drift**: CI's no-unexpected-changes guard reports a diff flipping version strings in `binding/index.cjs`. Fix: step 2.\n- **Registry flakes**: registry-bound fixtures can time out (about 50s) and look like regressions. Rerun before diagnosing, and never commit a `[timeout]` snapshot.\n- **New RustSec advisories**: Security Analysis runs cargo-deny only when `Cargo.lock` changes, and the version bump always changes it. An advisory published since `main` last touched `Cargo.lock` therefore fails first on the release PR. The check is not required; trace which dependency pulls the crate in and let the release manager decide whether to fix it before or after the release. Before opening a fix PR, check open dependency-update PRs (Renovate): one may already bump the crate and clear the check.\n\n## 6. Merge\n\nMerging the release PR is the release trigger. Before merging confirm: CI green, changelog validated, binding and documentation versions synced (including all three prompts), and (if used) the preview build verified.\n\nWhen the release publishes an npm package for the first time, confirm before merging that the package's npm settings list a trusted publisher for `voidzero-dev/vite-plus`, workflow `release.yml`, environment `release`. A missing or mismatched entry fails that publish with `ERR_PNPM_AUTH_TOKEN_EXCHANGE ... (status code 404)` after `vite-plus` is already on `latest`, and the GitHub release, Docker image, docs deployment, and Discord notification are skipped until the job is rerun.\n\nAuto-merge being enabled is not a completed merge. Confirm `mergedAt` and the merge commit, then follow the Release run for that commit; older successful runs can have skipped publishing because the version did not change.\n\n## 7. Automated release pipeline (what happens after merge)\n\n`release.yml` runs on the `main` push because `packages/cli/package.json` changed:\n\n1. `check`: compares the local version against `unpkg.com/vite-plus@latest`; everything below is skipped unless it changed.\n2. `build-rust`: full multi-platform build.\n3. `request-approval`: posts an approval request to the releases Discord channel, and the `Release` job waits on the `release` GitHub environment. **A person with environment approval rights must approve the run in the Actions UI.** The environment sets `prevent_self_review: true`, so whoever merged the release PR triggered the run and cannot approve it: a _different_ reviewer must. Check who can, and tell the release manager rather than leaving them waiting on themselves:\n\n   ```bash\n   gh api repos/voidzero-dev/vite-plus/actions/runs/<run-id>/pending_deployments \\\n     -q '.[] | \"\\(.environment.name) can_approve=\\(.current_user_can_approve) reviewers=\\([.reviewers[]?.reviewer.login] | join(\",\"))\"'\n   ```\n\n4. `Release`: publishes the NAPI bindings (`@voidzero-dev/vite-plus-<platform>`) and standalone CLI packages (`@voidzero-dev/vite-plus-cli-<platform>`, via `packages/cli/publish-native-addons.ts`), then `@voidzero-dev/vite-plus-core`, `vite-plus`, and `create-vite-plus` to npm (`--tag latest`). Each dependency tier waits for npm propagation before publication advances. It then creates the `vX.Y.Z` GitHub release (draft, with installer/binary assets, then undrafted). The generated body has only Published Packages and Installation sections.\n5. `publish-docker`: multi-arch toolchain image to `ghcr.io/voidzero-dev/vite-plus`, after npm publish (the image installs vp from npm).\n6. `deploy-docs`: deploys the production docs after a stable release is published. It is skipped for prereleases. When a prerelease is published to `latest` and its notes or CLI messages link to docs that production does not serve yet, ask the release manager whether to run `gh workflow run deploy-docs.yml --ref <ref>` once the `Release` job is publishing. Use the `vX.Y.Z` tag, or `main` while it still points at the release commit, and confirm the run's head SHA. The `vp` version that builds the docs does not need to match the release: the site and its install scripts come from the checked-out commit. The job authenticates with the `VOID_TOKEN` repository secret. When that token has expired, the job fails with `` `VOID_TOKEN` is invalid or expired `` and `discord-notify` is skipped. Ask someone with access to rotate the secret, then run `gh run rerun <run-id> --failed` to rerun the docs deployment and the Discord notification.\n7. `discord-notify`: announces to Discord after Docker publishing and docs deployment succeed (docs are skipped for prereleases).\n\n**When the `Release` job fails partway, fix the cause, then use \"Re-run failed jobs\", never \"Re-run all jobs\".** A full rerun repeats `check`, which now sees the new version on `unpkg.com/vite-plus@latest` and skips the whole release. A failed-jobs rerun reuses `check` and the build artifacts, and every npm publish step is idempotent: `.github/scripts/publish-npm-package.ts` skips versions the registry already serves (`<pkg>@X.Y.Z is already published; skipping upload.`), so only the unpublished packages upload. Confirm that from the rerun log. The rerun waits on the `release` environment again, and whoever started the rerun cannot approve it.\n\n**A successful publish command does not mean the packages are installable.** `pnpm publish` prints `✅ Published package <name>@X.Y.Z` as soon as the registry accepts the request, and the registry can then take tens of minutes to actually serve that version. This has shipped a broken release: `vite-plus@X.Y.Z` went live on `latest` with an exact dependency on `@voidzero-dev/vite-plus-core@X.Y.Z` that was invisible for about 35 minutes, so every `npm install vite-plus` failed with `ETARGET` and both `publish-docker` and `Deploy docs` failed on `ERR_PNPM_NO_MATCHING_VERSION`. The downstream job failures are the symptom, not the cause; do not re-run them until the registry has the package.\n\nCheck visibility directly, not through `npm view`, which caches:\n\n```bash\nfor pkg in '@voidzero-dev%2Fvite-plus-core' 'vite-plus'; do\n  curl -s -H 'Cache-Control: no-cache' \"https://registry.npmjs.org/$pkg?t=$(date +%s)\" |\n    python3 -c \"import json,sys;d=json.load(sys.stdin);print('$pkg', d['dist-tags'].get('latest'), 'X.Y.Z' in d['versions'])\"\ndone\n```\n\nBoth must report `True` before you trust the release. A stale `modified` timestamp on the packument is the giveaway that nothing landed. If `vite-plus` is visible and `core` is not, the release is broken **right now** for every new install: tell the release manager immediately and offer to move the tag back (`npm dist-tag add vite-plus@<prev> latest`) while the publish is sorted out. Confirm the fix with a real install in a temp directory, not just a registry read:\n\n```bash\nd=$(mktemp -d); cd \"$d\" && npm init -y >/dev/null && npm install vite-plus@X.Y.Z --no-audit --no-fund\n```\n\nThe full package document can update before npm's separately cached installation metadata. The workflow's `.github/scripts/wait-for-npm-packages.ts` checks that installation metadata and the referenced tarball, then waits for settlement. Use that script to reproduce its availability check; local visibility does not prove the workflow runner sees the same state.\n\nLocal clients can also mislead in the other direction. npm and pnpm cache a lookup made before the version landed, so pass `--prefer-online` when a just-published version reports `ETARGET`/`notarget` locally. pnpm 11 and later default to a one-day `minimumReleaseAge`, so `pnpm create vite-plus` (an unpinned `pnpm dlx create-vite-plus`) resolves to the previous `create-vite-plus` until the new version is a day old; verify it with `--config.minimumReleaseAge=0`.\n\n## 8. Post-release\n\n1. **Polish the GitHub release notes** (ask first): the auto-created release body has only Published Packages and Installation. Build the polished notes from the final release PR body:\n   - Drop the closing `---` / `Merging this PR ...` boilerplate.\n   - Preserve every changelog section through **Full Changelog**, including any later revisions requested by the release manager.\n   - Append the generated Published Packages and Installation sections, omit the redundant `View the full commit` line, and end Installation with a Docker usage block (keep the explanation to one short sentence):\n\n     ````markdown\n     **Docker:**\n\n     ```bash\n     docker run --rm -it -v \"$PWD:/app\" -w /app ghcr.io/voidzero-dev/vite-plus:X.Y.Z vp build\n     ```\n\n     Run any `vp` command without installing it; see the [Docker guide](https://viteplus.dev/guide/docker) for more.\n     ````\n\n   - After Installation, add a **Create a New App** section with the pnpm example only:\n\n     ````markdown\n     ### Create a New App\n\n     Start a project without installing the global `vp`: `create-vite-plus` runs `vp create` through your package manager.\n\n     ```bash\n     pnpm create vite-plus vite:application --directory my-app\n     cd my-app && pnpm exec vp dev\n     ```\n\n     Run the command without arguments to pick a template interactively, and see the [create guide](https://viteplus.dev/guide/create#without-the-global-cli) for other package managers, templates, and options.\n     ````\n\n   - **Present the draft to the release manager and apply only after approval.** Before review, write the complete draft to a temporary Markdown file with the proposed release title at the top and the full body below it. Update that file after every requested revision; do not treat chat excerpts as the canonical draft. After approval, use a body-only notes file (without the review title) to retitle the release and apply the notes:\n\n     ```bash\n     gh release edit vX.Y.Z --repo voidzero-dev/vite-plus \\\n       --title \"vite-plus vX.Y.Z\" --notes-file /tmp/release-notes.md\n     ```\n\n   - Keep the review draft, body-only notes file, and live release aligned after requested edits. Read back the live title and body to verify the update. Normalize CRLF and LF before comparing the approved file with the live body, because GitHub can change line endings. Re-run the step 3 validation greps, plus `grep -c 'Merging this PR'` (must be 0).\n   - For a SemVer prerelease, confirm that GitHub marks the release as a prerelease. The automated release can create a prerelease tag without setting that flag. Add `--prerelease` when applying the approved notes, then verify `isPrerelease` with `gh release view vX.Y.Z --json isPrerelease`.\n\n2. **Verify**:\n\n   ```bash\n   npm view vite-plus version                       # X.Y.Z\n   npm view @voidzero-dev/vite-plus-core version    # X.Y.Z\n   npm view create-vite-plus version                # X.Y.Z\n   npm vi\n\n<!-- [TRUNCATED AT 50KB] -->\n\n<!-- File: .claude/skills/spawn-process/SKILL.md (Tokens: ~661 | Category: anthropic-skill) -->\n---\nname: spawn-process\ndescription: Guide for writing subprocess execution code using the vp_command crate\nallowed-tools: Read, Grep, Glob, Edit, Write, Bash\n---\n\n# Add Subprocess Execution Code\n\nWhen writing Rust code that needs to spawn subprocesses (resolve binaries, build commands, execute programs), always use the `vp_command` crate. Never use `which`, `tokio::process::Command::new`, or `std::process::Command::new` directly.\n\n## Available APIs\n\n### `vp_command::resolve_bin(name, path_env, cwd)` — Resolve a binary name to an absolute path\n\nHandles PATHEXT (`.cmd`/`.bat`) on Windows. Pass `None` for `path_env` to search the current process PATH.\n\n```rust\n// Resolve using current PATH\nlet bin = vp_command::resolve_bin(\"node\", None, &cwd)?;\n\n// Resolve using a custom PATH\nlet custom_path = std::ffi::OsString::from(&path_env_str);\nlet bin = vp_command::resolve_bin(\"eslint\", Some(&custom_path), &cwd)?;\n```\n\n### `vp_command::build_command(bin_path, cwd)` — Build a command for a pre-resolved binary\n\nReturns `tokio::process::Command` with cwd, inherited stdio, and `fix_stdio_streams` on Unix already configured. Add args, envs, or override stdio as needed.\n\n```rust\nlet bin = vp_command::resolve_bin(\"eslint\", None, &cwd)?;\nlet mut cmd = vp_command::build_command(&bin, &cwd);\ncmd.args(&[\".\", \"--fix\"]);\ncmd.env(\"NODE_ENV\", \"production\");\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::build_shell_command(shell_cmd, cwd)` — Build a shell command\n\nUses `/bin/sh -c` on Unix, `cmd.exe /C` on Windows. Same stdio and `fix_stdio_streams` setup as `build_command`.\n\n```rust\nlet mut cmd = vp_command::build_shell_command(\"echo hello && ls\", &cwd);\nlet mut child = cmd.spawn()?;\nlet status = child.wait().await?;\n```\n\n### `vp_command::run_command(bin_name, args, envs, cwd)` — Resolve + build + run in one call\n\nCombines resolve_bin, build_command, and status().await. The `envs` HashMap must include `\"PATH\"` if you want custom PATH resolution.\n\n```rust\nlet envs = HashMap::from([(\"PATH\".to_string(), path_value)]);\nlet status = vp_command::run_command(\"node\", &[\"--version\"], &envs, &cwd).await?;\n```\n\n## Dependency Setup\n\nAdd `vp_command` to the crate's `Cargo.toml`:\n\n```toml\n[dependencies]\nvp_command = { workspace = true }\n```\n\nDo NOT add `which` as a direct dependency — binary resolution goes through `vp_command::resolve_bin`.\n\n## Exception\n\n`crates/vp_global_cli/src/shim/exec.rs` uses synchronous `std::process::Command` with Unix `exec()` for process replacement. This is the only place that bypasses `vp_command`.\n\n<!-- File: .claude/skills/sync-tsdown-cli/SKILL.md (Tokens: ~288 | Category: anthropic-skill) -->\n---\nname: sync-tsdown-cli\ndescription: Sync tsdown runtime CLI options with vp pack after a tsdown upgrade. Use for option forwarding changes; use sync-upstream-cli-help for static help wording.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync tsdown CLI\n\nRuntime options live in `packages/cli/src/pack-bin.ts`; static help lives in\n`packages/cli/src/help.ts`.\n\n1. Run `npx tsdown --help` from `packages/cli/` and compare it with `pack-bin.ts`.\n2. Add new forwarded options using the existing cac `.option()` pattern. For removed\n   options, add `// NOTE: removed from tsdown CLI in vX.Y.Z` for reviewer follow-up.\n3. Preserve runtime differences: `-c, --config` stays disabled because Vite+ uses\n   `vite.config.ts`, and `--env-prefix` keeps the `['VITE_PACK_', 'TSDOWN_']` default.\n4. For static labels and descriptions, follow `sync-upstream-cli-help`; do not adapt\n   upstream wording to explain runtime differences.\n5. Run `pnpm --filter vite-plus build-ts` and `vp pack -h`. Add a focused PTY snapshot\n   case under `crates/vp_cli_snapshots/tests/cli_snapshots/fixtures/` when a new runtime\n   option is exposed.\n\n<!-- File: .claude/skills/sync-upstream-cli-help/SKILL.md (Tokens: ~538 | Category: anthropic-skill) -->\n---\nname: sync-upstream-cli-help\ndescription: Sync Vite+'s static CLI help with Vite, Vitest, Oxlint, Oxfmt, and tsdown while preserving intentional omissions. Use when an upstream dependency upgrade changes CLI help.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream CLI help\n\n## Input and target\n\n- Read the diff from `$CLI_HELP_DIFF_REPORT`; first run\n  `test -r \"$CLI_HELP_DIFF_REPORT\"`. Act only when `$CLI_HELP_DIFF_CHANGED` is\n  `true`.\n- Treat the upgraded tool's `--help` output as the source of truth. The report locates\n  changes and versions; rerun the exact version when its diff is truncated.\n- Edit `commandHelpDocs` in `packages/cli/src/help.ts`.\n- Do not edit `packages/cli/src/utils/help.ts` for content drift. It owns terminal\n  wrapping, alignment, and the right margin.\n\n| Upstream help         | Document entry |\n| --------------------- | -------------- |\n| `vite --help`         | `dev`          |\n| `vite build --help`   | `build`        |\n| `vite preview --help` | `preview`      |\n| `vitest --help`       | `test`         |\n| `oxlint --help`       | `lint`         |\n| `oxfmt --help`        | `fmt`          |\n| `tsdown --help`       | `pack`         |\n\n## Change\n\n- For items Vite+ exposes, copy upstream labels, descriptions, section titles, and\n  section guidance exactly. Preserve intentional lines and lists, but not terminal\n  padding, automatic wrapping, ANSI color, or version banners.\n- Remove an upstream item only after confirming Vite+ no longer supports or\n  deliberately retains it.\n\n## Do not change\n\n- Keep Vite+-owned usage, summaries, examples, and documentation URLs.\n- Keep config selectors/loaders hidden, including `--config`, `--configLoader`, and\n  `--disable-nested-config`.\n- Do not add standalone modes such as `--init`, `--migrate`, or `--lsp`, top-level\n  `--version`, or options Vite+ does not forward.\n- Do not change runtime forwarding or rewrite upstream wording for a Vite+-specific\n  runtime default. Use `sync-tsdown-cli` for tsdown runtime changes.\n\nRe-record the affected CLI help snapshots and inspect their diffs. Do not modify help\ndocuments when the report contains no actionable exposed change.\n\n<!-- File: .claude/skills/sync-upstream-dependency-docs/SKILL.md (Tokens: ~272 | Category: anthropic-skill) -->\n---\nname: sync-upstream-dependency-docs\ndescription: Sync exact upstream dependency versions embedded in Vite+ documentation after the automated dependency upgrade. Use when bundled Vite, Vitest, Oxlint, Oxfmt, tsdown, Rolldown, or related package versions change.\nallowed-tools: Read, Grep, Glob, Edit, Bash\n---\n\n# Sync upstream dependency docs\n\n1. Read `$UPGRADE_DEPS_META_DIR/versions.json` and consider only entries whose `old`\n   and `new` values differ.\n2. Search `README.md`, `packages/*/README.md`, and `docs/**/*.md` for each changed\n   package name and its old exact version. Do not treat changelogs, RFC examples,\n   snapshots, or broad ranges such as `vitest@4.x` as current-version references.\n3. Update references that promise to match Vite+'s currently bundled version.\n   Manual-installation examples use placeholders for the `vite-plus` and Vitest\n   versions, so they do not need updates.\n4. Preserve the surrounding wording and formatting. Do not rewrite examples whose\n   version is intentionally historical or illustrative.\n5. Re-run the searches for the changed packages and inspect the focused diff. No\n   stale exact version may remain in documentation that describes the current bundle.\n\n<!-- File: .claude/skills/test-pkg-pr-new-migrate/SKILL.md (Tokens: ~766 | Category: anthropic-skill) -->\n---\nname: test-pkg-pr-new-migrate\ndescription: Verify a preview (registry bridge) build of vite-plus against a real project before release — run `vp migrate` from the preview commit against a local project, deps resolved through the registry bridge. Use when asked to verify/e2e-test a preview or pkg-pr-new build against a project, \"test PR #<N> on <project>\", or check a prerelease against a repo.\nallowed-tools: Bash, Read\n---\n\n# Verify a preview build against one project\n\nInstalls an isolated global `vp` built from a registry bridge commit build and runs `vp migrate` on a specified local project. The global CLI and the migrated project both pin `vite-plus`/`vite` to the clearly-defined `0.0.0-commit.<sha>` build. `vp migrate` itself writes the bridge registry into the project's `.npmrc` (or `.yarnrc.yml` for Yarn Berry) so the deps resolve, during this run and in the project's own CI; this script only force-stages that file past `.gitignore`.\n\nRequired inputs: a `<PR-or-SHA>` (the build to verify) and a `<project-path>`. If either is missing from the request, **ask the user** for it — never guess the PR/SHA, as the build under test is the user's choice.\n\n```bash\n.github/scripts/test-pkg-pr-new-migrate.sh <PR-or-SHA> <project-path> [migrate-options...]\n# e.g.\n.github/scripts/test-pkg-pr-new-migrate.sh 1891 /path/to/npmx.dev --no-interactive\n```\n\n- First arg is a PR number or commit SHA; the script resolves the immutable commit via the bridge `x-commit-key` header and verifies the bridge serves it (the preview publish workflow, triggered by the `preview-build` label, registers each commit).\n- Never touches `~/.vite-plus`; installs the project's committed dependencies before migrating, because `vp migrate` reads the original Vitest version from the lockfile or `node_modules`; refuses a dirty worktree unless `ALLOW_DIRTY=1`; prints the project's `git status`/`diff` at the end — inspect that to confirm the migration result.\n\n**The build under test must include the \"migrate writes the bridge registry\" feature** (this session's work / current branch head onward). The harness no longer writes the registry itself — it relies on `vp migrate` doing it. Testing an older build with this harness would leave the project with no bridge registry, so its deps resolve from npmjs (`ERR_PNPM_NO_MATCHING_VERSION` on the `0.0.0-commit.<sha>` version). Always verify a fresh build of the branch, not a stale published commit.\n\nThe script prints the resolved versions at the end, querying one package at a time (npm/yarn/bun `why` only accept a single package; `-r` is pnpm-only). Each must resolve to exactly ONE version: `vite-plus` and `vite` at the expected `0.0.0-commit.<sha>` (vite via the `@voidzero-dev/vite-plus-core` alias), `vitest` at the bundled upstream version. Multiple versions, or a stale/wrong version, means the migration or install is broken. To re-check by hand:\n\n```bash\ncd <project-path>\n# pnpm: one call, recursive across workspaces\nvp why -r vite-plus vite vitest\n# npm / yarn / bun: one package per call, no -r\nvp why vite-plus && vp why vite && vp why vitest\n```\n\n<!-- File: .claude/skills/verify-interactive-cli/SKILL.md (Tokens: ~1003 | Category: anthropic-skill) -->\n---\nname: verify-interactive-cli\ndescription: Drive and capture vp's interactive (clack) prompts in a tmux session to manually verify interactive UX and catch spinner-over-prompt bugs. Use when asked to test/verify/capture an interactive vp command's prompts (vp migrate, vp create, ...), reproduce a prompt-rendering bug, or show the real interactive CLI output for a PR.\nallowed-tools: Bash, Read\n---\n\n# Verify vp's interactive CLI prompts\n\nThe PTY snapshot suite covers scripted prompt flows. This skill complements it by driving an installed CLI in tmux, capturing clean output, and checking a prompt over several seconds for a spinner animating underneath it (a real, recurring UX bug class, e.g. the \"Preparing migration\" and \"Checking Node.js version support\" spinners).\n\n## Prerequisites\n\n- `tmux` (macOS has none by default): `brew install tmux`.\n- The global `vp` must contain the code under test. To exercise working-tree changes, rebuild first: `pnpm bootstrap-cli` (~5 min), then confirm with `vp --version`.\n\n## Driver\n\n`interactive-cli-tmux-driver.sh` is bundled in this skill's directory.\n\n```bash\n# Run to completion, auto-accepting every prompt's DEFAULT; prints a clean transcript.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\"\n\n# STOP at a specific prompt (do NOT answer it) and check for a spinner animating under it.\n.claude/skills/verify-interactive-cli/interactive-cli-tmux-driver.sh <project-dir> \"vp migrate\" \"Upgrade Node.js\"\n```\n\nHow it works:\n\n- Runs the command in a detached tmux session; `tmux capture-pane -p` yields clean text (clack's in-place redraws overwrite, so only resolved lines remain), far cleaner than `expect`'s raw ANSI capture.\n- Auto-accepts each prompt's default by sending Enter when the pane goes STABLE. A waiting prompt is static; animating spinners keep the pane changing, so Enter never fires mid-work.\n- End-detection uses `; echo \"$M1$M2 exit=$?\"` where M1/M2 are split vars, so the literal end marker is not in the typed command line (otherwise a grep matches the echoed command, not the program output).\n- With a STOP_AT regex it halts at the target prompt and captures twice ~3s apart: identical captures = static prompt (OK); differing captures (or a `Checking ... (Xs)` line) = a spinner is animating under the prompt = a UX bug.\n\n## Setting up a target project\n\nCreate a throwaway project that triggers the prompts you want to see, e.g. a fresh Vite app whose `.node-version` is below the supported range to exercise the Node-upgrade confirm:\n\n```bash\nmkdir -p /tmp/vp-demo && cd /tmp/vp-demo\nprintf '{\\n  \"name\":\"d\",\"private\":true,\"type\":\"module\",\"packageManager\":\"pnpm@10.18.0\",\\n  \"scripts\":{\"build\":\"vite build\",\"test\":\"vitest run\"},\\n  \"devDependencies\":{\"vite\":\"^8.0.0\",\"vitest\":\"^4.1.0\"}\\n}\\n' > package.json\necho \"24.3.0\" > .node-version\nprintf 'import { defineConfig } from \"vite\";\\nexport default defineConfig({});\\n' > vite.config.ts\ngit init -q && git add -A && git -c user.email=x@x -c user.name=x commit -qm init\n```\n\n## When you find a spinner-over-prompt bug\n\nFix it test-first: the prompt's code must pause the migration progress spinner before the confirm renders. Assert the call order is `['pause', 'confirm']`. Reference fix: `upgradeUnsupportedNodeVersions` (in `packages/cli/src/migration/migrator/setup.ts`) takes a `pauseProgress` callback that `bin.ts` wires to `clearMigrationProgress`, called right before `prompts.confirm`.\n\n## Gotchas\n\n- clack's active-prompt marker here is `›` (not always `◆`); detect a waiting prompt by pane stability, not a specific glyph.\n- `VP_SKIP_INSTALL=1` skips the dependency install but breaks steps that load `vite.config.ts` (e.g. the prettier auto-migration) because `vite` isn't installed; use it only when you stop before the install step.\n- A `&` inside a background task detaches the script, so the task reports \"completed\" early while it keeps running; poll a status file or `capture-pane` to track real progress.\n\n<!-- File: packages/cli/AGENTS.md (Tokens: ~543 | Category: anthropic-skill) -->\n<!--VITE PLUS START-->\n\n# Using Vite+, the Unified Toolchain for the Web\n\nThis project is using Vite+, a unified toolchain built on top of Vite, Rolldown, Vitest, tsdown, Oxlint, Oxfmt, and Vite Task. Vite+ wraps runtime management, package management, and frontend tooling in a single global CLI called `vp`. Vite+ is distinct from Vite, and it invokes Vite through `vp dev` and `vp build`. Run `vp help` to print a list of commands and `vp <command> --help` for information about a specific command.\n\nDocs are local at `node_modules/vite-plus/docs` or online at https://viteplus.dev/guide/.\n\n## Built-in Commands vs Scripts\n\n`vp <name>` runs a built-in command. `vp run <name>` runs a `package.json` script or a `vite.config.ts` task. Scripts cannot overwrite built-ins, so `vp dev` and `vp run dev` may do different things. Check `package.json` and `vite.config.ts` first, and run `vp run <name>` when the project defines a script or task with that name.\n\n## Running Scripts\n\nRun TypeScript and JavaScript files with `vpx <file>`, for example `vpx ./scripts/seed.ts --dry-run`. It handles enums, decorators, JSX, and tsconfig `paths`, so do not add `tsx` or `ts-node` to run scripts. `vpx --watch <file>` reruns the file when a file it uses changes, in place of `tsx watch`. `vp node <file>` runs plain Node.js.\n\n## Tool Versions\n\nRun `vp toolchain` to show versions and relationships in the active Vite+\nrelease. Add a tool name to select part of the graph. For example, run\n`vp toolchain vite`. Use `--global` to ignore the local `vite-plus` package. Use\n`vp why <package>` to show the package-manager dependency graph.\n\n## Review Checklist\n\n- [ ] Run `vp install` after pulling remote changes and before getting started.\n- [ ] Run `vp check` and `vp test` to format, lint, type check and test changes.\n- [ ] Check if there are `vite.config.ts` tasks or `package.json` scripts necessary for validation, run via `vp run <script>`.\n- [ ] If setup, runtime, or package-manager behavior looks wrong, run `vp env doctor` and include its output when asking for help.\n\n<!--VITE PLUS END-->\n\n<!-- File: .claude/agents/cargo-workspace-merger.md (Tokens: ~1405 | Category: subagent-persona) -->\n---\nname: cargo-workspace-merger\ndescription: \"Use this agent when you need to merge one Cargo workspace into another, specifically when integrating a subproject's crates and dependencies into a root workspace. This includes tasks like: adding crate path references to workspace members, merging workspace dependency definitions while avoiding duplicates, and ensuring only production dependencies (not unnecessary dev dependencies) are included.\\\\n\\\\n<example>\\\\nContext: The user wants to integrate the rolldown project into their existing Cargo workspace.\\\\nuser: \\\"I need to merge the rolldown Cargo workspace into our root workspace\\\"\\\\nassistant: \\\"I'll use the cargo-workspace-merger agent to handle this integration. This involves analyzing both Cargo.toml files, identifying the crates to add, and merging the necessary dependencies.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\\\\n\\\\n<example>\\\\nContext: The user has cloned a Rust project as a subdirectory and wants to integrate it.\\\\nuser: \\\"Can you add all the crates from ./external-lib into our workspace?\\\"\\\\nassistant: \\\"I'll launch the cargo-workspace-merger agent to analyze the external library's workspace structure and merge it into your root Cargo.toml.\\\"\\\\n<Task tool call to launch cargo-workspace-merger agent>\\\\n</example>\"\nmodel: opus\ncolor: yellow\n---\n\nYou are an expert Rust build system engineer specializing in Cargo workspace management and dependency resolution. You have deep knowledge of Cargo.toml structure, workspace inheritance, and dependency deduplication strategies.\n\n## Your Primary Mission\n\nMerge a child Cargo workspace (located in a subdirectory) into a parent root Cargo workspace. This involves two main tasks:\n\n1. **Adding crate references**: Add all crates from the child workspace to the root workspace's `[workspace.dependencies]` section with proper path references.\n\n2. **Merging workspace dependencies**: Combine the child workspace's `[workspace.dependencies]` with the root's dependencies, ensuring no duplicates and only including dependencies actually used by the crates being merged.\n\n## Step-by-Step Process\n\n### Step 1: Analyze the Child Workspace\n\n- Read the child workspace's `Cargo.toml` (e.g., `./rolldown/Cargo.toml`)\n- Identify all workspace members from the `[workspace.members]` section\n- Extract all `[workspace.dependencies]` definitions\n\n### Step 2: Identify Crates to Add\n\n- For each workspace member, locate its `Cargo.toml`\n- Extract the crate name from `[package].name`\n- Build a list of path references in the format: `crate_name = { path = \"./child/crates/crate_name\" }`\n\n### Step 3: Analyze Dependency Usage\n\n- For each crate in the child workspace, read its `Cargo.toml`\n- Collect all dependencies from `[dependencies]`, `[dev-dependencies]`, and `[build-dependencies]`\n- Focus on dependencies that reference `workspace = true` - these need the workspace-level definition\n- Create a set of actually-used workspace dependencies\n\n### Step 4: Filter and Merge Dependencies\n\n- From the child's `[workspace.dependencies]`, only include those that are actually used by the crates\n- Check for conflicts with existing root workspace dependencies:\n  - Same dependency, same version: Skip (already exists)\n  - Same dependency, different version: Flag for manual resolution and suggest keeping the newer version\n- Exclude dev-only dependencies that aren't needed for the merged crates\n\n### Step 5: Update Root Cargo.toml\n\n- Add all crate path references to `[workspace.dependencies]`\n- Add filtered workspace dependencies to `[workspace.dependencies]`\n- Maintain alphabetical ordering within sections for cleanliness\n- Preserve any existing comments and formatting\n\n## Output Format\n\nProvide:\n\n1. A summary of crates being added\n2. A summary of dependencies being merged\n3. Any conflicts or issues requiring manual attention\n4. The exact additions to make to the root `Cargo.toml`\n\n## Quality Checks\n\n- Verify all paths exist before adding references\n- Ensure no duplicate entries are created\n- Validate that merged dependencies don't break existing crates\n- After modifications, suggest running `cargo check --workspace` to verify the merge\n- Use highest compatible semver versions (if not pinned) and merge features in crates\n\n## Important Considerations\n\n- Use `vt_path` types for path operations as per project conventions\n- Dependencies with `path` references in the child workspace may need path adjustments\n- Feature flags on dependencies must be preserved\n- Optional dependencies must maintain their optional status\n- If a dependency exists in both workspaces with different features, merge the feature lists\n\n### Workspace Package Inheritance\n\nChild crates may inherit fields from `[workspace.package]` using `field.workspace = true`. Common inherited fields include:\n\n- `homepage`\n- `repository`\n- `license`\n- `edition`\n- `authors`\n- `rust-version`\n\n**Important**: If the child workspace's `[workspace.package]` defines fields that the root workspace does not, you must add those fields to the root workspace's `[workspace.package]` section. Otherwise, crates that inherit these fields will fail to build with errors like:\n\n```\nerror inheriting `homepage` from workspace root manifest's `workspace.package.homepage`\nCaused by: `workspace.package.homepage` was not defined\n```\n\n**Steps to handle this**:\n\n1. Read the child workspace's `[workspace.package]` section\n2. Compare with the root workspace's `[workspace.package]` section\n3. Add any missing fields to the root workspace (use the root project's own values, not the child's)\n\n## Error Handling\n\n- If a crate path doesn't exist, report it clearly and skip\n- If Cargo.toml parsing fails, provide the specific error\n- If version conflicts exist, list all conflicts before proceeding and ask for guidance\n\n### Crates with Compile-Time Environment Variables\n\nSome crates use `env!()` macros that require compile-time environment variables set via `.cargo/config.toml`. These crates often have `relative = true` paths that only work when building from their original workspace root.\n\n**Example**: `rolldown_workspace` uses `env!(\"WORKSPACE_DIR\")` which is set in `rolldown/.cargo/config.toml`.\n\n**How to handle**:\n\n1. Check child workspace's `.cargo/config.toml` for `[env]` section\n2. If crates use these env vars with `relative = true`, copy those env vars to root `.cargo/config.toml` with paths adjusted to point to the child workspace directory\n3. Example: If child has `WORKSPACE_DIR = { value = \"\", relative = true }`, root should have `WORKSPACE_DIR = { value = \"child-dir\", relative = true }`\n\n<!-- File: .claude/agents/monorepo-architect.md (Tokens: ~1156 | Category: subagent-persona) -->\n---\nname: monorepo-architect\ndescription: Use this agent when you need architectural guidance for monorepo tooling, particularly for reviewing code organization, module boundaries, and ensuring proper separation of concerns in Rust/Node.js projects. This agent should be invoked after implementing new features or refactoring existing code to validate architectural decisions and placement of functionality.\\n\\nExamples:\\n- <example>\\n  Context: The user has just implemented a new caching mechanism for the monorepo task runner.\\n  user: \"I've added a new caching system to handle task outputs\"\\n  assistant: \"I'll use the monorepo-architect agent to review the architectural decisions and ensure the caching logic is properly placed within the module structure.\"\\n  <commentary>\\n  Since new functionality was added, use the monorepo-architect agent to review the code architecture and module boundaries.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is refactoring the task dependency resolution system.\\n  user: \"I've refactored how we resolve task dependencies across packages\"\\n  assistant: \"Let me invoke the monorepo-architect agent to review the refactored code and ensure proper separation of concerns.\"\\n  <commentary>\\n  After refactoring core functionality, use the monorepo-architect agent to validate architectural decisions.\\n  </commentary>\\n</example>\\n- <example>\\n  Context: The user is adding cross-package communication features.\\n  user: \"I've implemented a new IPC mechanism for packages to communicate during builds\"\\n  assistant: \"I'll use the monorepo-architect agent to review where this IPC logic lives and ensure it doesn't create inappropriate cross-module dependencies.\"\\n  <commentary>\\n  When adding features that span multiple modules, use the monorepo-architect agent to prevent architectural violations.\\n  </commentary>\\n</example>\nmodel: opus\ncolor: purple\n---\n\nYou are a senior software architect with deep expertise in Rust and Node.js ecosystems, specializing in monorepo tooling and build systems. You have extensively studied and analyzed the architectures of nx, Turborepo, Rush, and Lage, understanding their design decisions, trade-offs, and implementation patterns.\n\nYour primary responsibility is to review code architecture and ensure that functionality is properly organized within the codebase. You focus on:\n\n**Core Architectural Principles:**\n\n- Single Responsibility: Each module, file, and function should have one clear purpose\n- Separation of Concerns: Business logic, I/O operations, and configuration should be clearly separated\n- Module Boundaries: Enforce clean interfaces between modules, preventing tight coupling\n- Dependency Direction: Dependencies should flow in one direction, typically from high-level to low-level modules\n\n**When reviewing code, you will:**\n\n1. **Analyze Module Structure**: Examine where new functionality has been placed and determine if it belongs there based on the module's responsibility. Look for code that crosses logical boundaries or mixes concerns.\n\n2. **Identify Architectural Violations**:\n   - Cross-module responsibilities where one module is doing work that belongs to another\n   - Circular dependencies or bidirectional coupling\n   - Business logic mixed with I/O operations\n   - Configuration logic scattered across multiple modules\n   - Violation of the dependency inversion principle\n\n3. **Suggest Proper Placement**: When you identify misplaced functionality, provide specific recommendations:\n   - Identify the correct module/file where the code should reside\n   - Explain why the current placement violates architectural principles\n   - Suggest how to refactor without breaking existing functionality\n   - Consider the impact on testing and maintainability\n\n4. **Reference Industry Standards**: Draw from your knowledge of nx, Turborepo, Rush, and Lage to:\n   - Compare architectural decisions with proven patterns from these tools\n   - Highlight when a different approach might be more scalable or maintainable\n   - Suggest battle-tested patterns for common monorepo challenges\n\n5. **Focus on Rust/Node.js Best Practices**:\n   - In Rust: Ensure proper use of ownership, traits for abstraction, and module organization\n   - In Node.js: Validate CommonJS/ESM module patterns, async patterns, and package boundaries\n   - For interop: Review FFI boundaries and data serialization approaches\n\n**Review Methodology:**\n\n1. Start by understanding the intent of the recent changes\n2. Map out the affected modules and their responsibilities\n3. Identify any code that seems out of place or creates inappropriate coupling\n4. Provide a prioritized list of architectural concerns (critical, important, minor)\n5. For each concern, explain the principle being violated and suggest a concrete fix\n\n**Output Format:**\n\nStructure your review as:\n\n- **Summary**: Brief overview of architectural health\n- **Critical Issues**: Must-fix architectural violations that will cause problems\n- **Recommendations**: Suggested improvements with rationale\n- **Positive Patterns**: Acknowledge well-architected decisions\n- **Comparison Notes**: When relevant, note how similar problems are solved in nx/Turborepo/Rush/Lage\n\nYou are pragmatic and understand that perfect architecture must be balanced with delivery speed. Focus on issues that will genuinely impact maintainability, testability, or scalability. Avoid nitpicking and recognize when 'good enough' is appropriate for the current stage of the project.\n\nWhen you lack context about the broader system, ask clarifying questions rather than making assumptions. Your goal is to ensure the codebase remains maintainable and follows established architectural patterns while evolving to meet new requirements.\n\n<!-- File: packages/cli/README.md (Tokens: ~2049 | Category: plugin-manifest) -->\n# VITE+(⚡︎) Local CLI\n\n**The Unified Toolchain for the Web**\n_runtime and package management, create, dev, check, test, build, pack, and monorepo task caching in a single dependency_\n\nThis package provides the project-local version of Vite+. The global `vp` command automatically delegates to this package for all project-specific tasks.\n\n---\n\nVite+ is the unified entry point for local web development. It combines [Vite](https://vite.dev/), [Vitest](https://vitest.dev/), [Oxlint](https://oxc.rs/docs/guide/usage/linter.html), [Oxfmt](https://oxc.rs/docs/guide/usage/formatter.html), [Rolldown](https://rolldown.rs/), [tsdown](https://tsdown.dev/), and [Vite Task](https://github.com/voidzero-dev/vite-task) into one zero-config toolchain that also manages runtime and package manager workflows:\n\n- **`vp env`:** Manage Node.js and package managers globally and per project\n- **`vp install`:** Install dependencies with automatic package manager detection\n- **`vp dev`:** Run Vite's fast native ESM dev server with instant HMR\n- **`vp check`:** Run formatting, linting, and type checks in one command\n- **`vp test`:** Run tests through bundled Vitest\n- **`vp build`:** Build applications for production with Vite + Rolldown\n- **`vp run`:** Run `package.json` scripts and monorepo tasks with caching and dependency-aware scheduling\n- **`vp pack`:** Build libraries for npm publishing or standalone app binaries\n- **`vp toolchain`:** Show the versions of Vite+, Vite, Rolldown, Oxc, and other tools\n- **`vp create` / `vp migrate`:** Scaffold new projects and migrate existing ones\n\nAll of this is configured from your project root and works across Vite's framework ecosystem.\nVite+ is fully open-source under the MIT license.\n\n## Getting Started\n\nInstall Vite+ globally as `vp`:\n\nFor Linux or macOS:\n\n```bash\ncurl -fsSL https://vite.plus | bash\n```\n\nFor Windows:\n\n```bash\nirm https://viteplus.dev/install.ps1 | iex\n```\n\n`vp` handles the full development lifecycle such as package management, development servers, linting, formatting, testing and building for production.\n\n## Configuring Vite+\n\nVite+ can be configured using a single `vite.config.ts` at the root of your project:\n\n```ts\nimport { defineConfig } from 'vite-plus';\n\nexport default defineConfig({\n  // Standard Vite configuration for dev/build/preview.\n  plugins: [],\n\n  // Vitest configuration.\n  test: {\n    include: ['src/**/*.test.ts'],\n  },\n\n  // Oxlint configuration.\n  lint: {\n    ignorePatterns: ['dist/**'],\n  },\n\n  // Oxfmt configuration.\n  fmt: {\n    semi: true,\n    singleQuote: true,\n  },\n\n  // Vite Task configuration.\n  run: {\n    tasks: {\n      'generate:icons': {\n        command: 'node scripts/generate-icons.js',\n        cache: {\n          env: ['ICON_THEME'],\n        },\n      },\n    },\n  },\n\n  // `vp staged` configuration.\n  staged: {\n    '*': 'vp check --fix',\n  },\n});\n```\n\nThis lets you keep the configuration for your development server, build, test, lint, format, task runner, and staged-file workflow in one place with type-safe config and shared defaults.\n\nUse `vp migrate` to migrate to Vite+. It merges tool-specific config files such as `.oxlintrc*`, `.oxfmtrc*`, and lint-staged config into `vite.config.ts`.\n\n### CLI Workflows (`vp help`)\n\n#### Start\n\n- **create** - Create a new project from a template\n- **migrate** - Migrate an existing project to Vite+\n- **config** - Configure hooks and agent integration\n- **hooks** - Manage the Git hook dispatcher\n- **staged** - Run linters on staged files\n- **install** (`i`) - Install dependencies\n- **env** - Manage Node.js and package managers\n\n#### Develop\n\n- **dev** - Run the development server\n- **check** - Run format, lint, and type checks\n- **lint** - Lint code\n- **fmt** - Format code\n- **test** - Run tests\n\n#### Execute\n\n- **run** - Run monorepo tasks\n- **exec** - Execute a command from local `node_modules/.bin`\n- **node** - Run a Node.js script with the resolved Vite+ environment; use `vpx <file>` for TypeScript\n- **dlx** - Execute a package binary without installing it as a dependency\n- **cache** - Manage the task cache\n\n#### Build\n\n- **build** - Build for production\n- **pack** - Build libraries\n- **preview** - Preview production build\n\n#### Manage Dependencies\n\nVite+ automatically wraps your package manager (pnpm, npm, Yarn, or Bun) based on `packageManager` and lockfiles:\n\n- **add** - Add packages to dependencies\n- **remove** (`rm`, `un`, `uninstall`) - Remove packages from dependencies\n- **update** (`up`) - Update packages to latest versions\n- **dedupe** - Deduplicate dependencies\n- **outdated** - Check outdated packages\n- **list** (`ls`) - List installed packages\n- **why** (`explain`) - Show why a package is installed\n- **info** (`view`, `show`) - View package metadata from the registry\n- **link** (`ln`) / **unlink** - Manage local package links\n- **rebuild** - Rebuild native modules\n- **pm** - Forward a command to the package manager\n\n#### Maintain\n\n- **toolchain** - Show Vite+ tool versions and their relationships\n- **upgrade** - Update `vp` itself to the latest version\n- **implode** - Remove `vp` and all related data\n\n### Scaffolding your first Vite+ project\n\nUse `vp create` to create a new project:\n\n```bash\nvp create\n```\n\nYou can run `vp create` inside of a project to add new apps or libraries to your project.\n\nOrganizations can expose a curated set of templates under their npm scope by\npublishing `@org/create` with a `createConfig.templates` manifest in its `package.json`.\nOnce published, `vp create @org` opens an interactive picker over those\ntemplates, and setting `create: { defaultTemplate: '@org' }` in\n`vite.config.ts` makes it the default for bare `vp create`. See the\n[Organization Templates guide](https://viteplus.dev/guide/create#organization-templates)\nfor the authoring workflow and\n[`create.defaultTemplate`](https://viteplus.dev/config/create) for the\nconfig reference.\n\n### Migrating an existing project\n\nYou can migrate an existing project to Vite+:\n\n```bash\nvp migrate\n```\n\n### GitHub Actions\n\nUse the official [`setup-vp`](https://github.com/voidzero-dev/setup-vp) action to install Vite+ in GitHub Actions:\n\n```yaml\n- uses: voidzero-dev/setup-vp@<setup-vp-version>\n  with:\n    node-version: '22'\n    cache: true\n```\n\nSet `<setup-vp-version>` to an exact version from the [`setup-vp` releases page](https://github.com/voidzero-dev/setup-vp/releases). You can use a commit SHA instead. Do not use the `v1` tag. The `v1` tag no longer receives updates.\n\nSee [Automatic Version Updates](https://viteplus.dev/guide/ci#automatic-version-updates) to configure Dependabot or Renovate.\n\n#### Manual Installation & Migration\n\nIf you are manually migrating a project to Vite+, install these dev dependencies first:\n\n```bash\nvp install -D vite-plus\n```\n\nAdd package-manager overrides so that other packages use the Vite+ versions. Alias `vite` to `@voidzero-dev/vite-plus-core`, setting `<vite-plus-version>` to the installed `vite-plus` version. Pin `vitest` to `<vitest-version>`, using the bundled version shown by `vp toolchain vitest`. The project and `vp test` then use the same Vitest copy. Without the pin, a dependency or workspace package can install a different Vitest version. The two versions can use separate mocks, `expect` functions, and runner states:\n\n```json\n\"overrides\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\nIf you are using `pnpm`, add this to your `pnpm-workspace.yaml`:\n\n```yaml\noverrides:\n  vite@*: npm:@voidzero-dev/vite-plus-core@<vite-plus-version> # Match the installed vite-plus version\n  vitest@*: <vitest-version> # Match the version from vp toolchain vitest\n```\n\nOr, if you are using Yarn:\n\n```json\n\"resolutions\": {\n  \"vite\": \"npm:@voidzero-dev/vite-plus-core@<vite-plus-version>\",\n  \"vitest\": \"<vitest-version>\"\n}\n```\n\n## Sponsors\n\nThanks to [namespace.so](https://namespace.so) for powering our CI/CD pipelines with fast, free macOS, Linux, and Windows runners.\n\n<!-- File: packages/cli/src/migration/migrator/README.md (Tokens: ~1803 | Category: plugin-manifest) -->\n# `migrator/` — migration logic, split by category\n\nThe `vp migrate` implementation used to live in a single ~7,300-line\n`packages/cli/src/migration/migrator.ts`. It is now split into category\nmodules in this directory. `migration/migrator.ts` is a **barrel** that only\nre-exports them:\n\n```ts\n// migration/migrator.ts\nexport * from './migrator/shared.ts';\nexport * from './migrator/eslint.ts';\n// ...one line per module\n```\n\nSo **external code keeps importing from `./migrator.ts`** (the barrel) and\nnothing outside this directory had to change.\n\n## Modules\n\nPick the file by what a function _does_, not by where it happens to be called.\n\n| File                     | Owns                                                                                                                                                                                                                    |\n| ------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `orchestrators.ts`       | Top-level entry points that wire everything together: `rewriteStandaloneProject`, `rewriteMonorepo`, `rewriteMonorepoProject`.                                                                                          |\n| `package-json.ts`        | `rewritePackageJson` and the direct dependency-spec rewriting it does.                                                                                                                                                  |\n| `vite-plus-bootstrap.ts` | \"Already on Vite+\" detection and the bootstrap/reconcile path: `detectVitePlusBootstrapPending`, `ensureVitePlusBootstrap`, `reconcileVitePlusBootstrapPackage`, the `ensure*`/`*Pending`/`*SatisfiesVitePlus` helpers. |\n| `catalog.ts`             | `pnpm-workspace.yaml` / catalog / overrides / build-allowance writers and catalog-dependency resolvers; bun catalog; `rewriteRootWorkspacePackageJson`; pnpm workspace-settings migration.                              |\n| `vitest-ecosystem.ts`    | Detecting direct vitest usage, the override-key (\"dependency selector\") parsing/dropping logic, managed-override sets, ecosystem alignment, legacy `vite-plus-test` wrapper-alias pruning.                              |\n| `vitest-v5.ts`           | Versioned Vitest v5 preflight, compatibility edits, and review findings. Pure AST and command transforms live under `../vitest-v5/`.                                                                                    |\n| `yarn.ts`                | `.yarnrc.yml`, Yarn PnP detection, workspace-hoisting fix, webdriverio detection.                                                                                                                                       |\n| `source-scan.ts`         | Scanning a project's source tree for signals (browser-mode, opt-in providers, `@nuxt/test-utils`, retained upstream-vitest references).                                                                                 |\n| `vite-config.ts`         | `vite.config.ts` merging, default-config injection, staged-config merge, lazy-plugin wrapping, import rewriting (`rewriteAllImports`), migrated-oxlint-config sanitization, lint-staged removal.                        |\n| `oxlint-config.ts`       | Oxlint JSON inheritance graphs and shared TypeScript config generation.                                                                                                                                                 |\n| `eslint.ts`              | ESLint → Oxlint migration, oxlint JS-plugin namespace handling, ESLint prompts/warnings.                                                                                                                                |\n| `prettier.ts`            | Prettier → Oxfmt migration and its prompts/warnings.                                                                                                                                                                    |\n| `tsup.ts`                | tsup → tsdown (`vp pack`) migration via `tsdown-migrate` and its prompts/warnings.                                                                                                                                      |\n| `tsx.ts`                 | tsx → `vpx` script migration, tsx dependency retention, and its prompt/warnings.                                                                                                                                        |\n| `tsconfig.ts`            | `tsconfig.json` cleanup and `types` rewriting.                                                                                                                                                                          |\n| `framework-shim.ts`      | Framework (Vue/Astro) shim detection and injection.                                                                                                                                                                     |\n| `git-hooks.ts`           | Vite+ hook defaults, project-owned hook preservation, and conservative detection of existing hook tools.                                                                                                                |\n| `setup.ts`               | `packageManager` pin, `setup-vp` GitHub Actions version, and Volta to `.node-version` migration.                                                                                                                        |\n| `core-finalization.ts`   | Finalizing an existing-Vite+ core migration (rules YAML, core package scripts).                                                                                                                                         |\n| `shared.ts`              | Cross-cutting constants, types, and tiny utilities used by **two or more** modules. The only module that other modules import from directly (see rules).                                                                |\n\n## Rules for adding / changing code\n\n1. **Add a function to the module that matches its category.** Keep it\n   `export`ed so the barrel surfaces it. If nothing fits, prefer extending an\n   existing module over creating a new one; if you do add a module, add a line\n   for it to `migration/migrator.ts`.\n\n2. **Importing another module's helper — use the barrel.** Reference\n   cross-module **functions** via the barrel:\n\n   ```ts\n   import { managedOverridePackages, rewritePackageJson } from '../migrator.ts';\n   ```\n\n   This creates an import cycle (the barrel re-exports your module), which is\n   **safe only because** these helpers are referenced _inside function bodies_\n   (at runtime), never at module-evaluation time. Preserve that invariant: do\n   not call a cross-module helper at the top level of a module.\n\n3. **`shared.ts` is the one exception — import it directly, never via the\n   barrel.** Things in `shared.ts` (e.g. `REMOVE_PACKAGES`,\n   `OPT_IN_BROWSER_PROVIDERS`, shared types) are referenced at _module-load_\n   time, so they must be imported from a fully-evaluated leaf:\n\n   ```ts\n   import { REMOVE_PACKAGES, type CatalogDependencyResolver } from './shared.ts';\n   ```\n\n   Keep `shared.ts` a **pure leaf**: it may import external packages and\n   `../utils/*` / `./report.ts` etc., but it must **not** import from any\n   sibling module or the barrel.\n\n4. **Where does a new shared thing go?** A constant / type / helper used by a\n   single module lives in that module. The moment a second module needs it,\n   move it to `shared.ts` and `export` it.\n\n5. **This split is structure only.** The barrel contains no logic; behavior\n   changes belong in the relevant module and need their unit test (and snap\n   test) updated, not the barrel.\n\n## When to add a new module\n\nDefault to extending an existing module. Add a new file only when one of these\nholds:\n\n- **A new self-contained category appears** — usually a new tool/format being\n  migrated (mirrors `eslint.ts`, `prettier.ts`, `yarn.ts`, `git-hooks.ts`).\n  Test: you can name its single responsibility without saying \"and\".\n- **An existing module outgrows readability _and_ has a clean seam** — a\n  cohesive, loosely-coupled sub-cluster. Rough trigger: **>~900–1,000 lines plus\n  a natural split point** (e.g. `catalog.ts` could become `catalog.ts` +\n  `pnpm-workspace.ts`). Size alone is not enough.\n- **Scattered helpers form a coherent theme** and consolidating them aids\n  discoverability.\n\nDo **not** add a file when: the function fits an existing category (extend it);\nit's one small helper (owning module, or `shared.ts` if shared); it would be 1–2\nfunctions with no theme (fragmentation is as bad as a monolith); or it would\nhave a tight two-way dependency with another module (they belong together).\n\nWhen you do add one: give it a single-responsibility name, add its `export *`\nline to `migration/migrator.ts`, add a row to the module table above, and decide\nits layer — if siblings reference it at **load time** it must be a pure leaf (or\nthose pieces go in `shared.ts`); helpers used only inside function bodies may\nimport from the barrel.\n\n## Validating a change\n\nFrom the repo root:\n\n```bash\nvp check                                             # format + lint + type-check\npnpm -F vite-plus exec vitest run src/migration      # migration unit tests\njust snapshot-test migration                         # migration CLI snapshot tests\n```\n\nReview any changed `.md` snapshots like code. A pure reorganization must not\nchange type-check results, the unit-test count, or snapshots. A behavior change\nshould be accompanied by the matching unit or snapshot test updates.\n\n<!-- File: packages/cli/templates/generator/README.md (Tokens: ~287 | Category: plugin-manifest) -->\n# Vite+ Code Generator Starter\n\nA starter for creating a Vite+ code generator.\n\n## Usage\n\nFrom monorepo root:\n\n```bash\n# run and select the generator\nvp create\n```\n\nFor automation, provide the directory and every required template option:\n\n```bash\nvp create <generator-name> --no-interactive -- --directory new-package --name new-package\n```\n\nVite+ sets `VP_CREATE_INTERACTIVE=0` for non-interactive local Bingo generators.\nThis starter then validates the arguments and runs Bingo's programmatic API.\nMissing options and existing directories fail before any files are generated.\nExisting generators are copied project files and are not updated by upgrading Vite+.\nTo adopt this behavior, update their entrypoint to match this starter.\nDirect invocation uses the interactive CLI unless this variable is set to `0`.\nWhen adding template options, also add their CLI types in `bin/index.ts`.\n\n## Development\n\n```bash\n# Edit the template\ncode src/template.ts\n\n# Test the generator CLI\nvp run dev\n\n# Run tests\nvp run test\n```\n\n## Customization\n\nEdit `src/template.ts` to customize:\n\n- Options schema (using Zod)\n- File generation logic\n- Scripts and suggestions\n\nMore information about the [Bingo Templates](https://create.bingo/) can be found [here](https://create.bingo/build/concepts/creations).\n\n<!-- File: packages/cli/templates/monorepo/README.md (Tokens: ~88 | Category: plugin-manifest) -->\n# Vite+ Monorepo Starter\n\nA starter for creating a Vite+ monorepo.\n\n## Development\n\n- Check everything is ready:\n\n```bash\nvp run ready\n```\n\n- Run the tests:\n\n```bash\nvp run -r test\n```\n\n- Build the monorepo:\n\n```bash\nvp run -r build\n```\n\n- Run the development server:\n\n```bash\nvp run dev\n```\n\n<!-- File: packages/create-vite-plus/README.md (Tokens: ~185 | Category: plugin-manifest) -->\n# create-vite-plus\n\nCreate a new [Vite+](https://viteplus.dev) project without installing the global `vp` CLI.\n\n```bash\npnpm create vite-plus\nnpm create vite-plus@latest\nyarn create vite-plus\nbun create vite-plus\n```\n\nThis runs [`vp create`](https://viteplus.dev/guide/create) from the `vite-plus` package and uses the package manager that ran it, unless you pass `--package-manager` or create the project inside an existing workspace. Arguments are forwarded unchanged, so the first argument is the template:\n\n```bash\npnpm create vite-plus vite:application --directory my-app\npnpm create vite-plus vite -- --template react-ts\n```\n\nnpm needs `--` before options meant for `vp create`, for example `npm create vite-plus@latest -- vite:library --no-interactive`.\n\n<!-- File: packages/prompts/README.md (Tokens: ~544 | Category: plugin-manifest) -->\n# @voidzero-dev/vite-plus-prompts\n\nTerminal prompts shared by Vite+ and tools that use its visual identity.\n\n```ts\nimport { isCancel, log, note, select } from '@voidzero-dev/vite-plus-prompts';\n\nconst framework = await select({\n  message: 'Choose a framework:',\n  options: [\n    { value: 'react', label: 'React', hint: 'TSX Pages' },\n    { value: 'vue', label: 'Vue', hint: 'Vue SFC Pages' },\n  ],\n});\n\nif (!isCancel(framework)) {\n  log.success(`Selected ${framework}`);\n  note('Run vp dev to start developing.', 'Next steps');\n}\n```\n\nBlue `›` pointers identify focused choices and active prompts. Completed steps\nand rounded box borders use gray; warnings use yellow and errors use red.\nInactive choices are dimmed, and descriptive hints appear on the focused choice.\nLogs retain severity symbols without guide bars and use compact spacing.\nNotes have complete borders and readable content by default.\n\nGuides are off by default. Pass `withGuide: true` for one call, or use\n`updateSettings({ withGuide: true })` to enable them globally. This controls the\nprompt gutter, not box borders. Logs accept `spacing` for explicit blank rows;\nnotes accept `format` and boxes accept `formatBorder` and `rounded`.\n\nSelect and multiselect prompts show compact keyboard instructions. Pass\n`showInstructions: false` to hide them. Text, password, path, and autocomplete\nvalidation supports synchronous or asynchronous functions and Standard Schema.\nPasswords accept a placeholder and always mask entered values. Path prompts\nsupport directory navigation and Tab completion; general autocomplete can opt\nin with `completeOnTab: true`.\n\n`accessible: true`, `updateSettings({ accessible: true })`, or `ACCESSIBLE=1`\nenables static prompt output for screen readers. Spinners use static output in\naccessible mode, CI, and non-TTY streams. Their `pause()` and `resume()` methods\npreserve elapsed time while excluding pauses. `CANCEL_SYMBOL` is exported for\ncallers that need the exact cancellation sentinel; `isCancel()` narrows results.\n\nThe interaction engine is `@clack/core` 1.5.1 or newer. Rendering and the shared\nvisual defaults are maintained in this package, so consumers can use the same\nappearance without implementing their own prompt renderers.\n\n## Verification\n\nFrom the repository root:\n\n```sh\nvp check packages/prompts/src\nvp test run packages/prompts/src/__tests__\npnpm --filter @voidzero-dev/vite-plus-prompts build\n```\n\n<!-- File: packages/tools/README.md (Tokens: ~437 | Category: plugin-manifest) -->\n# tools for internal development use\n\n## Bins\n\n- json-edit: A CLI tool to edit JSON files such as package.json, used by the release workflow to stamp build versions\n\n## `tool` subcommands\n\nRun with `tool <name>`:\n\n- sync-remote: Sync upstream dependency sources and catalog versions from `.upstream-versions.json`\n- install-global-cli: Install the local build of the global `vp` CLI. The tool\n  reuses an existing `~/.vite-plus` install. Otherwise, it uses the split\n  platform data directory.\n- brand-vite: Apply Vite+ branding patches to the synced vite source (also runs at the end of sync-remote)\n- local-npm-registry: Serve locally packed checkout packages behind a real registry HTTP interface for snapshot tests, ecosystem e2e, and local `vp migrate`/`vp create` iteration\n\n## Local npm registry\n\nSee the [contributing guide](../../CONTRIBUTING.md#test-vp-migrate--vp-create-through-a-local-npm-registry) for local build and usage commands.\n\n[`src/local-npm-registry.ts`](src/local-npm-registry.ts) packs the checkout, serves its tarballs through a registry HTTP interface, and proxies other packages upstream.\n\n- Served versions use an old publish time so package-manager minimum-release-age checks allow local builds immediately.\n- Wrapped commands use temporary Yarn Berry and bun caches to avoid reusing stale local builds with the same package version.\n- The server also backs [PTY snapshot cases](../../crates/vp_cli_snapshots/tests/cli_snapshots/README.md) and [ecosystem e2e tests](../../ecosystem-ci/patch-project.ts). Snapshots use recorded test packages and a run-scoped response cache. `local-registry = true` additionally serves the packed checkout; ecosystem and standalone runs retain upstream version merging.\n- Refresh the small snapshot registry fixtures explicitly with `node packages/tools/src/record-snapshot-registry.ts`. See the [fixture documentation](../../crates/vp_cli_snapshots/tests/cli_snapshots/registry/README.md).\n</yakaai_skills>"}