Skip to content

feat(create): support @org default templates#1398

Open
fengmk2 wants to merge 1 commit intomainfrom
vp-create-support-org
Open

feat(create): support @org default templates#1398
fengmk2 wants to merge 1 commit intomainfrom
vp-create-support-org

Conversation

@fengmk2
Copy link
Copy Markdown
Member

@fengmk2 fengmk2 commented Apr 16, 2026

Extends vp create so an organization can expose a curated set of templates through a single entry point. vp create @your-org fetches @your-org/create's vp.templates manifest from npm and renders an interactive picker over the listed entries. A create.defaultTemplate option in vite.config.ts makes an org the default for bare vp create. Zero-risk for orgs without a manifest — vp falls back to running @your-org/create as today.

Highlights

  • vp create @org → picker (with a Vite+ built-in templates escape hatch that stays reachable).
  • vp create @org/<name> → direct selection; falls back to the existing @org/create-<name> shorthand on miss.
  • vp create --no-interactive inside a repo with create.defaultTemplate set → prints the manifest as a machine-parseable table and exits 1.
  • Manifest entries accept npm specifiers, GitHub URLs, vite:* builtins, local workspace packages, or bundled subdirectories (./templates/foo extracted from the @org/create tarball with SHA-512 integrity into $VP_HOME/tmp/create-org/...).
  • monorepo: true entries are hidden in existing monorepos; direct selection refused with the same message as vite:monorepo.

Spec: rfcs/create-org-default-templates.md (4 review rounds). Docs: docs/guide/create.md § Organization Templates + new docs/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 vp create resolution paths that fetch and extract @scope/create tarballs and read .npmrc auth/registry settings; mistakes could break scaffolding flows or introduce edge-case security/file-path issues despite added validation and size/integrity checks.

Overview
vp create now supports organization template catalogs. vp create @scope can fetch @scope/create’s createConfig.templates manifest from the npm registry, show an interactive picker (with a built-in-templates escape hatch), and allow direct selection via @scope:<name> including --no-interactive table output.

Adds bundled-template support and safer registry handling. Manifest entries can point at relative ./... paths inside the @scope/create tarball, which is downloaded (with registry/.npmrc resolution + auth retry), integrity-checked, size-capped, extracted to a cache, and then scaffolded by directory copy; monorepo-only entries are filtered/blocked appropriately.

Introduces repo defaults and docs/tests. Adds create.defaultTemplate to vite.config.ts typing and vp create resolution precedence, updates help text and docs (new docs/config/create.md, expanded create guide), and adds extensive unit + snap tests plus a mock npm registry harness.

Reviewed by Cursor Bugbot for commit 7cdcde4. Configure here.

@netlify
Copy link
Copy Markdown

netlify Bot commented Apr 16, 2026

Deploy Preview for viteplus-preview canceled.

Name Link
🔨 Latest commit fa2cb10
🔍 Latest deploy log https://app.netlify.com/projects/viteplus-preview/deploys/69f05818278e320008afcabb

@fengmk2 fengmk2 self-assigned this Apr 16, 2026
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 16, 2026


How to use the Graphite Merge Queue

Add 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.

Comment thread rfcs/create-org-default-templates.md
fengmk2 added a commit that referenced this pull request Apr 16, 2026
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.
Comment thread rfcs/create-org-default-templates.md Outdated
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
@fengmk2 fengmk2 force-pushed the vp-create-support-org branch from d3c56db to 6d1e8f3 Compare April 23, 2026 07:11
Comment thread docs/guide/create.md Outdated
Comment thread docs/guide/create.md Outdated
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 23, 2026

@cursor review

Comment thread packages/cli/src/create/bin.ts
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 23, 2026

@cursor review

Comment thread packages/cli/src/create/org-resolve.ts Outdated
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 23, 2026

Running effect

image image image

Inside a monorepo
image

fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 23, 2026
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.
@fengmk2 fengmk2 force-pushed the vp-create-support-org branch from 764dd2d to f4ed12f Compare April 23, 2026 14:32
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 23, 2026

@cursor review

@fengmk2 fengmk2 changed the title docs(rfc): add RFC for @org default templates in vp create feat(create): support @org default templates via vp.templates manifest Apr 23, 2026
Comment thread packages/cli/src/create/bin.ts
@cpojer
Copy link
Copy Markdown
Member

cpojer commented Apr 24, 2026

It works:

CleanShot 2026-04-24 at 10 12 42@2x

When an entry is selected, it prints "selected 'web' from ankzw/create", but it shows the selection above. Can we get rid of that line?
CleanShot 2026-04-24 at 10 12 32@2x

I also noticed something I missed previously, this PR now supports something like vp create @nkzw/web which will pick the web template from @nkzw/create. I don't think we should support this "fake npm package" syntax, that's confusing and might create issues, like what if a real @nkzw/web package exists. If we want to support this shorthand, we need to come up with an unambiguous syntax, maybe @nkzw#web or @nkzw:web or similar.

@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 24, 2026

I also noticed something I missed previously, this PR now supports something like vp create @nkzw/web which will pick the web template from @nkzw/create. I don't think we should support this "fake npm package" syntax, that's confusing and might create issues, like what if a real @nkzw/web package exists. If we want to support this shorthand, we need to come up with an unambiguous syntax, maybe @nkzw#web or @nkzw:web or similar.

I would also be confused about this. Let's change it to the @nkzw:web syntax instead. This way the final command will be vp create @nkzw:web, which is consistent with the current built-in template syntax vp create vite:monorepo. @cpojer

fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
@fengmk2 fengmk2 force-pushed the vp-create-support-org branch from 428a4b4 to a5f36e0 Compare April 25, 2026 03:11
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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).
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
@fengmk2 fengmk2 marked this pull request as ready for review April 25, 2026 03:11
@fengmk2 fengmk2 changed the title feat(create): support @org default templates via vp.templates manifest feat(create): support @org default templates Apr 25, 2026
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 25, 2026

Upgrading to the alpha version allows early access to this feature:

vp upgrade --tag 0.1.20-alpha.3

vp create @nkzw

fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
fengmk2 added a commit that referenced this pull request Apr 25, 2026
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.
@fengmk2 fengmk2 force-pushed the vp-create-support-org branch from a65de7d to 7cdcde4 Compare April 25, 2026 09:45
@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 25, 2026

@cursor review

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

Reviewed by Cursor Bugbot for commit 7cdcde4. Configure here.

Comment thread packages/cli/src/create/bin.ts
@cpojer
Copy link
Copy Markdown
Member

cpojer commented Apr 26, 2026

I have 5 repos in my template, does this somehow limit to showing only 4?

CleanShot 2026-04-26 at 22 34 02@2x

@fengmk2
Copy link
Copy Markdown
Member Author

fengmk2 commented Apr 27, 2026

I have 5 repos in my template, does this somehow limit to showing only 4?

@cpojer Are you executing in a monorepo? If so, it will automatically ignore the template of the monorepo.

@socket-security
Copy link
Copy Markdown

socket-security Bot commented Apr 27, 2026

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednpm/​nanotar@​0.3.010010010081100

View full report

@cpojer
Copy link
Copy Markdown
Member

cpojer commented Apr 28, 2026

@cpojer Are you executing in a monorepo? If so, it will automatically ignore the template of the monorepo.

Ah, it wasn't immediately obvious, but that makes sense!

Extend `vp create` so an organization can expose a curated set of
templates through a single entry point: `vp create @your-org` opens an
interactive picker over the org's manifest, `vp create @your-org:web`
runs a specific entry directly, and `create.defaultTemplate` in
`vite.config.ts` makes an org the default for bare `vp create`.

