Skip to content

Bring create-launchpad-app into the repo, with framework selection - #152

Open
PaulBratslavsky wants to merge 12 commits into
mainfrom
feat/create-launchpad-app-cli
Open

Bring create-launchpad-app into the repo, with framework selection#152
PaulBratslavsky wants to merge 12 commits into
mainfrom
feat/create-launchpad-app-cli

Conversation

@PaulBratslavsky

@PaulBratslavsky PaulBratslavsky commented Sep 8, 2026

Copy link
Copy Markdown
Collaborator

Moves the create-launchpad-app CLI into this repository and teaches it to
scaffold any of the four frontends.

npx create-launchpad-app my-app

It asks which frontend you want, then clones, installs, seeds and starts it.
--framework astro skips the question.

Draft for review — please do not merge yet.

Why it belongs here

The CLI offers a --framework flag, so it carries its own copy of the frontend
list: names, ports and dev scripts. It has to be a copy, because the CLI runs
before the repo is cloned.

Kept in a separate repository, that copy drifts silently. Rename a frontend or
change a port here and the CLI keeps offering the old one, then checks the
wrong port or calls a dev script that no longer exists — a failure the user
hits, not us.

In-repo, a test can read the real sources. cli/test/registry-matches-launchpad.test.js
checks the CLI's registry against scripts/frontends.mts, the root
package.json scripts and the frontend directories themselves. It is not
vacuous — changing astro's port in the CLI fails with:

astro: CLI says port 9999, scripts/frontends.mts says 4321

Bugs fixed along the way

--package-manager npm could not work, and is removed. npm setup is not a
valid npm command — nor are npm seed or npm dev, which the flag also ran.
Each needed npm run. The flag never worked, so nobody can be relying on it.

(An earlier version of this description also claimed Corepack refuses npm here
because the root package.json pins yarn@4.5.0. That is wrong: npm install
and npm run dev both work fine. packageManager constrains which yarn or
pnpm Corepack shims in, not whether npm may run. The invalid commands are the
whole reason.)

Also: the port check was hardcoded to 1337/3000 and would have checked the wrong
port for every frontend but Next; --no-start printed yarn dev regardless of
the frontend chosen; the version was written out in both package.json and
cli.js; TOTAL_STEPS was a constant while the steps were already conditional;
the Node floor was 18, but Nuxt 4 and Astro 6 need 20.19; and the clone stripped
.git without ever running git init, leaving the project untracked.

Flags

Flag Behavior
-f, --framework <name> Frontend to run. Prompts if omitted.
-r, --ref <ref> Branch or tag to clone.
--repo <url|path> Clone from a fork, or a local checkout.
--dry-run Print the plan and exit.
--no-seed, --no-start, --no-git Skip those steps.

--repo takes a path as well as a URL, so testing needs neither the network nor
a pushed branch — local sources are rewritten to file:// so --depth still
applies.

One temporary piece to delete on merge

needsRef in cli/src/frameworks.js makes astro, nuxt and tanstack clone
feat/tanstack-frontend, since they are not on the default branch yet. It
carries a comment saying to remove it once #150 merges. Until then the picker
can still offer all four and each one works.

Stacking

