start

Start a project on the Visual Stack — a multi-step setup form captures what's being built, recommends a stack, and either instantiates the vstack-template-base template, sets up a specs-and-design-onl

SKILL.md

GitHub

Turns an idea — or an existing repo — into this project. One multi-step form, then whichever of three paths its answers call for.

                                   ┌─ skip dev ──► specs/ + design/ only (no clone)
  idea ──► setup form ──► choice ──┤
                                   └─ full ─────► clone ──► delete the rest ──► toolchain ──► ready
  existing code ──► infer ──► confirm screen ──► recorded (nothing touched)

Every path ends with two files the rest of the chain reads: specs/product.md — what this product is — and .vstack/pipeline.json — where the pipeline stands.

1 · Pick the mode

Look at the target directory first:

The directory is Mode
empty, missing, or has no real code fresh — the form runs before anything exists
template-derived (stacks/, add-ons/, design/, specs/ all present) template — don't clone again; the form reads the real inventory
a real codebase that isn't template-derived existing — infer, confirm, record; never scaffold or delete

In fresh mode, don't interrogate up front — the form asks the questions. The only thing worth asking before serving it is where the project directory should go if the user hasn't said.

2 · Serve the form

SKILL=<this skill dir>
node "$SKILL/assets/chooser-server.mjs" --repo <project dir> --port 7799

Start it with run_in_background: true, then tell the user to open http://localhost:7799/.

The form is four steps: what are you building → details (kind, name, one-liner) → stack & add-ons, with a pack recommended from the earlier answers — → confirm. On step 3 the user can instead skip development setup entirely; the whole path then produces UI and specs only.

Where the inventory comes from:

  • Template mode (repo has stacks/ + add-ons/): scanned from disk — a pack added upstream appears with no change here.

  • Fresh mode (nothing on disk yet): the server fetches the live stacks/ + add-ons/ listing of vstack-template-base from GitHub ("source": "github"); offline or rate-limited it falls back to its built-in catalog snapshot ("source": "snapshot"). Either way, after cloning, reconcile: if the chosen pack or an add-on doesn't exist in the cloned template, stop and re-ask; if the template ships packs the form didn't show, mention them before deleting anything.

  • Existing mode: no inventory. Infer the facts first — read package.json / lockfiles / framework configs / the directory layout / README / recent git log — write them as a prefill, and serve:

    node "$SKILL/assets/chooser-server.mjs" --repo <dir> --mode existing --prefill <dir>/.vstack/prefill.json
    

    Prefill shape: { "building": "webapp", "kind": "saas", "name": "…", "oneLiner": "…", "detected": "Next.js 14 app router, Prisma + Postgres, deployed on Vercel", "startAt": "confirm" }. The page opens on the confirm step with everything filled in and editable.

It asks once: on send it writes <repo>/.vstack/choice.json and exits. Nothing stays listening. That makes this the one place in the stack that waits rather than watches — there is a single answer coming, so the run_in_background: true start above is also the wait: the server's exit re-invokes you, and that is the moment to read <repo>/.vstack/choice.json. (The review and bridge tools stream instead, because their pages keep talking.)

If the user would rather answer in chat, that's fine — ask the same questions in prose and write the same JSON yourself. Ask in product terms when you do — "will several organisations share one deployment, with their data kept separate?", never "do you want multi-tenancy?". The form is the nicer path, not the only one.

3 · Act on the choice

choice.json carries mode, building, kind, name, oneLiner, skipDev, pack, addons, deleting, and (existing mode) detected. Three paths:

3a · Development skipped → the minimal workspace

No clone. In the project directory:

mkdir -p <dir>/specs <dir>/design

Write specs/product.md (§4) and .vstack/pipeline.json (§6) with "specsOnly": true. Done — say that /vstack:review and the spec stages now have a home, and that running /vstack:start again later adds real development setup.

3b · Existing app → record only

Write specs/product.md and .vstack/pipeline.json; create specs/ and design/ only if missing. Touch nothing else — no scaffolding, no deletions, no template. The detected text goes into product.md as the stack section.

3c · Full setup → clone and instantiate

With a GitHub repo — keeps the template link and the history clean:

gh repo create <name> --template Cavalry-Collective/vstack-template-base --private --clone

Local only:

git clone --depth 1 https://github.com/Cavalry-Collective/vstack-template-base.git <name>
rm -rf <name>/.git && git -C <name> init && git -C <name> add -A

Then read the contracts — root CLAUDE.md, apps/backend/CLAUDE.md, apps/frontend/CLAUDE.md, db/CLAUDE.md, infra/CLAUDE.md — and, if the choice came from the snapshot, reconcile it (§2) before deleting anything.

Adoption is deletion. Show the delete list, get a yes, then:

cd <repo>
for p in $(jq -r '.deleting.packs[]'  .vstack/choice.json); do rm -rf "stacks/$p"; done
for a in $(jq -r '.deleting.addons[]' .vstack/choice.json); do rm -rf "add-ons/$a"; done

Record the choice in root CLAUDE.md Learnings: Stack: <pack>; appendices under stacks/<pack>/. Then the rest of the Day-1 checklist, unchanged:

  • Toolchain from the surviving pack's README.md: its dev command block → the root CLAUDE.md Common commands placeholder (delete the banner); its CI block → .github/workflows/ci.yml (different blocks — a dev-only migration command in CI is a real bug); deploy.yml filled or deleted per the pack's conflict register; a real .env.example; the remaining ci.yml gates the pack doesn't cover — i18n key parity, migration up/down round-trip, a11y scan.
  • Primary form factor in apps/frontend/CLAUDE.md — mobile-first, desktop-first or responsive-equal. Ask; it isn't inferable and it changes every screen.
  • Rebrand and confirm the design guide — edit the primitive tier in design/tokens.css to the project's brand, open design/design-guide.html, and have the user confirm it reads as one system. This gates everything visual — no screen gets built against an unconfirmed system.
  • Copy runtime config — .env and any secrets, which don't come from the template.

4 · Write specs/product.md

Every path writes it. It is the product's constitution — the durable answer to what is this, which every later stage reads before doing anything. Build it from choice.json plus whatever the conversation has already established — constraints, audience, non-goals the user has mentioned:

# <name>

<one-liner>

## What it is
<building · kind, in plain words — and anything the conversation added>

## Stack
<pack + add-ons | the detected stack, verbatim from the confirm screen | "Specs & design only — no development setup yet">

## Constraints & principles
<only what's actually been said — never invent principles to fill the section>

Thin is fine. A three-line product.md that is all true beats a page of boilerplate.

5 · Hand back the repo-level steps (full setup only)

Protect main with a required-CI rule, stand up staging if the pack defines one, confirm the first CI run is green. Don't do these unasked — print them as a checklist and offer to run any of them. Branch protection must come after the first green push, or a required-status rule rejects a branch whose CI has never run.

Then confirm no placeholder survived — both must return nothing:

grep -rn 'FILL IN ON SETUP\|TODO: replace' . --exclude-dir=stacks --exclude-dir=specs --exclude-dir=.git \
  | grep -v '^\./README\.md:'
grep -n '^<pm> ' CLAUDE.md

Delete the template's own root README.md once instantiation is done — its Day-1 checklist is spent.

Notes

  • Never edit assets/chooser.html or chooser-server.mjs to fit a project — they're the engine. The shell chrome is stamped in from lib/shell/ — see lib/shell/README.md.
  • The form is a one-shot by design: it asks once, so it doesn't use the live-link machinery behind wireframe and user-story-map. Don't wire it into them.
  • Unknown directories still work. A pack or add-on with no built-in blurb renders from its own README, so an upstream addition never breaks the page.
  • Flags: --out moves choice.json; --port moves the port (busy usually means a form is already up); --project names the project when --repo doesn't exist yet; --mode existing + --prefill <file> is the existing-app path.
  • The page always sends straight to the server — there is no copy-paste fallback. If the server has gone away it shows a retry state and keeps the answers until the tab closes, so if a send fails, restart the server and have the user press Try again.
  • The snapshot catalog inside chooser-server.mjs mirrors the template's packs and add-ons. If the template gains one, add it there too — fresh mode can't scan what isn't cloned yet.

State & handoff

  • Read nothing — this is where the pipeline begins.
  • Write specs/product.md and <repo>/.vstack/pipeline.json, the file every later stage reads:
    { "version": 1, "project": "<name>", "stage": "start", "phase": null,
      "specsOnly": false,
      "artifacts": { "product": "specs/product.md", "stack": "<pack>", "addOns": ["…"] },
      "history": [{ "stage": "start", "at": "<iso>", "note": "<pack> + N add-ons" }] }
    
    "specsOnly": true and no stack/addOns keys on the skip-dev path; in existing mode stack is the detected description rather than a pack id. Commit it — it's a project record, not scratch. Keep .vstack/choice.json beside it as the raw answer.
  • Next — /vstack:requirements writes specs/requirements.md. That stage isn't built yet; until it is, say so and offer to write specs/requirements.md by hand together, or go straight to a first screen with /vstack:review. Don't invent a requirements stage to fill the gap. From there the built chain runs wireframe → spec → user-story-map → phase-preview → phase-build. The former /vstack:go router is no longer part of the pipeline; it is a compatibility alias for the wireframe tool.