@wix/agent-skills 1.31.0 → 1.31.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +7 -8
- package/skills/wix-app/references/DASHBOARD_PAGE.md +1 -1
- package/skills/wix-app/references/STORES_VERSIONING.md +1 -1
- package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +11 -50
- package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +14 -10
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +5 -8
- package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +5 -4
- package/skills/wix-headless-kit/SKILL.md +31 -14
- package/skills/wix-headless-kit/guides/api-run.md +116 -31
- package/skills/wix-headless-kit/guides/existing-site.md +9 -2
- package/skills/wix-headless-kit/guides/reference-mode.md +9 -1
- package/skills/wix-headless-kit/install/AGENTS.md +10 -7
- package/skills/wix-headless-kit/install/attach.mjs +35 -30
- package/skills/wix-headless-kit/install/background.mjs +32 -0
- package/skills/wix-headless-kit/install/bootstrap.mjs +28 -11
- package/skills/wix-headless-kit/install/check.mjs +2 -2
- package/skills/wix-headless-kit/install/context.mjs +62 -8
- package/skills/wix-headless-kit/install/deploy.mjs +36 -11
- package/skills/wix-headless-kit/install/env-ignore.mjs +16 -0
- package/skills/wix-headless-kit/install/install.mjs +95 -0
- package/skills/wix-headless-kit/install/pins.json +1 -1
- package/skills/wix-headless-kit/install/proc.mjs +46 -0
- package/skills/wix-headless-kit/install/setup.mjs +18 -27
- package/skills/wix-headless-kit/install/templates.mjs +4 -3
- package/skills/wix-headless-templates/blog/app/components/blog/RichContent.tsx +3 -2
- package/skills/wix-headless-templates/blog/project/package-lock.json +1287 -1299
- package/skills/wix-headless-templates/blog/project/package.json +1 -1
- package/skills/wix-headless-templates/blog/project/src/components/blog/RichContent.tsx +3 -2
- package/skills/wix-headless-templates/blog/rest/authors.ts +5 -5
- package/skills/wix-headless-templates/blog/rest/comments.ts +4 -4
- package/skills/wix-headless-templates/blog/rest/post-likes.ts +3 -3
- package/skills/wix-headless-templates/blog/rest/posts.ts +4 -4
- package/skills/wix-headless-templates/blog/rest/taxonomy.ts +5 -5
- package/skills/wix-headless-templates/blog/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/blog/seed/seed-blog.mjs +4 -3
- package/skills/wix-headless-templates/bookings/rest/booking.ts +4 -4
- package/skills/wix-headless-templates/bookings/rest/services.ts +4 -4
- package/skills/wix-headless-templates/bookings/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/bookings/seed/seed-bookings.mjs +4 -2
- package/skills/wix-headless-templates/cms/rest/collections.ts +3 -3
- package/skills/wix-headless-templates/cms/rest/items.ts +4 -4
- package/skills/wix-headless-templates/cms/seed/seed-cms.mjs +2 -1
- package/skills/wix-headless-templates/donations/rest/campaigns.ts +4 -4
- package/skills/wix-headless-templates/donations/rest/donate.ts +3 -3
- package/skills/wix-headless-templates/donations/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/donations/seed/seed-donations.mjs +5 -3
- package/skills/wix-headless-templates/events/rest/events.ts +4 -4
- package/skills/wix-headless-templates/events/rest/orders.ts +4 -4
- package/skills/wix-headless-templates/events/rest/registration.ts +4 -4
- package/skills/wix-headless-templates/events/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/events/seed/seed-events.mjs +5 -3
- package/skills/wix-headless-templates/faq/rest/faq.ts +4 -4
- package/skills/wix-headless-templates/faq/seed/seed-faq.mjs +2 -1
- package/skills/wix-headless-templates/forms/rest/forms.ts +4 -4
- package/skills/wix-headless-templates/forms/rest/submissions.ts +4 -4
- package/skills/wix-headless-templates/forms/seed/seed-forms.mjs +2 -1
- package/skills/wix-headless-templates/members/rest/auth.ts +3 -3
- package/skills/wix-headless-templates/members/rest/members.ts +4 -4
- package/skills/wix-headless-templates/members/seed/seed-members.mjs +2 -1
- package/skills/wix-headless-templates/portfolio/rest/portfolio.ts +4 -4
- package/skills/wix-headless-templates/portfolio/seed/SEED.md +4 -2
- package/skills/wix-headless-templates/portfolio/seed/seed-portfolio.mjs +8 -7
- package/skills/wix-headless-templates/pricing-plans/rest/plan-access.ts +3 -3
- package/skills/wix-headless-templates/pricing-plans/rest/plans.ts +4 -4
- package/skills/wix-headless-templates/pricing-plans/rest/purchase.ts +3 -3
- package/skills/wix-headless-templates/pricing-plans/seed/seed-pricing-plans.mjs +2 -1
- package/skills/wix-headless-templates/rentals/rest/rentals.ts +4 -4
- package/skills/wix-headless-templates/rentals/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/rentals/seed/seed-rentals.mjs +4 -2
- package/skills/wix-headless-templates/restaurants/rest/menu.ts +4 -4
- package/skills/wix-headless-templates/restaurants/rest/ordering.ts +5 -5
- package/skills/wix-headless-templates/restaurants/rest/reservations.ts +4 -4
- package/skills/wix-headless-templates/restaurants/seed/SEED.md +1 -1
- package/skills/wix-headless-templates/restaurants/seed/seed-restaurants.mjs +6 -4
- package/skills/wix-headless-templates/shared/capabilities/site-search/rest/search.ts +4 -4
- package/skills/wix-headless-templates/shared/capabilities/site-search/seed/install.mjs +6 -4
- package/skills/wix-headless-templates/shared/seed/images.mjs +41 -5
- package/skills/wix-headless-templates/shared/seed/main.mjs +15 -0
- package/skills/wix-headless-templates/shared/seed/wix-cli.mjs +43 -11
- package/skills/wix-headless-templates/storefront/INSTRUCTIONS.md +1 -1
- package/skills/wix-headless-templates/storefront/rest/cart.ts +5 -5
- package/skills/wix-headless-templates/storefront/rest/catalog.ts +5 -5
- package/skills/wix-headless-templates/storefront/seed/SEED.md +44 -14
- package/skills/wix-headless-templates/storefront/seed/seed-store.mjs +23 -15
- package/skills/wix-manage/SKILL.md +28 -18
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +5 -1
- package/skills/wix-manage/references/app-installation/list-installed-apps.md +19 -9
- package/skills/wix-manage/references/automations/activation-status.md +52 -0
- package/skills/wix-manage/references/automations/api-catalog.md +81 -0
- package/skills/wix-manage/references/automations/build-and-manage-automations.md +34 -0
- package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +3 -0
- package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +277 -363
- package/skills/wix-manage/references/bookings/external-calendar-integration.md +3 -0
- package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +3 -0
- package/skills/wix-manage/references/cms/cms-data-items-crud.md +11 -2
- package/skills/wix-manage/references/cms/cms-publishing-flow.md +63 -16
- package/skills/wix-manage/references/custom-apps/use-app-skills-and-app-tools.md +3 -1
- package/skills/wix-manage/references/forms/update-form.md +3 -0
- package/skills/wix-manage/references/pricing-plans/create-and-update-pricing-plans.md +3 -0
- package/skills/wix-manage/references/restaurants/wix-restaurants-setup.md +3 -0
- package/skills/wix-manage/references/sites/create-headless-site.md +7 -50
- package/skills/wix-manage/references/sites/manage-oauth-apps.md +4 -4
- package/skills/wix-manage/references/sites/read-site-context.md +4 -0
- package/skills/wix-manage/references/sites/send-feedback-to-wix.md +92 -0
- package/skills/wix-manage/references/sites/upload-static-site.md +298 -283
- package/skills/wix-manage/references/stores/add-store-pages-to-site.md +3 -0
- package/skills/wix-manage/references/stores/change-store-currency.md +3 -0
- package/skills/wix-manage/references/stores/create-product-catalog-v3.md +3 -0
- package/skills/wix-manage/references/stores/find-products-query-and-search-catalog-v3.md +3 -0
- package/skills/wix-manage/references/stores/update-product-with-options.md +192 -22
- package/skills/wix-manage/references/bookings/create-appointment-service.md +0 -243
- package/skills/wix-manage/references/bookings/create-booking-service-from-prompt.md +0 -31
- package/skills/wix-manage/references/bookings/create-class-service.md +0 -222
- package/skills/wix-manage/references/bookings/create-course-service.md +0 -251
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
// `env pull` runs here when `.env.local` is missing (or --refresh): it is the source of the content
|
|
17
17
|
// identity, and the Astro build refuses to run without it. Non-interactive (CI=1); a failure is
|
|
18
18
|
// reported in `pullError` and the config's ids stand in, so a caller can still work offline.
|
|
19
|
-
import { spawnSync } from "node:child_process";
|
|
20
19
|
import { existsSync, readFileSync } from "node:fs";
|
|
21
20
|
import { join } from "node:path";
|
|
21
|
+
import { failure, npmSync } from "./proc.mjs";
|
|
22
22
|
const PINS = JSON.parse(readFileSync(new URL("./pins.json", import.meta.url), "utf8"));
|
|
23
23
|
const WIX_CLI = `@wix/cli@${PINS["@wix/cli"]}`;
|
|
24
24
|
|
|
@@ -60,7 +60,7 @@ export function readWixConfig(cwd) {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
const ENV_PULL = ["-y", WIX_CLI, "env", "pull"];
|
|
63
|
-
const runPull = (dir) =>
|
|
63
|
+
const runPull = (dir) => npmSync("npx", ENV_PULL, { cwd: dir, env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 180_000 });
|
|
64
64
|
|
|
65
65
|
/**
|
|
66
66
|
* `wix env pull` into `cwd/.env.local`, in place. Every project shape gets the command since Wix CLI
|
|
@@ -72,13 +72,15 @@ export function pullEnv(cwd) {
|
|
|
72
72
|
const envFile = join(cwd, ".env.local");
|
|
73
73
|
const r = runPull(cwd);
|
|
74
74
|
if (r.status === 0 && existsSync(envFile)) return { ok: true, via: "in place" };
|
|
75
|
-
return { ok: false, error: (r
|
|
75
|
+
return { ok: false, error: failure(r, `env pull produced no .env.local — is the Wix CLI logged in? (npx ${WIX_CLI} whoami)`).trim().slice(-400) };
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
/**
|
|
79
79
|
* Whether a frontend exists in this folder: a `package.json` (a bundled project), an `index.html` at
|
|
80
80
|
* the root (plain pages), or an `index.html` inside the folder the config's `site.outputDirectory`
|
|
81
|
-
* names (a static site laid out for release, pages under site/).
|
|
81
|
+
* names (a static site laid out for release, pages under site/). `publicIndex` and `astroPages` tell
|
|
82
|
+
* a dropped site on its Wix Dev Machine apart from an Astro project: the machine copies the drop's
|
|
83
|
+
* pages into public/ of an Astro starter that has no src/pages/. Nothing else counts — not this
|
|
82
84
|
* skill's code, not the skills folder, not AGENTS.md: those say who ran here, not what is here.
|
|
83
85
|
*/
|
|
84
86
|
export function frontendPresent(cwd = process.cwd()) {
|
|
@@ -89,13 +91,50 @@ export function frontendPresent(cwd = process.cwd()) {
|
|
|
89
91
|
rootIndex: has("index.html"),
|
|
90
92
|
outputIndex: !!outDir && outDir !== "." && has(join(outDir, "index.html")),
|
|
91
93
|
outputDirectory: outDir || null,
|
|
94
|
+
publicIndex: has(join("public", "index.html")),
|
|
95
|
+
astroPages: has(join("src", "pages")),
|
|
92
96
|
};
|
|
93
97
|
}
|
|
94
98
|
|
|
95
99
|
/**
|
|
96
|
-
*
|
|
100
|
+
* The Wix CLI's blank Astro starter (`wix-astro-blank`, templates/blank/project), still as the CLI
|
|
101
|
+
* left it: every package it declares is one of the blank template's. That is the one frontend the
|
|
102
|
+
* kit knows carries no work of its own — a Dev Machine's never-released site, a bare `wix create`
|
|
103
|
+
* — so a vertical's composed template can take its place (deploy.mjs). A starter with any package
|
|
104
|
+
* of its own is someone's project. The blank template's package.json is read when the templates
|
|
105
|
+
* are on disk; otherwise the starter's known package names stand in.
|
|
106
|
+
*/
|
|
107
|
+
const BLANK_STARTER = {
|
|
108
|
+
name: "wix-astro-blank",
|
|
109
|
+
packages: ["@astrojs/react", "@types/react", "@types/react-dom", "@wix/astro", "@wix/astro-pages", "@wix/astro-wix-hosting-adapter", "@wix/cli", "@wix/dashboard", "@wix/essentials", "@wix/sdk", "astro", "react", "react-dom", "typescript"],
|
|
110
|
+
};
|
|
111
|
+
export function blankStarter(cwd = process.cwd(), blankPkgPath = null) {
|
|
112
|
+
const pkgPath = join(cwd, "package.json");
|
|
113
|
+
if (!existsSync(pkgPath)) return false;
|
|
114
|
+
let pkg;
|
|
115
|
+
try { pkg = JSON.parse(readFileSync(pkgPath, "utf8")); } catch { return false; }
|
|
116
|
+
if (pkg.name !== BLANK_STARTER.name) return false;
|
|
117
|
+
let known = BLANK_STARTER.packages;
|
|
118
|
+
if (blankPkgPath && existsSync(blankPkgPath)) {
|
|
119
|
+
try {
|
|
120
|
+
const blank = JSON.parse(readFileSync(blankPkgPath, "utf8"));
|
|
121
|
+
known = Object.keys({ ...blank.dependencies, ...blank.devDependencies });
|
|
122
|
+
} catch { /* the known names stand in */ }
|
|
123
|
+
}
|
|
124
|
+
return Object.keys({ ...pkg.dependencies, ...pkg.devDependencies }).every((name) => known.includes(name));
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* What the folder IS, from a few file facts — the one classification every script and SKILL.md
|
|
97
129
|
* step 3 share: wix.config.json, its site.outputDirectory, the migration variables in .env.local,
|
|
98
|
-
* package.json, index.html
|
|
130
|
+
* package.json, index.html (at the root or in public/), src/pages/. `migrationActive` comes from
|
|
131
|
+
* siteContext (it needs `.env.local`).
|
|
132
|
+
*
|
|
133
|
+
* Two axes decide it: whether a site is bound to the folder (the config), and what frontend is
|
|
134
|
+
* here (`facts.frontend`: none, the CLI's untouched blank starter, the starter with plain pages in
|
|
135
|
+
* public/, plain pages with no bundler, or someone's project). The migration flag is read first.
|
|
136
|
+
* Anything the rows below do not name is `wix-project`, the fill-only path: never scaffold, init,
|
|
137
|
+
* reseed or delete.
|
|
99
138
|
*
|
|
100
139
|
* empty nothing that reads as a project → setup CREATE: the run makes the site, seeds the plan
|
|
101
140
|
* project a frontend, no wix.config.json → setup ADOPT: `init` gives it a new, empty site, seeds the plan
|
|
@@ -103,6 +142,12 @@ export function frontendPresent(cwd = process.cwd()) {
|
|
|
103
142
|
* migration a config whose .env.local declares an ACTIVE editor migration, with or without the blank
|
|
104
143
|
* Astro starter the download carries → setup MIGRATE; nothing seeded. Decided before the
|
|
105
144
|
* frontend test: the starter's package.json must not read as a project to iterate on
|
|
145
|
+
* static-in-astro a config, a package.json, public/index.html and no src/pages/ (a site published
|
|
146
|
+
* through the drop flow, on its Wix Dev Machine) → the pages stay plain files in
|
|
147
|
+
* public/; the static REST layer deploys beside them; build and release
|
|
148
|
+
* blank-starter a config and the CLI's blank starter with no work of its own (a Dev Machine's
|
|
149
|
+
* never-released site, a bare `wix create`) → attach.mjs on the config's site: the
|
|
150
|
+
* vertical's composed template takes the starter's place, install and seed run detached
|
|
106
151
|
* wix-project a config AND a frontend (package.json, or index.html in the output folder) → iterate:
|
|
107
152
|
* never scaffold, init or reseed; deploy.mjs adds a solution, edits, release
|
|
108
153
|
* published-static a config, index.html at the ROOT, no package.json, no laid-out output folder (a site
|
|
@@ -112,21 +157,30 @@ export function frontendPresent(cwd = process.cwd()) {
|
|
|
112
157
|
export function folderShape(cwd = process.cwd(), { migrationActive = false } = {}) {
|
|
113
158
|
const config = existsSync(join(cwd, "wix.config.json"));
|
|
114
159
|
const f = frontendPresent(cwd);
|
|
160
|
+
const starter = f.packageJson && blankStarter(cwd);
|
|
115
161
|
const next = {
|
|
116
162
|
empty: "setup.mjs --vertical <v> --business-name <brand> [--plan]: creates the site here and seeds the plan (CREATE)",
|
|
117
163
|
project: "setup.mjs --vertical <v> --stack <stack> [--plan]: init links the folder to a new, empty site, seeds the plan and deploys (ADOPT)",
|
|
118
164
|
"config-only": "attach.mjs: the site exists and has no frontend yet; read what it holds (the vertical's read-site.mjs) — nothing is seeded; the vertical's seed module with a plan only when the brief supplies or describes content",
|
|
119
165
|
migration: "setup.mjs (MIGRATE): the shipped code into the starter the download carries (or the composed template around a bare config); the site being migrated owns its content — guides/migration.md",
|
|
120
|
-
"
|
|
166
|
+
"static-in-astro": "a dropped site on its Dev Machine: the pages are plain files in public/, served as they are. deploy.mjs <vertical…> --stack static --out public puts the REST layer in public/js/wix/; wire the pages in place with <script type=\"module\">: do the least that makes them work: connect each control the pages show and add only what a control can't work without (more only when the brief asks), and no Astro pages. Read the site (read-site.mjs); run the vertical's seed module with a plan only for content the brief supplies or describes. Release with npx @wix/cli@latest build && npx @wix/cli@latest release; the URL stays",
|
|
167
|
+
"blank-starter": "attach.mjs --business-name <brand> --vertical <v…> [--plan plan.json] (the site comes from wix.config.json): the CLI's blank starter with no work of its own, as a Dev Machine or a bare wix create leaves it. attach deploys the vertical onto it with its composed lock and starts the install and the seed detached: build the brand layer while they run, then sync on .install-exit and .seed-exit before release (SKILL.md step 3). Read the site first (read-site.mjs) when it may already hold content",
|
|
168
|
+
"wix-project": "iterate: never scaffold, init or reseed. deploy.mjs <vertical…> --stack <stack> adds a solution, then one install: node <SKILL_ROOT>/install/install.mjs (it skips when node_modules already satisfies the lock); file edits for a change; release. Read the site (read-site.mjs) before any seed module runs",
|
|
121
169
|
"published-static": "setup.mjs --vertical <v>: the config's site, no init; site/ becomes the upload and the REST layer lands in site/js/wix/; move the pages, styles and assets into site/. Nothing is seeded: read the site, then run the vertical's seed module with a plan when the brief gives content; release keeps the URL",
|
|
122
170
|
};
|
|
123
171
|
let shape;
|
|
124
172
|
if (!config) shape = f.packageJson || f.rootIndex ? "project" : "empty";
|
|
125
173
|
else if (migrationActive) shape = "migration";
|
|
174
|
+
else if (f.packageJson && f.publicIndex && !f.astroPages) shape = "static-in-astro";
|
|
175
|
+
else if (starter) shape = "blank-starter";
|
|
126
176
|
else if (f.packageJson || f.outputIndex) shape = "wix-project";
|
|
127
177
|
else if (f.rootIndex) shape = "published-static";
|
|
128
178
|
else shape = "config-only";
|
|
129
|
-
|
|
179
|
+
const frontend = !(f.packageJson || f.rootIndex || f.outputIndex) ? "none"
|
|
180
|
+
: starter ? (f.publicIndex && !f.astroPages ? "starter+static" : "starter")
|
|
181
|
+
: f.packageJson ? "project"
|
|
182
|
+
: "static";
|
|
183
|
+
return { shape, next: next[shape], facts: { config, frontend, starter, ...f } };
|
|
130
184
|
}
|
|
131
185
|
|
|
132
186
|
/**
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
// <out>/js/wix/ — shared/rest/ (client, media, config) + each vertical's rest/ + the
|
|
19
19
|
// vertical's transport-agnostic core files (types, *-core) from its app/ — writes
|
|
20
20
|
// the client id, and strips it to browser-ready ESM with tsc (comments kept, the
|
|
21
|
-
// .ts sources kept beside the .js). No package.json is touched.
|
|
22
|
-
//
|
|
21
|
+
// .ts sources kept beside the .js). No package.json is touched. Every vertical
|
|
22
|
+
// with a rest/ folder is supported. `--out <dir>` (default: the project root)
|
|
23
23
|
// is the folder the site is served from — the one wix.config.json's
|
|
24
24
|
// site.outputDirectory points at — so the modules land where the pages import
|
|
25
25
|
// them and nothing else in the project gets uploaded.
|
|
@@ -45,7 +45,7 @@ import { dirname, join, resolve } from "node:path";
|
|
|
45
45
|
import { fileURLToPath } from "node:url";
|
|
46
46
|
import { templatesDir } from "./templates.mjs";
|
|
47
47
|
import { syncLockRoot } from "./lock.mjs";
|
|
48
|
-
import { siteContext } from "./context.mjs";
|
|
48
|
+
import { blankStarter, siteContext } from "./context.mjs";
|
|
49
49
|
const PINS = JSON.parse(readFileSync(new URL("./pins.json", import.meta.url), "utf8"));
|
|
50
50
|
const WIX_CLI = `@wix/cli@${PINS["@wix/cli"]}`;
|
|
51
51
|
|
|
@@ -130,7 +130,7 @@ const VERTICAL_DEPS = {
|
|
|
130
130
|
blog: {
|
|
131
131
|
core: {
|
|
132
132
|
"@wix/blog": "^1.0.645",
|
|
133
|
-
"@wix/ricos": "^11.
|
|
133
|
+
"@wix/ricos": "^11.17.0", // the viewer-only entry @wix/ricos/viewer
|
|
134
134
|
},
|
|
135
135
|
astro: {
|
|
136
136
|
"@wix/seo": "^1.0.79",
|
|
@@ -268,7 +268,7 @@ const siteSearchConfigSource = () =>
|
|
|
268
268
|
`export const siteSearchPlan = ${JSON.stringify(siteSearch, null, 2)};\n`;
|
|
269
269
|
// ---- static stack: the REST layer, composed flat and stripped ------------------------------------
|
|
270
270
|
if (stack === "static") {
|
|
271
|
-
const {
|
|
271
|
+
const { npmSync } = await import("./proc.mjs");
|
|
272
272
|
const { rmSync } = await import("node:fs");
|
|
273
273
|
const OUT = outDir ? resolve(PROJECT, outDir) : PROJECT;
|
|
274
274
|
const JS = join(OUT, "js", "wix");
|
|
@@ -309,7 +309,11 @@ if (stack === "static") {
|
|
|
309
309
|
const fp = join(JS, f);
|
|
310
310
|
if (!existsSync(fp)) continue;
|
|
311
311
|
const src = readFileSync(fp, "utf8");
|
|
312
|
-
|
|
312
|
+
// The rest/ files import their real paths in the templates (../../shared/rest/client.js,
|
|
313
|
+
// ../app/wix/<vertical>/cart-core.js); flat in one folder, every import is a sibling.
|
|
314
|
+
const fixed = src
|
|
315
|
+
.replace(/from\s+"\.{1,2}\/(?:[^"]*\/)([^"/]+)"/g, 'from "./$1"')
|
|
316
|
+
.replace(/from\s+"\.\/types(?:\.js)?"/g, `from "./${vertical}-types.js"`);
|
|
313
317
|
if (fixed !== src) writeFileSync(fp, fixed);
|
|
314
318
|
}
|
|
315
319
|
if (VERTICALS.includes(vertical)) result.verticals.push(vertical);
|
|
@@ -336,14 +340,14 @@ if (stack === "static") {
|
|
|
336
340
|
}
|
|
337
341
|
// Strip to ESM with tsc — comments are the spec for whoever reads js/wix/, so they stay.
|
|
338
342
|
const sources = readdirSync(JS).filter((f) => f.endsWith(".ts")).map((f) => join(JS, f));
|
|
339
|
-
const tsc =
|
|
343
|
+
const tsc = npmSync(
|
|
340
344
|
"npx",
|
|
341
345
|
["-y", "-p", `typescript@${PINS.typescript}`, "tsc", ...sources, "--outDir", JS, "--module", "esnext", "--target", "es2022",
|
|
342
346
|
"--moduleResolution", "bundler", "--lib", "es2022,dom", "--strict", "--skipLibCheck", "--removeComments", "false"],
|
|
343
347
|
{ encoding: "utf8", timeout: 180_000 },
|
|
344
348
|
);
|
|
345
349
|
if (tsc.status !== 0) {
|
|
346
|
-
console.log(JSON.stringify({ ...result, error: `tsc strip failed: ${(tsc.stdout || tsc.stderr ||
|
|
350
|
+
console.log(JSON.stringify({ ...result, error: `tsc strip failed: ${(tsc.stdout || tsc.stderr || tsc.error?.message || `exit ${tsc.status}`).slice(-800)}` }));
|
|
347
351
|
process.exit(1);
|
|
348
352
|
}
|
|
349
353
|
rmSync(join(JS, "tsconfig.json"), { force: true });
|
|
@@ -431,7 +435,28 @@ if (siteSearch) {
|
|
|
431
435
|
result.capabilities = { ...result.capabilities, siteSearch: true };
|
|
432
436
|
if (siteSearch.install) {
|
|
433
437
|
// Indexing needs the Wix Site Search app on the site — a site mutation the deploy never makes.
|
|
434
|
-
result.note = [result.note,
|
|
438
|
+
result.note = [result.note, `siteSearch.install: run node ${join(REF, "shared", "capabilities", "site-search", "seed", "install.mjs")} from the project root after the seed (results appear about half a minute later)`].filter(Boolean).join("; ");
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
// ---- the blank starter takes the vertical's composed template --------------------------------
|
|
443
|
+
// A project that is still the blank starter (context.mjs blankStarter: a Dev Machine's, or a bare
|
|
444
|
+
// `wix create`) holds only the blank template's packages. Deploying one vertical onto it makes
|
|
445
|
+
// exactly that vertical's composed template (templates/<vertical>/project: the blank scaffold plus
|
|
446
|
+
// the vertical), so the starter takes the composed package.json dependencies and lockfile, and the
|
|
447
|
+
// install reads the lock instead of resolving the vertical's tree. A project with any package of its
|
|
448
|
+
// own, a second vertical, or two verticals in one run is left alone.
|
|
449
|
+
if (stack === "astro" && result.verticals.length === 1 && blankStarter(PROJECT, join(REF, "blank", "project", "package.json"))) {
|
|
450
|
+
// The composed project/ is left out of a committed templates copy; templatesDir fetches it on need.
|
|
451
|
+
const composed = join(templatesDir({ need: `${result.verticals[0]}/project` }), result.verticals[0], "project");
|
|
452
|
+
if (existsSync(join(composed, "package-lock.json"))) {
|
|
453
|
+
const pkg = JSON.parse(readFileSync(PKG_JSON, "utf8"));
|
|
454
|
+
const template = JSON.parse(readFileSync(join(composed, "package.json"), "utf8"));
|
|
455
|
+
pkg.dependencies = template.dependencies;
|
|
456
|
+
pkg.devDependencies = template.devDependencies;
|
|
457
|
+
writeFileSync(PKG_JSON, JSON.stringify(pkg, null, 2) + "\n");
|
|
458
|
+
cpSync(join(composed, "package-lock.json"), join(PROJECT, "package-lock.json"));
|
|
459
|
+
result.lockFromTemplate = result.verticals[0];
|
|
435
460
|
}
|
|
436
461
|
}
|
|
437
462
|
|
|
@@ -468,9 +493,9 @@ if (result.verticals.length && existsSync(PKG_JSON)) {
|
|
|
468
493
|
if (existsSync(PROJECT_LOCK)) {
|
|
469
494
|
const sync = syncLockRoot(PROJECT);
|
|
470
495
|
if (sync && (sync.promoted.length || sync.missing.length)) result.lock = sync;
|
|
471
|
-
result.note = [result.note, "install with
|
|
496
|
+
result.note = [result.note, "install with node <SKILL_ROOT>/install/install.mjs (nothing when node_modules already satisfies the lock)"].filter(Boolean).join("; ");
|
|
472
497
|
} else if (result.depsAdded?.length) {
|
|
473
|
-
result.note = [result.note, "run
|
|
498
|
+
result.note = [result.note, "run node <SKILL_ROOT>/install/install.mjs to pick up depsAdded"].filter(Boolean).join("; ");
|
|
474
499
|
}
|
|
475
500
|
}
|
|
476
501
|
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
// .env.local holds the app's WIX_CLIENT_SECRET (written by `wix env pull`, `init` and attach.mjs), so
|
|
2
|
+
// every project setup.mjs and attach.mjs leave behind ignores it in git. A line `.env` alone does not
|
|
3
|
+
// cover `.env.local`; `.env.local`, `.env*`, `.env.*` or `*.local` do.
|
|
4
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { join } from "node:path";
|
|
6
|
+
|
|
7
|
+
const COVERS_ENV_LOCAL = /^\s*(\.env\.local|\.env\*|\.env\.\*|\*\.local)\s*$/m;
|
|
8
|
+
|
|
9
|
+
/** Appends `.env.local` and `.env` to dir/.gitignore unless it already ignores `.env.local`. */
|
|
10
|
+
export function ignoreEnvLocal(dir) {
|
|
11
|
+
const gi = join(dir, ".gitignore");
|
|
12
|
+
const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
|
|
13
|
+
if (COVERS_ENV_LOCAL.test(cur)) return false;
|
|
14
|
+
writeFileSync(gi, cur + (cur && !cur.endsWith("\n") ? "\n" : "") + "\n# local env (pulled from Wix)\n.env.local\n.env\n");
|
|
15
|
+
return true;
|
|
16
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
// The project's one dependency install, decided from what is already on disk. Run from the
|
|
2
|
+
// PROJECT ROOT:
|
|
3
|
+
//
|
|
4
|
+
// node <SKILL_ROOT>/install/install.mjs runs it, prints {decision, lock, status}
|
|
5
|
+
// node <SKILL_ROOT>/install/install.mjs --decide prints the decision without installing
|
|
6
|
+
//
|
|
7
|
+
// What the lock asks for is compared with node_modules (each entry's package.json version):
|
|
8
|
+
// none every entry is there at its version — nothing runs. A machine image that pre-warms the
|
|
9
|
+
// solutions' packages, or a second deploy that added nothing, ends here in under a second.
|
|
10
|
+
// install node_modules exists and holds most of the lock — `npm install --ignore-scripts` keeps what
|
|
11
|
+
// is there and writes only the difference (seconds for a few packages).
|
|
12
|
+
// ci no node_modules, or a tree that lacks most of the lock (the blank starter under a
|
|
13
|
+
// solution's lock: measured 67 s for ci against 84 s for install there) —
|
|
14
|
+
// `npm ci --ignore-scripts || npm install --ignore-scripts` (ci needs a lock that agrees
|
|
15
|
+
// with package.json; without one, install).
|
|
16
|
+
// Every path ends with `.install-exit` holding the exit code (0 for none), the marker setup.mjs,
|
|
17
|
+
// attach.mjs and SKILL.md step 3 sync on. Never two installs concurrently in one project: a second
|
|
18
|
+
// npm in the same node_modules redoes the first one's work.
|
|
19
|
+
import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
20
|
+
import { join } from "node:path";
|
|
21
|
+
import { npmSync } from "./proc.mjs";
|
|
22
|
+
|
|
23
|
+
/** {total, missing, sample}: the lock's node_modules entries the tree lacks or holds at another version. */
|
|
24
|
+
export function lockGaps(projectDir) {
|
|
25
|
+
const lockPath = join(projectDir, "package-lock.json");
|
|
26
|
+
if (!existsSync(lockPath)) return null;
|
|
27
|
+
let lock;
|
|
28
|
+
try { lock = JSON.parse(readFileSync(lockPath, "utf8")); } catch { return null; }
|
|
29
|
+
// Optional entries (a platform's native binaries, an optional peer) are absent on every other platform and
|
|
30
|
+
// npm leaves them out without complaint, so they never count as gaps.
|
|
31
|
+
const entries = Object.entries(lock.packages ?? {}).filter(([path, e]) => path.startsWith("node_modules/") && !e.link && !e.optional && !e.devOptional);
|
|
32
|
+
let missing = 0;
|
|
33
|
+
const sample = [];
|
|
34
|
+
// A package.json dependency the lock's root entry does not declare (deploy's fill-only patch
|
|
35
|
+
// added it after the lock was written) is a gap too: the lock no longer describes the project.
|
|
36
|
+
try {
|
|
37
|
+
const pkg = JSON.parse(readFileSync(join(projectDir, "package.json"), "utf8"));
|
|
38
|
+
const root = lock.packages?.[""] ?? {};
|
|
39
|
+
for (const field of ["dependencies", "devDependencies"]) {
|
|
40
|
+
for (const name of Object.keys(pkg[field] ?? {})) {
|
|
41
|
+
if (!(root[field]?.[name] ?? root.dependencies?.[name] ?? root.devDependencies?.[name])) {
|
|
42
|
+
missing++;
|
|
43
|
+
if (sample.length < 5) sample.push(`${name} (in package.json, not in the lock)`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
} catch { /* no package.json: the lock entries decide */ }
|
|
48
|
+
for (const [path, e] of entries) {
|
|
49
|
+
let version = null;
|
|
50
|
+
try { version = JSON.parse(readFileSync(join(projectDir, path, "package.json"), "utf8")).version; } catch { /* absent */ }
|
|
51
|
+
if (version !== e.version) {
|
|
52
|
+
missing++;
|
|
53
|
+
if (sample.length < 5) sample.push(`${path.replace(/^node_modules\//, "")}@${e.version}${version ? ` (have ${version})` : ""}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { total: entries.length, missing, sample };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function installDecision(projectDir = process.cwd()) {
|
|
60
|
+
const hasModules = existsSync(join(projectDir, "node_modules"));
|
|
61
|
+
const lock = lockGaps(projectDir);
|
|
62
|
+
if (!hasModules) return { decision: existsSync(join(projectDir, "package-lock.json")) ? "ci" : "install", lock };
|
|
63
|
+
if (!lock || lock.total === 0) return { decision: "install", lock };
|
|
64
|
+
if (lock.missing === 0) return { decision: "none", lock };
|
|
65
|
+
return { decision: lock.missing > lock.total / 2 ? "ci" : "install", lock };
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** Runs the decided install; stdio defaults to inherited. Returns {decision, lock, status}. */
|
|
69
|
+
export function runInstall(projectDir = process.cwd(), { stdio = "inherit" } = {}) {
|
|
70
|
+
const { decision, lock } = installDecision(projectDir);
|
|
71
|
+
const flags = ["--ignore-scripts"];
|
|
72
|
+
let status = 0;
|
|
73
|
+
if (decision === "ci") {
|
|
74
|
+
let r = npmSync("npm", ["ci", ...flags], { cwd: projectDir, stdio });
|
|
75
|
+
if (r.status !== 0) r = npmSync("npm", ["install", ...flags], { cwd: projectDir, stdio });
|
|
76
|
+
if (r.error) console.error(`npm could not start: ${r.error.message}`);
|
|
77
|
+
status = r.status ?? 1;
|
|
78
|
+
} else if (decision === "install") {
|
|
79
|
+
const r = npmSync("npm", ["install", ...flags], { cwd: projectDir, stdio });
|
|
80
|
+
if (r.error) console.error(`npm could not start: ${r.error.message}`);
|
|
81
|
+
status = r.status ?? 1;
|
|
82
|
+
}
|
|
83
|
+
writeFileSync(join(projectDir, ".install-exit"), `${status}\n`);
|
|
84
|
+
return { decision, lock, status };
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (process.argv[1] && /install\.mjs$/.test(process.argv[1])) {
|
|
88
|
+
if (process.argv.includes("--decide")) {
|
|
89
|
+
console.log(JSON.stringify(installDecision(), null, 2));
|
|
90
|
+
} else {
|
|
91
|
+
const r = runInstall(process.cwd());
|
|
92
|
+
console.log(JSON.stringify(r, null, 2));
|
|
93
|
+
process.exit(r.status);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
// npm and npx, started the same way on every OS, and the two detached jobs (the dependency install
|
|
2
|
+
// and the seed) without a POSIX shell.
|
|
3
|
+
//
|
|
4
|
+
// On Windows npm and npx are .cmd shims: Node starts a .cmd only through a shell (ENOENT without
|
|
5
|
+
// one, EINVAL for an explicit .cmd since Node 20.12). npm ships their JS entry points beside
|
|
6
|
+
// node.exe, and those start directly with node. A Node without npm beside it (a version manager's
|
|
7
|
+
// shim) gets the .cmd through cmd.exe, as one quoted command line.
|
|
8
|
+
import { spawn, spawnSync } from "node:child_process";
|
|
9
|
+
import { existsSync, openSync } from "node:fs";
|
|
10
|
+
import { dirname, join } from "node:path";
|
|
11
|
+
import { fileURLToPath } from "node:url";
|
|
12
|
+
|
|
13
|
+
const isWin = process.platform === "win32";
|
|
14
|
+
const BACKGROUND = join(dirname(fileURLToPath(import.meta.url)), "background.mjs");
|
|
15
|
+
|
|
16
|
+
const cmdQuote = (a) => (/^[\w@%+=:,./\\-]+$/.test(a) ? a : `"${String(a).replace(/"/g, '""')}"`);
|
|
17
|
+
|
|
18
|
+
/** How to start `npm` or `npx` with `args` here: `{ file, args, shell }` for spawn/spawnSync. */
|
|
19
|
+
export function npmCommand(name, args) {
|
|
20
|
+
if (!isWin) return { file: name, args, shell: false };
|
|
21
|
+
const js = join(dirname(process.execPath), "node_modules", "npm", "bin", `${name}-cli.js`);
|
|
22
|
+
if (existsSync(js)) return { file: process.execPath, args: [js, ...args], shell: false };
|
|
23
|
+
return { file: [`${name}.cmd`, ...args.map(cmdQuote)].join(" "), args: [], shell: true };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/** spawnSync for npm / npx; same return shape. */
|
|
27
|
+
export function npmSync(name, args, opts = {}) {
|
|
28
|
+
const c = npmCommand(name, args);
|
|
29
|
+
return spawnSync(c.file, c.args, { ...opts, shell: c.shell, windowsHide: true });
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Why a spawnSync result failed: its output, else the spawn error, else the exit status. */
|
|
33
|
+
export const failure = (r, fallback = "") =>
|
|
34
|
+
String(r.stderr || r.stdout || r.error?.message || fallback || `exit ${r.status ?? r.signal}`);
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Starts install/background.mjs detached: `install` (npm ci, else npm install, output to `log`) or
|
|
38
|
+
* `seed` (node <seed> <plan>: result JSON, log and exit-code marker as files in `cwd`).
|
|
39
|
+
*/
|
|
40
|
+
export function startBackground(job, args, { cwd, log }) {
|
|
41
|
+
const fd = log ? openSync(log, "a") : "ignore";
|
|
42
|
+
const child = spawn(process.execPath, [BACKGROUND, job, ...args], {
|
|
43
|
+
cwd, detached: true, windowsHide: true, stdio: ["ignore", fd, fd],
|
|
44
|
+
});
|
|
45
|
+
child.unref();
|
|
46
|
+
}
|
|
@@ -30,13 +30,15 @@
|
|
|
30
30
|
// to recover one failed step. Emits ONE JSON event per line and exits in ~35s with the two long
|
|
31
31
|
// steps — the dependency install and the seed — running detached in the background (logs and
|
|
32
32
|
// completion markers in the final event), so the caller can build the brand layer while they finish.
|
|
33
|
-
import {
|
|
34
|
-
import { cpSync, existsSync,
|
|
33
|
+
import { spawnSync } from "node:child_process";
|
|
34
|
+
import { cpSync, existsSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
35
35
|
import { basename, dirname, join, resolve } from "node:path";
|
|
36
36
|
import { fileURLToPath } from "node:url";
|
|
37
37
|
import { AGENT_CONFIG_FILES, writeAgentsMd } from "./agents-md.mjs";
|
|
38
|
+
import { ignoreEnvLocal } from "./env-ignore.mjs";
|
|
38
39
|
import { listVerticals, templatesDir } from "./templates.mjs";
|
|
39
40
|
import { folderShape, siteContext } from "./context.mjs";
|
|
41
|
+
import { failure, npmSync, startBackground } from "./proc.mjs";
|
|
40
42
|
const PINS = JSON.parse(readFileSync(new URL("./pins.json", import.meta.url), "utf8"));
|
|
41
43
|
const WIX_CLI = `@wix/cli@${PINS["@wix/cli"]}`;
|
|
42
44
|
|
|
@@ -129,7 +131,7 @@ if (mode === "create") {
|
|
|
129
131
|
// a committed copy fetches it here, at the commit the rest came from.
|
|
130
132
|
const template = stack === "astro" ? join(templatesDir({ need: `${vertical}/project` }), vertical, "project") : null;
|
|
131
133
|
emit("scaffolding", { folder: folderName, template });
|
|
132
|
-
const scaffold =
|
|
134
|
+
const scaffold = npmSync(
|
|
133
135
|
"npm",
|
|
134
136
|
// --skip-git: this wrapper composes its own steps and leaves version control to
|
|
135
137
|
// the caller / the enclosing repo; the scaffold's own `git init` + "Initial
|
|
@@ -143,7 +145,7 @@ if (mode === "create") {
|
|
|
143
145
|
{ env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 300_000 },
|
|
144
146
|
);
|
|
145
147
|
if (scaffold.status !== 0 || !existsSync(join(projectDir, "wix.config.json"))) {
|
|
146
|
-
fail("scaffold", (scaffold
|
|
148
|
+
fail("scaffold", failure(scaffold, `scaffold produced no wix.config.json — is the Wix CLI logged in? (npx ${WIX_CLI} whoami)`).slice(-600));
|
|
147
149
|
}
|
|
148
150
|
}
|
|
149
151
|
} else if (mode === "migrate") {
|
|
@@ -164,9 +166,6 @@ if (mode === "create") {
|
|
|
164
166
|
scaffolded.name = basename(cwd).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "site";
|
|
165
167
|
writeFileSync(pkgPath, JSON.stringify(scaffolded, null, 2) + "\n");
|
|
166
168
|
}
|
|
167
|
-
const gi = join(cwd, ".gitignore");
|
|
168
|
-
const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
|
|
169
|
-
if (!/\.env/.test(cur)) writeFileSync(gi, cur + "\n# local env (pulled from Wix)\n.env.local\n.env\n");
|
|
170
169
|
} else if (publishedStatic) {
|
|
171
170
|
// ---- 1 · a published static site: the config came with the folder, the site exists -------------
|
|
172
171
|
// No init: `wix.config.json` already names the site the pages are published on (and its app). The
|
|
@@ -179,10 +178,10 @@ if (mode === "create") {
|
|
|
179
178
|
// wix.config.json and .env.local, touches nothing else. Non-interactive under CI=1; the site is
|
|
180
179
|
// named after the folder (rename it in the dashboard).
|
|
181
180
|
emit("adopting", { folder: cwd });
|
|
182
|
-
const init =
|
|
181
|
+
const init = npmSync("npm", ["create", `@wix/new@${PINS["@wix/create-new"]}`, "--", "init"],
|
|
183
182
|
{ cwd, env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 300_000 });
|
|
184
183
|
if (init.status !== 0 || !has("wix.config.json")) {
|
|
185
|
-
fail("init", (init
|
|
184
|
+
fail("init", failure(init, `init produced no wix.config.json — is the Wix CLI logged in? (npx ${WIX_CLI} whoami)`).slice(-600));
|
|
186
185
|
}
|
|
187
186
|
}
|
|
188
187
|
const wixConfig = JSON.parse(readFileSync(join(projectDir, "wix.config.json"), "utf8"));
|
|
@@ -208,7 +207,7 @@ const deploy = spawnSync(
|
|
|
208
207
|
[join(SKILL_ROOT, "install", "deploy.mjs"), ...verticals, "--stack", stack, ...(stack === "static" ? ["--out", STATIC_OUT] : []), ...(planPath ? ["--plan", resolve(planPath)] : [])],
|
|
209
208
|
{ cwd: projectDir, encoding: "utf8", timeout: 60_000 },
|
|
210
209
|
);
|
|
211
|
-
if (deploy.status !== 0) fail("deploy", deploy
|
|
210
|
+
if (deploy.status !== 0) fail("deploy", failure(deploy));
|
|
212
211
|
let deployResult = {};
|
|
213
212
|
try { deployResult = JSON.parse(deploy.stdout); } catch { /* keep going with raw output below */ }
|
|
214
213
|
if (deployResult.error) fail("deploy", deployResult.error);
|
|
@@ -243,7 +242,10 @@ if (mode === "create" && !subfolder && projectDir !== cwd) {
|
|
|
243
242
|
}
|
|
244
243
|
}
|
|
245
244
|
|
|
246
|
-
// ---- 2d · the agent config files `wix create` would have written
|
|
245
|
+
// ---- 2d · .env.local ignored, and the agent config files `wix create` would have written ---------
|
|
246
|
+
// Every mode leaves a .env.local with the app secret (env pull, init, the migration download), and a
|
|
247
|
+
// folder's own .gitignore may not cover it.
|
|
248
|
+
ignoreEnvLocal(projectDir);
|
|
247
249
|
// Skipped by the CLI because of --skip-install. Fill-only: a project that has its own AGENTS.md
|
|
248
250
|
// keeps it (the event says `kept`).
|
|
249
251
|
emit("agent_configs", writeAgentsMd(projectDir, { skill: basename(SKILL_ROOT), stack, ...(migrating ? { migration: { parentSiteId: ctx.migration.parentSiteId, deploySiteId: ctx.deploy.siteId } } : {}) }));
|
|
@@ -255,14 +257,8 @@ emit("agent_configs", writeAgentsMd(projectDir, { skill: basename(SKILL_ROOT), s
|
|
|
255
257
|
let install = null;
|
|
256
258
|
if (stack !== "static" && existsSync(join(projectDir, "package.json"))) {
|
|
257
259
|
const installLog = join(projectDir, "npm-install.log");
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
cwd: projectDir,
|
|
261
|
-
detached: true,
|
|
262
|
-
stdio: ["ignore", logFd, logFd],
|
|
263
|
-
});
|
|
264
|
-
child.unref();
|
|
265
|
-
install = { log: installLog, doneMarker: "node_modules/.package-lock.json" };
|
|
260
|
+
startBackground("install", [], { cwd: projectDir, log: installLog });
|
|
261
|
+
install = { log: installLog, doneMarker: ".install-exit", success: "file contains 0" };
|
|
266
262
|
emit("install_started", install);
|
|
267
263
|
}
|
|
268
264
|
|
|
@@ -276,7 +272,7 @@ if (stack !== "static" && existsSync(join(projectDir, "package.json"))) {
|
|
|
276
272
|
const madeTheSite = mode === "create" || mode === "adopt";
|
|
277
273
|
let seed = null;
|
|
278
274
|
if (planPath && !madeTheSite) {
|
|
279
|
-
emit("seed_skipped", { reason: `${mode}: the site existed before this run; setup seeds only a site it created. Read what the site holds (
|
|
275
|
+
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` });
|
|
280
276
|
}
|
|
281
277
|
if (planPath && madeTheSite) {
|
|
282
278
|
const seedDir = join(TEMPLATES, vertical, "seed");
|
|
@@ -286,12 +282,7 @@ if (planPath && madeTheSite) {
|
|
|
286
282
|
if (!seedName) fail("seed", `no seed module found under ${seedDir}`);
|
|
287
283
|
const seedFile = join(seedDir, seedName);
|
|
288
284
|
const planAbs = resolve(planPath);
|
|
289
|
-
|
|
290
|
-
"sh",
|
|
291
|
-
["-c", `node "${seedFile}" "${planAbs}" > seed-result.json 2> seed.log; echo $? > .seed-exit`],
|
|
292
|
-
{ cwd: projectDir, detached: true, stdio: "ignore" },
|
|
293
|
-
);
|
|
294
|
-
seedChild.unref();
|
|
285
|
+
startBackground("seed", [seedFile, planAbs], { cwd: projectDir });
|
|
295
286
|
seed = { resultFile: "seed-result.json", log: "seed.log", doneMarker: ".seed-exit", success: "file contains 0" };
|
|
296
287
|
emit("seeding_started", { vertical, ...seed, ...(verticals.length > 1 ? { note: `the plan seeds ${vertical}; the other verticals' seeds run afterwards, each with its own plan` } : {}) });
|
|
297
288
|
}
|
|
@@ -325,7 +316,7 @@ emit("ready_for_brand_layer", {
|
|
|
325
316
|
? "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; "
|
|
326
317
|
: madeTheSite
|
|
327
318
|
? (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; ")
|
|
328
|
-
: `nothing was seeded (setup seeds only a site it created): read what the site holds with
|
|
319
|
+
: `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; `) +
|
|
329
320
|
(others.length && mode !== "migrate"
|
|
330
321
|
? `${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); `
|
|
331
322
|
: "") +
|
|
@@ -7,7 +7,8 @@
|
|
|
7
7
|
//
|
|
8
8
|
// Resolution, in order:
|
|
9
9
|
// 1. the sibling skill folder, `<SKILL_ROOT>/../wix-headless-templates/`: a checkout of the repository,
|
|
10
|
-
// or
|
|
10
|
+
// or an install that carries both skills (the cold start's `npx skills add`, the npm package,
|
|
11
|
+
// the Codex and Cursor plugins; the Claude plugin leaves it out, so there it is fetched).
|
|
11
12
|
// 2. the cache `<SKILL_ROOT>/templates/`, filled by an earlier call (`--refresh` refetches).
|
|
12
13
|
// 3. a fetch: a sparse, shallow clone of `skills/wix-headless-templates/` from the repository the skill was installed
|
|
13
14
|
// from (skills-lock.json's `source`, default wix/skills), at the branch or tag the install
|
|
@@ -25,7 +26,7 @@
|
|
|
25
26
|
// `need`; when a committed copy lacks it, that part is fetched at the same commit.
|
|
26
27
|
import { cpSync, existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
27
28
|
import { tmpdir } from "node:os";
|
|
28
|
-
import { dirname, join, resolve } from "node:path";
|
|
29
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
29
30
|
import { spawnSync } from "node:child_process";
|
|
30
31
|
import { fileURLToPath } from "node:url";
|
|
31
32
|
|
|
@@ -42,7 +43,7 @@ const git = (args, opts = {}) => spawnSync("git", args, { encoding: "utf8", time
|
|
|
42
43
|
// skill folder, its entry for this skill. "wix/skills" → the default branch of that repository;
|
|
43
44
|
// "https://github.com/owner/repo/tree/<ref>" → that ref.
|
|
44
45
|
export function installSource() {
|
|
45
|
-
const skill = SKILL_ROOT
|
|
46
|
+
const skill = basename(SKILL_ROOT);
|
|
46
47
|
let dir = SKILL_ROOT;
|
|
47
48
|
for (let i = 0; i < 6; i++) {
|
|
48
49
|
const p = join(dir, "skills-lock.json");
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
// The post-body renderer — wire AS-IS (machinery, not a reference). A post body is a Ricos
|
|
2
2
|
// document, not HTML and not text: never innerHTML it, never String() its nodes. This is the
|
|
3
|
-
// documented render path (@wix/ricos
|
|
3
|
+
// documented render path (@wix/ricos/viewer + quick-start plugins: the viewer-only entry, 11.17+,
|
|
4
|
+
// without the editor, its dev tools and CodeMirror that the root import drags into every bundle).
|
|
4
5
|
//
|
|
5
6
|
// In Astro this component MUST be a `client:only="react"` island — the ricos viewer breaks
|
|
6
7
|
// under SSR. Dark theme: the ricos CSS hardcodes near-black text; scope an override on
|
|
7
8
|
// `.ricos-content` forcing the foreground token (in Astro use <style is:global> — React
|
|
8
9
|
// islands don't inherit scoped Astro styles).
|
|
9
|
-
import { quickStartViewerPlugins, RicosViewer } from "@wix/ricos";
|
|
10
|
+
import { quickStartViewerPlugins, RicosViewer } from "@wix/ricos/viewer";
|
|
10
11
|
import "@wix/ricos/css/all-plugins-viewer.css";
|
|
11
12
|
|
|
12
13
|
// Module-level, once — never rebuild the plugin list per render.
|