Targets feat/tanstack-frontend (#150), because the CLI configures the
frontends that PR adds. Merge #150 first — GitHub will retarget this to
main automatically.

Verification

10 tests pass. Scaffolded end to end from both GitHub and a local checkout: four
frontends cloned, a git repo initialized with a 563-file commit, .env files
written, dependencies installed. All four resolve to their own dev script and
port, next/dev/3000 through tanstack/dev:tanstack/3002. npm pack ships
9 files.

🤖 Generated with Claude Code

codingafterthirty and others added 3 commits September 8, 2026 11:26
The CLI scaffolds this repo and offers a --framework flag, so it carries
its own copy of the frontend list: names, ports and dev scripts. It has to
be a copy, because the CLI runs before the repo is cloned.

Kept in a separate repo, that copy drifts silently. Rename a frontend or
change a port here and the CLI keeps offering the old one, then checks the
wrong port or calls a dev script that no longer exists — a failure the user
hits, not us.

In-repo, a test can read the real sources.
cli/test/registry-matches-launchpad.test.js checks the CLI's registry
against scripts/frontends.mts, the root package.json scripts and the
frontend directories themselves. Verified it catches drift: changing
astro's port in the CLI fails with "CLI says port 9999,
scripts/frontends.mts says 4321".

cli/ uses npm rather than yarn. It is published to npm separately and is
not part of the yarn setup that installs the frontends, so `yarn cli` and
`yarn test:cli` install its dependencies on demand — both work from a fresh
clone with no extra step.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZNfEU8h231jo1hu7kcviZ
The prompt listed all four frontends regardless of which ref was being
cloned, so choosing Astro against main got you an error explaining that
Astro is not there. Offering a choice and then refusing it is worse than
not offering it.

The clone now happens first and the prompt is built from the frontend
directories found in it. Against main that is next alone, so the prompt is
skipped entirely rather than showing a one-item menu. Against a ref with
all four, all four are offered.

--framework is validated against the same list, and distinguishes a
frontend that exists but is missing from this ref ("try --ref ...") from a
name that is not a frontend at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZNfEU8h231jo1hu7kcviZ
Three problems, all found by running it.

The picker was skipped whenever the clone held a single frontend, which on
LaunchPad's default branch is always. Running with no flags chose Next
silently and never showed the menu. It now always lists all four.

Choosing a frontend that is not on the default branch used to fail with an
error telling you to pass --ref. The ref is now derived from the choice:
astro, nuxt and tanstack fetch feat/tanstack-frontend and say so, next
takes the default branch, and an explicit --ref still wins. `needsRef` in
src/frameworks.js carries a note to delete it once the multi-frontend PR
merges. The post-clone check stays as a safety net for a stale --ref or a
renamed directory.

The clone spinner sat motionless for the ~19 seconds LaunchPad's 54MB takes
to fetch, which reads as a hang. It now counts elapsed seconds and says
what it is waiting for. git's own --progress was tried first and reverted:
it emits thousands of carriage-return updates that do not collapse when
stdout is not a TTY, which is worse than no feedback at all.

Adds --repo, taking a fork URL or a local path. git clones from a path, so
testing needs neither the network nor a pushed branch; local sources are
rewritten to file:// so --depth still applies. Verified by scaffolding from
a local checkout — the clone becomes instant and only yarn install costs
time.

Checked all four end to end: each resolves to its own dev script and port,
next/dev/3000 through tanstack/dev:tanstack/3002.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZNfEU8h231jo1hu7kcviZ
@vercel

vercel Bot commented Sep 8, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
launch-pad Ready Ready Preview Sep 9, 2026 5:25pm UTC

Request Review

Scaffolding with --framework astro left CLIENT_URL at http://localhost:3000,
so the Preview button in the Strapi admin opened Next while the site being
worked on was Astro on 4321.

`yarn setup` propagates the shared PREVIEW_SECRET to every frontend but
leaves CLIENT_URL alone, because it cannot know which frontend you intend to
run. `yarn use <framework>` is the command that sets it, and the CLI knows
the answer, so it now runs it after setup.

PREVIEW_SECRET was already correct — verified identical across strapi and
all four frontends. Only the URL was wrong.

Verified per frontend: next 3000, astro 4321, nuxt 3001, tanstack 3002.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZNfEU8h231jo1hu7kcviZ
The port check only ran when the CLI was about to start the servers, so
scaffolding with --no-start said nothing about a conflict. It also did not
work.

`isPortAvailable` bound a socket to decide, with no host. Node then listens
on IPv6 `::`, which on macOS does not collide with Strapi's IPv4 `0.0.0.0`,
so an occupied port reported as free. Astro is worse: it binds `[::1]`
specifically, and on BSD a wildcard bind coexists with a loopback one. No
bind probe covers every combination, so it connects instead — if something
accepts, the port is taken, whatever it bound to. Verified against a live
Strapi and Astro, which both used to report free.

The check now runs as soon as the frontend is known, before cloning, and
reports which directory holds each port. Two LaunchPad checkouts is the
case worth naming: the second one answers on the right port with its own
PREVIEW_SECRET, so the admin's preview fails with "Invalid token" and
nothing points at a port conflict. That is exactly how this was found.

    ⚠ Port 4321 (Astro) is already in use.
        held by: /Users/paul/Desktop/lp-cli-test/my-app/astro

Before starting it stays a hard failure; up front it is a warning, since
--no-start means nothing is about to bind anything.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XZNfEU8h231jo1hu7kcviZ
@PaulBratslavsky
PaulBratslavsky changed the base branch from feat/tanstack-frontend to main September 9, 2026 15:35
PaulBratslavsky and others added 2 commits September 9, 2026 08:35
astro, nuxt and tanstack are on the default branch, so the CLI no longer
needs to send those choices to feat/tanstack-frontend. Removes
MULTI_FRONTEND_REF, needsRef and resolveRef, along with the notice
explaining which branch was being used and why.

--ref stays, for forks and for pinning a tag.

Verified against a plain clone of main with no flags: all four frontends
present, CLIENT_URL on the chosen frontend's port, PREVIEW_SECRET matching
between Strapi and that frontend.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQSJu9Etpa6qjCny1CPWcc
create-launchpad-app is already published by someone else — an unrelated
package, currently at 1.0.38. The README told people to run
`npx create-launchpad-app`, which would have installed that instead of
this. The name was never available to take.

create-strapi-launchpad is free, and says whose LaunchPad it scaffolds.
`npm create strapi-launchpad` works as the shorthand.

The bin is renamed to match, along with the help examples, both READMEs
and AGENTS.md.

Verified the way a user gets it rather than through npm link: packed the
tarball, installed it globally, ran it from an unrelated directory. Nine
files ship, no tests or node_modules among them.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQSJu9Etpa6qjCny1CPWcc
1.0.0 claims a stable interface. The flags are a day old and have already
changed twice — --package-manager was removed, and the picker was rebuilt
after it turned out to be offering frontends the clone did not have.

0.x says that plainly and leaves room to keep changing them. npm versions
cannot be reused once published, so this is worth getting right before the
first publish rather than after.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQSJu9Etpa6qjCny1CPWcc
`yarn dev` is Next for every scaffold, so someone who chose Nuxt and typed
the obvious command gets the wrong frontend. Rather than change what `dev`
means — which would break it for existing contributors — the CLI now prints
the frontend-specific command before the dev servers take over the
terminal:

    To restart later:  cd my-app && yarn dev:nuxt

It goes before the servers start because their output scrolls past
immediately, and this is what people need after the first Ctrl+C. The
--no-start path already ended with it.

Also corrects a claim repeated in four places: that Corepack refuses npm
because the root package.json pins yarn@4.5.0. It does not. `npm install`
and `npm run dev` both work here; `packageManager` constrains which yarn or
pnpm Corepack shims in, not whether npm may run.

The real reason the CLI drives yarn is duller and true: every directory
ships a yarn.lock and its scripts shell out to yarn. Removing
--package-manager was still right, but for the reason that was actually
tested — `npm setup`, `npm seed` and `npm dev` are all "Unknown command",
since npm accepts bare names only for its own built-ins.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQSJu9Etpa6qjCny1CPWcc
The command is already shown before the servers start, but by then it is
long gone: a real run buries it under about 280 lines of server output. The
moment it is wanted is right after Ctrl+C, so it now has the last word.

    ℹ Stopped.

      Restart:          cd q2 && yarn dev:astro
      Other frontends:  yarn dev (Next.js), yarn dev:nuxt, yarn dev:tanstack

`yarn dev` is named as Next explicitly. It is the command people reach for,
and in a scaffold built for another frontend it quietly starts the wrong
one — better said out loud than discovered.

Needs an explicit SIGINT handler. Ctrl+C signals the whole process group, so
Node tears the process down before anything after the await runs. The first
version relied on that and printed nothing, which only showed up by sending
a real SIGINT rather than reading the code. SIGTERM is handled the same way,
and a flag keeps it to once if both the signal and the await fire.

`yarn dev` in the repo and in every scaffold is untouched.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQSJu9Etpa6qjCny1CPWcc
Two user-facing changes since 0.1.0:

- The restart command is printed when the dev servers stop, not only before
  they start, where about 280 lines of server output bury it.
- Corrects the claim that Corepack refuses npm here. It does not; the CLI
  drives yarn because every LaunchPad directory ships a yarn.lock and its
  scripts call yarn.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GQSJu9Etpa6qjCny1CPWcc
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