The manifest lives in `@your-org/create`'s `package.json` under
`createConfig.templates` (tool-neutral key, parallel to `publishConfig`).
Each entry carries `{ name, description, template, monorepo? }`. The
`template` field accepts npm specifiers, GitHub URLs, `vite:*`
builtins, local workspace packages, or relative paths
(`./templates/foo`) that resolve against the extracted `@org/create`
tarball — letting an org ship N templates from one package.

Highlights:

- Spec syntax: `@scope` (picker), `@scope:name` (direct entry),
  `@scope@version` / `@scope:name@version` (pinned). The colon
  separator avoids collision with real `@org/package` npm specifiers
  and the existing `@org/create-name` shorthand.
- Bundled subdirectory templates: tarball downloaded once over HTTPS,
  SHA-512 integrity-verified, extracted via nanotar to
  `$VP_HOME/tmp/create-org/<host>/<scope>/create/<version>/`. The
  `<host>` segment (sanitized for Windows-illegal characters) keeps
  cross-registry resolutions from sharing a slot. Extracts atomically
  via `<dir>.tmp-…` staging + rename; sibling staging older than 24h
  is pruned at the start of each fresh extract.
- `.npmrc` registry/auth: walk-up parser that layers user → project
  → env, honoring `@scope:registry=...` overrides and resolving
  credentials via `_authToken` / `_auth` / username:_password.
  Retries with auth only on 401/403 — public registries never see a
  token.
- `monorepo: true` entries are filtered from the picker inside an
  existing monorepo and rejected on direct selection there. When a
  bundled monorepo entry scaffolds successfully outside one, the
  generated `vite.config.ts` gains
  `create: { defaultTemplate: '@scope' }` so a bare `vp create` from
  inside the new workspace opens the same org's picker.
- Builtin escape hatch: the org picker always appends a "Vite+
  built-in templates" entry routing to the standard
  `getInitialTemplateOptions` flow.
- `--no-interactive` prints the manifest as a fixed-column table so
  agents and CI scripts can recover available names.
- Git-init prompt unified across the `vite:monorepo` and
  bundled-`@org`-monorepo paths in `bin.ts`. After `git init` succeeds,
  `ensureGitignoreNodeModules` writes / appends `node_modules` so
  bundled templates without their own `.gitignore` don't track
  installed deps on the first commit.
- Schema rules: names are kebab-case, must be unique, and the `__vp_`
  prefix is reserved for internal sentinels (e.g. the picker's
  per-call escape-hatch UUID). Bundled paths that escape the package
  root are rejected at validation time, before any tarball fetch.
- Tar entries preserve their stored mode (so `gradlew` and friends
  stay executable); setuid/setgid/sticky bits are stripped.

Net change: 4132 insertions across 70 files. Ships
`packages/cli/src/create/org-{manifest,resolve,picker,tarball}.ts`,
`packages/cli/src/create/templates/bundled.ts`, the new
`packages/cli/src/utils/npm-config.ts` module, the
`UserConfig.create.defaultTemplate` augmentation, the
`injectCreateDefaultTemplate` and `ensureGitignoreNodeModules`
helpers, plus the integration in `bin.ts` and the `discoverTemplate`
manifest branch.

Tests: 12 new unit-test files (~230 specs) covering schema parsing,
version pinning, scope/auth resolution, picker filtering + escape
hatch, tarball cache key + extract atomicity, gitignore edge cases,
and the migrator helper. Eight snap-test fixtures under
`packages/cli/snap-tests/create-org-*` exercise the end-to-end flow
through a shared mock npm registry, including the new
`create-org-bundled-monorepo` case that verifies
`create.defaultTemplate` injection and `git init` for bundled
monorepo templates.

Docs: new `docs/guide/create.md § Organization Templates`, new
`docs/config/create.md` reference, README pointer to viteplus.dev,
and a fully-revised RFC at `rfcs/create-org-default-templates.md`
documenting the design as shipped.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants