Conversation
✅ Deploy Preview for viteplus-preview canceled.
|
How to use the Graphite Merge QueueAdd the label auto-merge to this PR to add it to the merge queue. You must have a Graphite account in order to use the merge queue. Sign up using this link. An organization admin has enabled the Graphite Merge Queue in this repository. Please do not merge from GitHub as this will restart CI on PRs being processed by the merge queue. This stack of pull requests is managed by Graphite. Learn more about stacking. |
When `create.defaultTemplate: '@org'` is set, bare `vp create` now drops into the org picker with a trailing "Vite+ built-in templates" escape-hatch entry that routes to the existing `getInitialTemplateOptions` flow. This preserves interactive discovery of `vite:monorepo` / `vite:application` / `vite:library` / `vite:generator` without adding a new CLI flag; explicit specifiers (e.g. `vp create vite:application`) remain the scripted bypass. Addresses @cpojer's review feedback on #1398.
When `create.defaultTemplate: '@org'` is set, bare `vp create` now drops into the org picker with a trailing "Vite+ built-in templates" escape-hatch entry that routes to the existing `getInitialTemplateOptions` flow. This preserves interactive discovery of `vite:monorepo` / `vite:application` / `vite:library` / `vite:generator` without adding a new CLI flag; explicit specifiers (e.g. `vp create vite:application`) remain the scripted bypass. Addresses @cpojer's review feedback on #1398.
Let manifest entries use relative paths (`./templates/demo`) that resolve against the enclosing @org/create package root, so orgs can ship one package containing N templates instead of publishing N @org/template-* packages — the pattern already used by create-vite, create-next-app, and most enterprise scaffolding kits. Vite+ fetches the tarball URL already returned by the registry manifest, extracts it once per version into `$VP_HOME/tmp/create-org/<scope>/<name>/<version>/`, and scaffolds by directory copy. Paths that escape the package root are rejected at schema-validation time. Addresses @fengmk2's review feedback on #1398 asking whether `./templates/demo`-style bundled paths would be supported.
d3c56db to
6d1e8f3
Compare
Two inline review findings from @fengmk2 on PR #1398: - Bundled subdirectory section was leaking implementation details (tarball download mechanics, SHA-512 integrity verification, cache path under $VP_HOME, NPM_CONFIG_REGISTRY handling). Users just need to know that `./...` paths resolve against the @org/create package root and that the directory is copied verbatim. The rest belongs in the RFC. - Non-interactive inspection section explained the design rationale for keeping the output machine-parseable ("so scripts and AI agents can recover the list without a separate `--list` flag"). That's an RFC-level decision; users just need to see the shape of the output.
|
@cursor review |
The early-exit "template name is required" guard fired before `getConfiguredDefaultTemplate` ran, so `vp create --no-interactive` in a repo with `create.defaultTemplate: '@your-org'` exited with a generic "template name required" message instead of reading the configured default. The documented precedence (CLI arg > create.defaultTemplate > interactive picker) was silently broken for all non-interactive / CI usage. Fix: move the guard to after the full resolution chain (CLI arg → create.defaultTemplate → @org manifest). Only error if `selectedTemplateName` is still empty once every input source has had a chance to fill it. If the configured default is an @org scope, the manifest resolver's own `--no-interactive` branch prints the manifest table — exactly what a user expects when the config was supposed to pick the org. New snap-test `create-org-config-default` locks in the fix: a fixture with `create: { defaultTemplate: '@your-org' }` in vite.config.ts runs bare `vp create --no-interactive` and verifies the @your-org manifest table prints instead of the generic missing-argument error. Reported by Cursor Bugbot on #1398.
|
@cursor review |
When `create.defaultTemplate: '@org'` is set, bare `vp create` now drops into the org picker with a trailing "Vite+ built-in templates" escape-hatch entry that routes to the existing `getInitialTemplateOptions` flow. This preserves interactive discovery of `vite:monorepo` / `vite:application` / `vite:library` / `vite:generator` without adding a new CLI flag; explicit specifiers (e.g. `vp create vite:application`) remain the scripted bypass. Addresses @cpojer's review feedback on #1398.
Let manifest entries use relative paths (`./templates/demo`) that resolve against the enclosing @org/create package root, so orgs can ship one package containing N templates instead of publishing N @org/template-* packages — the pattern already used by create-vite, create-next-app, and most enterprise scaffolding kits. Vite+ fetches the tarball URL already returned by the registry manifest, extracts it once per version into `$VP_HOME/tmp/create-org/<scope>/<name>/<version>/`, and scaffolds by directory copy. Paths that escape the package root are rejected at schema-validation time. Addresses @fengmk2's review feedback on #1398 asking whether `./templates/demo`-style bundled paths would be supported.
Two inline review findings from @fengmk2 on PR #1398: - Bundled subdirectory section was leaking implementation details (tarball download mechanics, SHA-512 integrity verification, cache path under $VP_HOME, NPM_CONFIG_REGISTRY handling). Users just need to know that `./...` paths resolve against the @org/create package root and that the directory is copied verbatim. The rest belongs in the RFC. - Non-interactive inspection section explained the design rationale for keeping the output machine-parseable ("so scripts and AI agents can recover the list without a separate `--list` flag"). That's an RFC-level decision; users just need to see the shape of the output.
The early-exit "template name is required" guard fired before `getConfiguredDefaultTemplate` ran, so `vp create --no-interactive` in a repo with `create.defaultTemplate: '@your-org'` exited with a generic "template name required" message instead of reading the configured default. The documented precedence (CLI arg > create.defaultTemplate > interactive picker) was silently broken for all non-interactive / CI usage. Fix: move the guard to after the full resolution chain (CLI arg → create.defaultTemplate → @org manifest). Only error if `selectedTemplateName` is still empty once every input source has had a chance to fill it. If the configured default is an @org scope, the manifest resolver's own `--no-interactive` branch prints the manifest table — exactly what a user expects when the config was supposed to pick the org. New snap-test `create-org-config-default` locks in the fix: a fixture with `create: { defaultTemplate: '@your-org' }` in vite.config.ts runs bare `vp create --no-interactive` and verifies the @your-org manifest table prints instead of the generic missing-argument error. Reported by Cursor Bugbot on #1398.
When a user types `vp create @org` expecting the picker but `@org/create` has no `vp.templates` manifest, emit a one-line `info:` note before falling through to run `@org/create` as a normal package: No `vp.templates` manifest in @org/create — running it as a normal package. Without this, a later `ERR_PNPM_DLX_NO_BIN` (the typical failure when `@org/create` is data-only, as with `@eggjs/create`) looks mysterious — the user has no way to know the picker was attempted and skipped. Triggered only for scope-only input (`vp create @org`). The per-entry form (`vp create @org/name`) stays silent because it's ambiguous — the user might have genuinely wanted `@org/create-name` via the existing shorthand. Also helps diagnose registry-mirror sync lag: if a user's mirror hasn't synced a just-published manifest, they now see "no manifest" instead of the downstream dlx failure. Reported by @fengmk2 on #1398 after `vp create @eggjs` in a monorepo showed the monorepo parent-dir prompt and then `pnpm dlx @eggjs/create` without ever running the picker.
Manifest entries like `{ template: '@your-org/template-web' }` were
passing through `discoverTemplate` → `expandCreateShorthand` and being
rewritten into `@your-org/create-template-web` — because the shorthand
rule targets any `@scope/name` that doesn't already start with `create-`.
The manifest author's literal package name was silently transformed,
causing vp to fetch the wrong package.
Fix: thread a `skipShorthand` flag through `discoverTemplate` that short-
circuits the final `expandCreateShorthand` call. Set it when
`resolveOrgManifestForCreate` returns `kind: 'replaced'` (the path
responsible for manifest-resolved specifiers). The earlier branches in
`discoverTemplate` (`vite:`, GitHub URLs, local workspace packages)
still match first, so manifest entries using those specifier kinds
behave unchanged.
Unit test added to `discovery.spec.ts` pinning both halves of the
contract: `@your-org/template-web` stays literal with the flag set,
and still expands via shorthand without it (the existing behavior
for non-manifest inputs).
Reported by Cursor Bugbot on #1398.
764dd2d to
f4ed12f
Compare
|
@cursor review |
I would also be confused about this. Let's change it to the |
Manifest entries like `{ template: '@your-org/template-web' }` were
passing through `discoverTemplate` → `expandCreateShorthand` and being
rewritten into `@your-org/create-template-web` — because the shorthand
rule targets any `@scope/name` that doesn't already start with `create-`.
The manifest author's literal package name was silently transformed,
causing vp to fetch the wrong package.
Fix: thread a `skipShorthand` flag through `discoverTemplate` that short-
circuits the final `expandCreateShorthand` call. Set it when
`resolveOrgManifestForCreate` returns `kind: 'replaced'` (the path
responsible for manifest-resolved specifiers). The earlier branches in
`discoverTemplate` (`vite:`, GitHub URLs, local workspace packages)
still match first, so manifest entries using those specifier kinds
behave unchanged.
Unit test added to `discovery.spec.ts` pinning both halves of the
contract: `@your-org/template-web` stays literal with the flag set,
and still expands via shorthand without it (the existing behavior
for non-manifest inputs).
Reported by Cursor Bugbot on #1398.
When the `--directory` guard was widened from `!isBuiltinTemplate` to `!isDirectScaffoldTemplate` (to accept bundled @org templates alongside builtins), the error text was left behind and still said "only available for builtin templates". Users hitting it today are told --directory doesn't work with bundled templates, even though it does. Now reads: "only available for builtin and bundled @org templates". Reported by Cursor Bugbot on #1398.
BREAKING (unpublished feature): the org-create manifest now lives at `createConfig.templates` in `@org/create`'s package.json, replacing the Vite+-specific `vp.templates` location. Rationale (fengmk2 + cpojer, PR #1398 review): the manifest metadata is tool-neutral (names, descriptions, template specifiers) and belongs under a neutral field name. Following the precedent of npm's `publishConfig`, `createConfig` reads as "configuration used specifically when this package is consumed as a create initializer", leaving room for other scaffolding tools to adopt the same convention later. Vite+-specific behavior, if/when it's needed, can sit at `createConfig.vp.*` without further schema changes. Schema shape unchanged (name / description / template / keywords / monorepo). Validation error messages now reference `createConfig.templates[...]` instead of `vp.templates[...]`. Updated: org-manifest reader and schema validator, RegistryVersionMeta type, org-resolve info note, bin.ts help text, define-config JSDoc, RFC manifest schema + field reference + resolution flow sections, docs guide + config pages, root README, all 7 snap-test mock manifests (and the bundled-fixture tarball + integrity hash), and the unit-test regex that asserts the error path name.
BREAKING (unpublished feature): the manifest-entry selector uses `:` instead of `/`. `vp create @your-org:web` now picks the `web` entry from `@your-org/create`'s `createConfig.templates` manifest. The old `@your-org/web` form is no longer a manifest lookup — it reverts to the pre-feature `expandCreateShorthand` path that turns it into `@your-org/create-web`, exactly as it worked before this feature. Rationale (cpojer + fengmk2, PR #1398 review): the `/` form collides with real npm packages. If `@your-org/web` exists on npm as a genuine package, `vp create @your-org/web` would silently prefer the manifest entry over the real package — confusing, and fragile. The `:` separator mirrors the existing `vite:monorepo` / `vite:library` builtin-template syntax, which users already read as "namespace:entry". It also keeps manifest entries lexically distinct from any real npm package path. Miss behavior: `vp create @org:foo` where `foo` isn't in the manifest (or the manifest is absent) is a HARD ERROR listing the available entries. No silent fall-through — the `:` form is an explicit manifest lookup, and the fallback shorthand is reserved for the `/` form. Updated: `parseOrgScopedSpec` parses only `@scope` and `@scope:name` (returns `null` for `@scope/anything`), `resolveOrgManifestForCreate` errors on direct-selection miss, CLI help text, RFC command matrix + examples + error-handling table + compatibility section, docs guide + config page, snap-test fixture commands (`create-org-bundled`, `create-org-monorepo-direct-in-monorepo`), `--no-interactive` hint line, and unit tests for `parseOrgScopedSpec` (new test pinning the null return for the `/` form).
Removed the `prompts.log.info('selected ... from ...')` line that used
to precede every manifest-driven template resolution. Rationale
(cpojer, PR #1398): the clack picker already prints the selected entry
above in its completed-prompt line, so the info note was a redundant
restatement of what the user just picked a moment before.
Impact on downstream failure context (the original reason the
breadcrumb was added, via the Cursor "chosen template fails to resolve
with context" finding): for the interactive path, the picker's own
completed-prompt line still shows the selection. For the
non-interactive `@org:name` path, the error surfaces from the
downstream runner — no vp-side framing. If that proves confusing in
practice, we can add the breadcrumb back only in failure paths.
Snap regenerated for `create-org-bundled` (the only fixture that
captured the line). 265 unit tests still pass.
- org-resolve: replace `prompts.log.error + process.exit(1)` pairs with
`cancelAndExit(msg, 1)` for a consistent exit style; route the inline
`templates.filter((t) => !(t.monorepo && isMonorepo))` sites through the
existing `filterManifestForContext` helper; drop the pointless dynamic
`await import('../resolve-vite-config.ts')` since `findViteConfigUp` was
already statically imported from the same module.
- OrgResolution: drop the unused `entry` field from `replaced`/`bundled`
variants.
- org-tarball: stream the response body and enforce the 50 MB cap during
read; a malicious 1 GB tarball is now rejected before being fully
buffered. Short-circuit early via Content-Length when the server
advertises one.
- bin / discovery: trim narrating comments that restated the code; keep
the `@org/template-web` vs `create-template-web` note since it
explains a non-obvious invariant.
…dirs PR review follow-ups for the org tarball extractor: - bugbot TOCTOU race (#3135845042): the final `rename(stagingDir → destDir)` now catches ENOTEMPTY/EEXIST/EPERM. A losing concurrent `vp create @org:<name>` drops its staging tree and returns the already-populated cache dir instead of surfacing an unhandled error. - staging-dir accumulation (#3135903176): `cleanupStaleStagingDirs` runs before each fresh extract and removes sibling `<destDir>.tmp-*` trees older than 24 hours. The mtime gate keeps concurrent live extractions safe — a staging dir that's currently being written is always younger than the cutoff. - hoist `path.resolve(stagingDir)` out of the per-entry loop (#3135922205) — no behavior change, one resolve per extract. - document why non-`package/` tar roots are accepted (#3135908121) with a source reference to the npm-pack convention.
`@org/create` is a new feature — we only need to support the tarball layout produced by `npm pack`, which always wraps every entry under `package/`. Drop the accept-anything fallback; entries outside `package/` are silently ignored alongside the existing PaxHeader / root-directory skips.
Simplify-round follow-ups: - drop `EPERM` from the rename-race allowlist and require `destDir/package.json` to actually exist before treating the rename failure as a won-race: permission errors (SIP, read-only FS, cross-device) now surface instead of being silently swallowed. - trim the `normalizeEntryName` JSDoc to one sentence; export it and `cleanupStaleStagingDirs` for direct unit testing. - add 9 tests covering the `package/` prefix strip, skip paths (PaxHeader, root dir, outside `package/`), and the 24-hour mtime gate in `cleanupStaleStagingDirs` (stale deleted, fresh preserved, unrelated siblings untouched, missing parent tolerated).
…orepo Address Codex review follow-ups for the org-template flow (exec-bit finding ignored per the user): - Pinned versions: parseOrgScopedSpec now returns the optional `version` suffix and readOrgManifest resolves it against `versions[...]` / `dist-tags[...]`, throwing loudly when the requested version is unknown. `vp create @your-org@1.2.3` and `vp create @your-org:web@next` no longer silently scaffold `dist-tags.latest`. - npm registry / auth: getNpmRegistry(scope?) reads cwd-walked and user `.npmrc` files plus `npm_config_*` env vars, honoring `@your-org:registry=...` scope mappings. getNpmAuthHeader parses `//host[/path]/:_authToken`, `:_auth`, and username/password pairs. The packument fetch and tarball download retry with auth only when the server returns 401/403 — public registries never see a token. - Bundled monorepo entries: `OrgResolution.bundled` now carries the `monorepo` flag. bin.ts routes bundled monorepo templates through the same post-processing as the `vite:monorepo` builtin (rewriteMonorepo, agent instructions, hooks, install, fmt, summary) instead of the standalone path. - Docs: mention version pinning and `.npmrc` support in the guide. - Tests: +5 new scenarios for version pinning, scope-specific registry, auth retry, and no-auth-on-first-request behavior.
Apply each tar entry's stored mode (masked to the low 9 permission bits) after writing the file. Files shipped with 0755 — shell scripts, gradlew/mvnw wrappers, release helpers — are now executable in the scaffolded project instead of arriving as plain files. setuid / setgid / sticky bits are stripped on purpose: those have no place in a user-land scaffold and could surprise reviewers later. Also export `parseEntryMode` and cover it with tests (parses octal, masks elevated mode bits, tolerates missing / unparseable input).
Post-simplify cleanup for the org-template feature: - Move `getNpmRegistry`, `getNpmAuthHeader`, `.npmrc` parsing, and the new `fetchNpmResource` wrapper into `utils/npm-config.ts` so the concerns stay out of the local-package-metadata file. - `fetchNpmResource` centralizes the 401/403 retry-with-auth pattern previously copy-pasted in `fetchPackument` and `fetchTarball`; future tweaks (WWW-Authenticate parsing, observability) land in one place. - `checkNpmPackageExists` now honors the scope-specific registry and flows through the same auth retry — probes against private registries no longer miss because of a hardcoded public-registry URL. - `OrgResolution.bundled.monorepo` flips to `monorepo?: true` to match `OrgTemplateEntry.monorepo?: boolean`; non-monorepo entries drop the redundant `false` field entirely.
PR review (#3136050751): the bundled scaffold-fallback name was hardcoded to `vite-plus-template`, so two bundled templates from the same org produced identical default project names. Propagate the manifest entry's `name` through `OrgResolution.bundled.entryName` and use it in the fallback, so `@your-org:demo` defaults to `vite-plus-demo` and the picker case (where `selectedTemplateName` is scope-only) still gets a descriptive name.
PR review (#3136678759): the cwd walk-up in `getNpmConfig` passes through `$HOME` when the project lives under it, re-loading `~/.npmrc` AFTER the project `.npmrc` was loaded. The last `loadFileInto` wins, so user-level registry/auth silently clobbered project-level settings — inverting the documented precedence. Fix: collect project `.npmrc` paths while walking up (skipping the already-loaded `~/.npmrc` duplicate), then apply them in reverse (root → cwd) so the innermost file wins. Add a dedicated test suite covering the project-vs-user precedence, `@scope:registry` resolution, and `_authToken` matching.
`getConfiguredDefaultTemplate` used to inspect only the directory it was handed — which, for standalone repos, was reset to `cwd` earlier in `bin.ts`. Running `vp create` from `repo/apps/web/` therefore never saw `repo/vite.config.ts` and its configured default. Walk up from `startDir` to find the nearest project root first (monorepo marker or `.git`) and read the config from there. Both monorepo and standalone-repo layouts now pick up the root config regardless of which subdirectory the command was invoked from. - `resolve-vite-config.ts`: extend `findWorkspaceRoot` with a `.git` branch so standalone git repos resolve to the repo root. Export it so the create flow can reuse it. - `org-resolve.ts`: `getConfiguredDefaultTemplate` now walks via `findWorkspaceRoot` before looking for `vite.config.ts`. - Add `__tests__/org-resolve.spec.ts` covering the root case, the walk-up-from-subdir case, no-config, and no-defaultTemplate.
A subproject (git submodule, nested clone) can have its own `.git` directory. If that inner `.git` is matched during the primary walk-up, it preempts a monorepo marker higher up the tree and `vp create` resolves to the subproject root instead of the monorepo root. Split `findWorkspaceRoot` into two passes: - Primary: walk up looking only for authoritative monorepo markers (`pnpm-workspace.yaml`, `workspaces` in `package.json`, `lerna.json`). - Fallback: only if no marker was found anywhere in the ancestry, walk again looking for `.git` as the standalone repo boundary. Add a test covering the subproject case (inner `.git` must not shadow an outer `pnpm-workspace.yaml`).
…anups
Post-simplify cleanup:
- `findWorkspaceRoot` now uses a single walk with a deferred `.git`
match. The two-pass version walked the entire ancestry twice in the
no-marker case; now we record the first `.git` seen and return it
only after the walk exhausts without finding a monorepo marker.
Same "markers win globally" semantics, half the I/O.
- Export `hasViteConfig` and use it in `getConfiguredDefaultTemplate`
instead of the degenerate `findViteConfigUp(x, x)` (start == stop
meant it only checked the single directory anyway).
- Drop the dead `?? 'template'` fallback in `bin.ts` —
`bundledEntryName` is always set whenever `isBundledTemplate` is true
(both come from the same `resolved.kind === 'bundled'` branch).
- `fetchNpmResource` init type is now `Omit<RequestInit, 'signal'>`
so callers can't accidentally pass a `signal` that the
`AbortSignal.timeout(timeoutMs)` inside would silently override.
- Tighten `expandNpmrcValue` JSDoc to list the four `.npmrc` features
we do NOT handle (backslash escapes, `${VAR-default}`, inline `;`
comments, `key[]=` lists).
…ty comment Two PR review follow-ups: - `discovery.spec.ts` (#3137604410, #3137608041): the `@acme-corp` placeholder rename inadvertently touched pre-existing GitHub-URL tests that have nothing to do with the `@org` feature. Revert those four lines back to `acme-corp`; the new manifest-specific tests below keep `@your-org`. - `org-tarball.ts` (#3137690878): the `verifyIntegrity` comment claimed "don't silently accept" but the unknown-format branch does exactly that. Rewrite the comment to match reality — we skip verification for unknown integrity formats because `sha512-<base64>` is the universal case and erroring out on anything else would break extracts for no benefit.
Per PR review: extending `findWorkspaceRoot` to recognize `.git` as a project boundary is out of scope for this PR. Restore it to the original monorepo-marker-only behavior (`pnpm-workspace.yaml`, `workspaces` in `package.json`, `lerna.json`). `getConfiguredDefaultTemplate` still walks up via `findWorkspaceRoot` so monorepo layouts continue to pick up the root `vite.config.ts` from any subdirectory. Standalone git repos (no monorepo marker) now only see a config that sits at the cwd. Update `org-resolve.spec.ts` to exercise the walk-up via `pnpm-workspace.yaml` instead of `.git`, and remove the nested-`.git`- subproject test since there's nothing for that case to assert against anymore.
PR review (#3138196923, #3138197820, #3138198930): the earlier revert went half-way — the pre-feature GitHub-URL parsing tests should keep their original `nkzw-tech/fate-template` placeholder, not `acme-corp`. These tests are unrelated to the `@org` feature and should never have been touched by the placeholder refactor.
428a4b4 to
a5f36e0
Compare
|
Upgrading to the alpha version allows early access to this feature: |
Four findings from a Codex review of the feature, addressed together: 1. **High — registry-aware tarball cache key**: `getExtractionDir` now namespaces by the tarball URL's host, so two repos resolving the same `<scope>@<version>` through different registries don't share a cache slot. Layout: `<host>/<scope>/create/<version>` (e.g. `registry.npmjs.org/@your-org/create/1.0.0`). Falls back to a `_unknown` segment if the URL parse fails. 2. **Medium — picker escape-hatch sentinel**: replace the static `__vp_builtin_escape__` value with a per-invocation `__vp_builtin_escape__::<uuid>` value plus a `Map<string, entry | ESCAPE_HATCH>` lookup so a manifest entry can never collide with the sentinel. Also enforce the reservation at the schema level — any entry `name` starting with `__vp_` is rejected as malformed. 3. **Medium — drop unused `keywords` field**: parsed and validated but never consumed. Remove from `OrgTemplateEntry`, `validateEntry`, and the docs `Each entry supports` table. 4. **Low — JSDoc on `getConfiguredDefaultTemplate`**: the comment referenced `.git`-based discovery that was reverted. Trim it to match current behavior (workspace markers only). Tests: +1 covering the reserved `__vp_` prefix; existing 160 still pass.
- `org-tarball.ts` — `new URL('https:/typo').host` parses successfully
but yields `''`, which would join into an empty cache-path segment
and silently bucket all such manifests together. Fall back to
`_unknown` for empty hosts the same way we do for hard parse
failures.
- `org-picker.ts` — the "should never happen" branch in
`pickOrgTemplate` used to return `ORG_PICKER_CANCEL`, masquerading
an internal invariant violation as a user-initiated cancel. Throw
loudly instead so a real bug surfaces.
- `org-manifest.ts` — add a one-liner explaining why the `__vp_`
name prefix is reserved (internal sentinel namespace, e.g. the
picker's escape-hatch nonce) so the next maintainer doesn't have
to reverse-engineer the link.
`manifest.tarballUrl` comes from the npm registry, which guarantees a valid URL. The `_unknown` fallback was defensive against a hypothetical malformed input, but if the URL ever fails to parse it's a real bug — let it throw rather than silently bucket the extraction under a fallback path.







Extends
vp createso an organization can expose a curated set of templates through a single entry point.vp create @your-orgfetches@your-org/create'svp.templatesmanifest from npm and renders an interactive picker over the listed entries. Acreate.defaultTemplateoption invite.config.tsmakes an org the default for barevp create. Zero-risk for orgs without a manifest — vp falls back to running@your-org/createas today.Highlights
vp create @org→ picker (with aVite+ built-in templatesescape hatch that stays reachable).vp create @org/<name>→ direct selection; falls back to the existing@org/create-<name>shorthand on miss.vp create --no-interactiveinside a repo withcreate.defaultTemplateset → prints the manifest as a machine-parseable table and exits 1.vite:*builtins, local workspace packages, or bundled subdirectories (./templates/fooextracted from the@org/createtarball with SHA-512 integrity into$VP_HOME/tmp/create-org/...).monorepo: trueentries are hidden in existing monorepos; direct selection refused with the same message asvite:monorepo.Spec:
rfcs/create-org-default-templates.md(4 review rounds). Docs:docs/guide/create.md§ Organization Templates + newdocs/config/create.md.Testing: 264 unit tests pass, 8 snap-test fixtures under
packages/cli/snap-tests/create-org-*(shared mock-registry helper at.shared/mock-npm-registry.mjs).Note
Medium Risk
Adds new network-facing template discovery (registry fetch + optional tarball download/extraction) and new config-driven behavior for
vp create, which can affect scaffolding flows and security boundaries around path/tar handling despite added validation and tests.Overview
vp createnow supports organization-scoped template catalogs:vp create @orgfetches@org/create’screateConfig.templatesmanifest, shows an interactive picker (with a built-in-templates escape hatch), and@org:name[@version]directly selects a manifest entry.Adds per-repo defaults via
vite.config.ts(create.defaultTemplate), applies correct precedence (CLI arg > config > built-in picker), and extends--no-interactivebehavior to print a stable manifest table + exit 1 when an org scope needs a selection.Implements bundled templates referenced by relative paths (e.g.
./templates/demo) by downloading and extracting the@org/createtarball into a cache, copying the selected subdirectory, validating manifests/paths, filteringmonorepo: trueentries inside monorepos, and improving npm registry/auth handling via new.npmrc-awarenpm-configutilities. Updates CLI help snapshots, adds extensive unit + snap-test fixtures (including a mock npm registry), updates docs/site nav, and introducesnanotardependency.Reviewed by Cursor Bugbot for commit 428a4b4. Configure here.