1// Set up the project in the current folder — one deterministic call from "the folder and the2// brief" to "brand layer can start". The folder decides (install/context.mjs folderShape, five file3// facts: wix.config.json, its site.outputDirectory, the migration variables in .env.local,4// package.json, index.html):5//6// node <SKILL_ROOT>/install/setup.mjs --vertical <storefront|bookings|…>[,<vertical>…] \7// [--plan plan.json] [--business-name "<Brand>"] [--stack astro|react|lib|static] \8// [--site <metaSiteId> --app <appId>] [--subfolder [--folder-name <npm-safe-name>]]9//10// empty (or loose files: a CSV, a brief) → CREATE: `wix create` with the vertical's composed template11// (templates/<vertical>/project), placed in the current directory (`--business-name` required).12// With --site and --app (a site provisioned through the Wix MCP: the metaSiteId and appId the13
98if (siteFlag && !(UUID.test(siteFlag) && UUID.test(appFlag))) fail("args", "--site and --app are UUIDs (the provision call's metaSiteId and appId)");
99if (siteFlag && stackFlag && stackFlag !== "astro") fail("args", `--site takes the astro stack: the composed template is copied in and released as a build; --stack ${stackFlag} does not apply`);
100
101// The env the plugin route's build reads: the public client id, the cloud provider the hosting runs
102// the server build on, and stand-ins for the three values the hosting runtime injects at release (the
129// The plugin route's release of an Astro project: the build, the two zips, the upload tool.
130const ZIP_PY = (name, skip) => `python3 -c "import zipfile,pathlib as P; X=(${skip.map((s) => `'${s}'`).join(",")}); z=zipfile.ZipFile('${name}','w',zipfile.ZIP_DEFLATED); [z.write(p) for p in P.Path('.').rglob('*') if p.is_file() and p.parts[0] not in X and not p.name.startswith('.env') and str(p) not in ('project.zip','build.zip','source.zip')]; z.close()"`;
131const pluginRelease = (head) =>
132 `npx astro build from the project root (the Wix hosting adapter writes .wix/build-metadata.json and dist/), then release with the Wix MCP's UploadHeadlessWebsiteFiles, siteId ${siteFlag}, in one of two forms. Attached: one zip of the project without node_modules (zip -qr project.zip . -x 'node_modules/*' '.astro/*' '.agents/*' '.env*'; without a zip binary: ${ZIP_PY("project.zip", ["node_modules", ".astro", ".agents"])}) passed as attachments. From a shell that reaches the internet (the upload URLs are on Amazon S3: curl -sI https://s3.amazonaws.com shows Server: AmazonS3 where they work; a sandbox limited to a few hosts gets a proxy's refusal without that header and cannot release this way): the build zip (zip -qr build.zip dist .wix) and the source zip (zip -qr source.zip . -x 'node_modules/*' 'dist/*' '.wix/*' '.astro/*' '.agents/*' '.env*' 'build.zip'; without a zip binary: ${ZIP_PY("source.zip", ["node_modules", "dist", ".wix", ".astro", ".agents"])}), then { siteId, prepare: true, size } with size from wc -c < build.zip, the curl -X PUT commands its reply gives, run as given, and the second call it names, { siteId, uploadKey, sourceUploadId, expectedHeadRevisionId }. ${head} The tool releases the build to the site's Wix hosting, stores the source in the site's Code Store repository and returns the live URL; EXPECTED_HEAD_REQUIRED or a 409 CODE_STORE_HEAD_CHANGED released nothing (SKILL.md step 5). Not wix build, not wix release: this run has no Wix CLI session`;
133
134// ---- 0 · read the folder --------------------------------------------------------------------------
156 next: `a project downloaded from the site's Code Store repository: nothing scaffolded, deployed or seeded. Edit the files the change needs (deploy.mjs <vertical…> --stack astro adds a solution, then node ${join(SKILL_ROOT, "install", "install.mjs")}); then ${pluginRelease("expectedHeadRevisionId is the revision.id the download returned, in either form.")}`,
157 });
158 process.exit(0);
159 }
160 if (shape !== "migration" && shape !== "published-static") {
161 fail("place", `this folder is already a Wix project (wix.config.json), shape "${shape}": nothing to set up. ${ctx.folder.next} (SKILL.md step 3)` + (ctx.pullError ? ` — note: env pull failed here (${ctx.pullError.slice(0, 120)})` : ""));
164 fail("args", `a published static site (index.html beside wix.config.json, no package.json) is the static stack; --stack ${stackFlag} does not apply`);
168if (siteFlag && mode !== "create") fail("args", `--site applies to an empty folder (shape "empty"); this folder is "${shape}": ${folderShape(cwd).next}`);
317 fail("place", `the current directory already has: ${clashes.join(", ")} — run with --subfolder to keep the project in ${folderName}/, or start in an empty folder`);
318 }
319 try {
320 for (const entry of entries) renameSync(join(projectDir, entry), join(cwd, entry));
363 emit("seed_skipped", { reason: `the seed modules run with the Wix CLI's session, and this run has none: on the plugin route the plan is seeded through ExecuteWixAPI, as the cold start's run describes (step 4, https://www.wix.com/headless/skill.md), with the vertical's SEED.md (${join(TEMPLATES, vertical, "seed", "SEED.md")}) as the contract` });
364} else if (planPath && !madeTheSite) {
365 emit("seed_skipped", { reason: `${mode}: the site existed before this run; setup seeds only a site it created. Read what the site holds (${join(TEMPLATES, vertical, "seed", "read-site.mjs")}), then run templates/${vertical}/seed/seed-*.mjs ${planPath} yourself when the brief supplies or describes content` });
377 emit("seeding_started", { vertical, ...seed, ...(verticals.length > 1 ? { note: `the plan seeds ${vertical}; the other verticals' seeds run afterwards, each with its own plan` } : {}) });
382 astro: pluginRelease("expectedHeadRevisionId is the headRevisionId the prepare returned; an attached release of this new site's first build leaves it out (the repository has nothing in it yet)."),
383}[stack] : {
384 astro: `npx ${WIX_CLI} build, then npx ${WIX_CLI} release`,
385 react: `the project's own build, then npx ${WIX_CLI} release of the build folder named in wix.config.json (what Wix hosting serves and how routes must be shaped: SKILL.md step 1)`,
386 lib: `the project's own build, then npx ${WIX_CLI} release of the build folder named in wix.config.json (SKILL.md step 1)`,
387 static: `npx ${WIX_CLI} release — no build; it uploads ${STATIC_OUT}/ whole (wix.config.json site.outputDirectory), so the pages, styles and assets move into ${STATIC_OUT}/ first and import the modules from ./js/wix/ there; the root keeps the config, the plan, the seed output and the skills`,
388}[stack];
389// On a migration preview the links and `siteId` are the PARENT's — the site whose content the pages
390// show and whose dashboard manages it; `deploySiteId` is where `wix release` goes.
407 (publishedStatic ? "a published static site: its pages are live on this site already and keep their URL; move index.html, the other pages, styles and assets into site/ (the root keeps the config, the plan, the seed output and the skills) and wire the solution into the page that needs it; " : "") +
408 (mode === "migrate"
409 ? "a migration preview: the site being migrated owns its content (read it with the vertical's read-site.mjs when the brief allows probing; never seed it); theme + write the home page; "
410 : madeTheSite && publisher === "plugin"
411 ? `the site is new and empty: seed it through ExecuteWixAPI (the cold start's run, step 4, the plan per step 2 and the vertical's SEED.md as the contract${planPath ? `, the plan ${planPath}` : ", a plan drafted from the brief"}), and name the placeholder content in the closing message; theme + write the home page; `
412 : madeTheSite
413 ? (planPath ? "theme + write the home page; " : "the site is new and empty and no plan was given, so nothing was seeded yet: seed it now (a plan per step 2 — the brief's content, or one drafted per the vertical's SEED.md — then the vertical's seed module), and name the placeholder content in the closing message; theme + write the home page; ")
414 : `nothing was seeded (setup seeds only a site it created): read what the site holds with ${join(TEMPLATES, vertical, "seed", "read-site.mjs")}, then run ${join(TEMPLATES, vertical, "seed")}/seed-*.mjs <plan> when the brief supplies or describes content; theme + write the home page; `) +
415 (others.length && mode !== "migrate"
416 ? `${others.join(", ")} deployed too, no further install needed: run each one's seed module (templates/<vertical>/seed/) with its own plan when the brief gives it content (the members seed installs the Members Area app and needs no plan); `
417 : "") +
418 (install || seed ? "then wait for the done markers" + (seed ? ", verify .seed-exit is 0 (else read seed.log and re-run the seed module)" : "") + "; " : "") +
419 `then ${release}` +
420 (mode === "migrate" ? "; the release is the PREVIEW on the deploy site — close with its URL and the parent's dashboard, say the original site is unchanged and offer to complete the migration once they approve — guides/migration.md" : ""),