@wix/agent-skills 1.18.2 → 1.19.1

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.
Files changed (68) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-app/SKILL.md +42 -40
  3. package/skills/wix-app/references/BACKEND_API.md +151 -152
  4. package/skills/wix-app/references/DASHBOARD_PAGE.md +6 -10
  5. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +16 -7
  6. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +12 -8
  7. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +53 -131
  8. package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +1 -2
  9. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +83 -73
  10. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +3 -2
  11. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +9 -0
  12. package/skills/wix-app/references/editor-react-component/PARTS.md +5 -2
  13. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +1 -1
  14. package/skills/wix-app/scripts/pkg-root.cjs +73 -0
  15. package/skills/wix-app/scripts/scan-a11y-code.cjs +310 -40
  16. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +118 -24
  17. package/skills/wix-app/scripts/scan-a11y-render.cjs +551 -0
  18. package/skills/wix-app/scripts/scan-a11y-review.cjs +373 -0
  19. package/skills/wix-base44-connector/SKILL.md +24 -22
  20. package/skills/wix-base44-connector/scripts/utils.cjs +39 -3
  21. package/skills/wix-docs/SKILL.md +47 -20
  22. package/skills/wix-headless/entry/bootstrap.mjs +106 -50
  23. package/skills/wix-headless/entry/skill.md +5 -3
  24. package/skills/wix-headless/references/managed/AUTHENTICATION.md +25 -5
  25. package/skills/wix-headless-fast/SKILL.md +25 -7
  26. package/skills/wix-headless-fast/entry/skill.md +23 -14
  27. package/skills/wix-headless-fast/install/fast-path.mjs +48 -5
  28. package/skills/wix-headless-fast/install/pin-agents-md.mjs +57 -0
  29. package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +3 -3
  30. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +204 -35
  31. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +146 -36
  32. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +8 -2
  33. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +95 -20
  34. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +21 -2
  35. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +104 -18
  36. package/skills/wix-manage/SKILL.md +14 -8
  37. package/skills/wix-manage/references/bookings/booking-service-policy-setup.md +59 -33
  38. package/skills/wix-manage/references/bookings/bookings-staff-setup.md +45 -22
  39. package/skills/wix-manage/references/bookings/multi-resource-service-creation.md +70 -12
  40. package/skills/wix-manage/references/calendar/configure-default-business-hours.md +53 -17
  41. package/skills/wix-manage/references/google-ads/billing-and-payment.md +3 -3
  42. package/skills/wix-manage/references/google-ads/create-performance-max-campaign.md +8 -8
  43. package/skills/wix-manage/references/google-ads/get-campaign-suggestions.md +4 -4
  44. package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +1 -1
  45. package/skills/wix-manage/references/google-ads/install-and-create-account.md +9 -9
  46. package/skills/wix-manage/references/google-ads/query-campaign-analytics.md +7 -7
  47. package/skills/wix-manage/references/seo/manage-content-plan.md +181 -135
  48. package/skills/wix-manage/references/seo/manage-google-search-console.md +272 -0
  49. package/skills/wix-manage/references/sites/upload-static-site.md +53 -0
  50. package/skills/wix-manage/references/stores/change-store-currency.md +1 -1
  51. package/skills/wix-manage/references/stores/create-product-catalog-v3.md +17 -1
  52. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed-with-install.md +12 -9
  53. package/skills/wix-vibe-headless/platforms/base44-ecom-light-seed.md +12 -9
  54. package/skills/wix-vibe-headless/platforms/base44-ecom-light.md +1 -1
  55. package/skills/wix-vibe-headless/platforms/base44.md +11 -3
  56. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +5 -1
  57. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +5 -4
  58. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +33 -5
  59. package/skills/wix-app/references/dashboard-page/COLLECTION_TOOLKIT.md +0 -80
  60. package/skills/wix-app/references/dashboard-page/ENTITY_PAGE_TOOLKIT.md +0 -140
  61. package/skills/wix-app/references/dashboard-page/PATTERNS_BUNDLE_READING.md +0 -112
  62. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductCard.tsx +0 -100
  63. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductDetailView.tsx +0 -130
  64. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ProductGrid.tsx +0 -48
  65. package/skills/wix-headless-fast/references/storefront/app/components/storefront/ShopView.tsx +0 -66
  66. package/skills/wix-headless-fast/references/storefront/app/components/storefront/VariantPicker.tsx +0 -103
  67. package/skills/wix-headless-fast/references/storefront/app-astro/pages/products/[slug].astro +0 -56
  68. package/skills/wix-headless-fast/references/storefront/app-astro/pages/shop.astro +0 -23
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
- import readline from 'node:readline';
3
+ import { existsSync, mkdirSync, openSync, readFileSync, writeFileSync } from 'node:fs';
4
+ import { tmpdir } from 'node:os';
5
+ import { join } from 'node:path';
4
6
 
5
7
  // ── tiny event protocol (one JSON object per line) ───────────────────────────
6
8
  const emit = (event, extra = {}) =>
@@ -24,6 +26,12 @@ const WIX = [bin('npx'), '-y', '@wix/cli@latest']; // run the CLI via npx — no
24
26
  // Respect an existing value so a known runner (claude, cursor, …) keeps its name.
25
27
  const AGENT_ENV = { ...process.env, AI_AGENT: process.env.AI_AGENT || 'wix-headless-skill' };
26
28
 
29
+ // Where the detached login parks its output between runs. It has to outlive this
30
+ // process, so it can't be a pipe and can't live in the project.
31
+ const STATE_DIR = join(tmpdir(), 'wix-headless-login');
32
+ const LOG = join(STATE_DIR, 'login.log');
33
+ const PIDFILE = join(STATE_DIR, 'login.pid');
34
+
27
35
  // run a command, capture stdout+stderr (combined), return {status, out}
28
36
  function capture(cmd, args, opts = {}) {
29
37
  const r = spawnSync(cmd, args, { encoding: 'utf8', shell: isWin, env: AGENT_ENV, ...opts });
@@ -41,60 +49,108 @@ function checkCli() {
41
49
  }
42
50
 
43
51
  // ── 2. Reuse an existing session, or start device login ──────────────────────
44
- function hasExistingSession() {
45
- const r = capture(WIX[0], [...WIX.slice(1), 'whoami']);
46
- if (r.status !== 0) return false;
47
- emit('logged_in');
48
- return true;
49
- }
52
+ const hasExistingSession = () => capture(WIX[0], [...WIX.slice(1), 'whoami']).status === 0;
50
53
 
51
- function login() {
52
- return new Promise((resolve) => {
53
- const child = spawn(WIX[0], [...WIX.slice(1), 'login'], { shell: isWin, env: AGENT_ENV });
54
- const rl = readline.createInterface({ input: child.stdout });
55
- let loggedIn = false;
56
- // Buffer the CLI's own diagnostics (stderr + any non-event stdout chatter) so a
57
- // failure reports the REAL cause — network/proxy error, expired code, etc. — not
58
- // a guess. Keep only the tail so a chatty CLI can't blow up memory, and draining
59
- // stderr also avoids the pipe filling up and stalling the child.
60
- let diag = '';
61
- const collect = (chunk) => {
62
- diag = (diag + chunk).slice(-2000);
63
- };
64
- child.stderr?.on('data', collect);
65
- rl.on('line', (line) => {
66
- const t = line.trim();
67
- if (!t) return;
68
- // In agent mode the CLI emits {event:"awaiting_user"|"success"|"logged_in", ...}
69
- // — pass those straight through so the agent can relay the device code.
54
+ function loginEvents() {
55
+ if (!existsSync(LOG)) return [];
56
+ return readFileSync(LOG, 'utf8')
57
+ .split('\n')
58
+ .map((l) => l.trim())
59
+ .filter(Boolean)
60
+ .map((l) => {
70
61
  try {
71
- const ev = JSON.parse(t);
72
- if (ev && ev.event) {
73
- process.stdout.write(t + '\n');
74
- if (ev.event === 'success' || ev.event === 'logged_in') {
75
- loggedIn = true;
76
- resolve();
77
- }
78
- return;
79
- }
80
- collect(line + '\n'); // JSON, but not an event — keep for diagnostics
62
+ return JSON.parse(l);
81
63
  } catch {
82
- collect(line + '\n');
83
- } // non-JSON CLI chatter — keep for diagnostics
84
- });
85
- // Only a success/logged_in event counts as login. On any other exit, surface the
86
- // CLI's actual output — do NOT assume a cause (agent mode, etc.).
87
- child.on('close', (code) => {
88
- if (loggedIn) return;
89
- const detail =
90
- diag.trim().slice(-1500) ||
91
- `wix login exited (code ${code}) before authenticating, with no output.`;
92
- fail('login_failed', { code, detail });
93
- });
94
- child.on('error', (e) => fail('login_failed', { detail: String(e) }));
64
+ return null; // non-JSON CLI chatter
65
+ }
66
+ })
67
+ .filter((e) => e && e.event);
68
+ }
69
+
70
+ const alive = (pid) => {
71
+ try {
72
+ process.kill(pid, 0);
73
+ return true;
74
+ } catch {
75
+ return false;
76
+ }
77
+ };
78
+
79
+ // A login this machine started earlier may still be waiting on the browser. Reuse
80
+ // its code instead of minting a second one — a new login invalidates nothing, but
81
+ // it hands the user a different code than the one already on their screen.
82
+ function pendingLogin() {
83
+ if (!existsSync(PIDFILE)) return null;
84
+ let state;
85
+ try {
86
+ state = JSON.parse(readFileSync(PIDFILE, 'utf8'));
87
+ } catch {
88
+ return null;
89
+ }
90
+ if (!state.pid || !alive(state.pid)) return null;
91
+ const ev = loginEvents().find((e) => e.event === 'awaiting_user');
92
+ if (!ev) return null;
93
+ // Don't hand back a code that's about to expire mid-typing.
94
+ const ageSeconds = Math.round((Date.now() - state.startedAt) / 1000);
95
+ if (ageSeconds > (ev.expiresInSeconds ?? 600) - 60) return null;
96
+ return ev;
97
+ }
98
+
99
+ // Detach so the login keeps polling after this process exits. stdio goes to a
100
+ // file, not a pipe: a pipe dies with the parent, and the CLI would get EPIPE.
101
+ function startLogin() {
102
+ mkdirSync(STATE_DIR, { recursive: true });
103
+ writeFileSync(LOG, '');
104
+ const out = openSync(LOG, 'a');
105
+ const child = spawn(WIX[0], [...WIX.slice(1), 'login'], {
106
+ detached: true,
107
+ stdio: ['ignore', out, out],
108
+ env: AGENT_ENV,
109
+ shell: isWin,
110
+ });
111
+ child.on('error', (e) => fail('login_failed', { detail: String(e) }));
112
+ writeFileSync(PIDFILE, JSON.stringify({ pid: child.pid, startedAt: Date.now() }));
113
+ child.unref();
114
+ }
115
+
116
+ async function waitForCode(timeoutMs = 60000) {
117
+ const deadline = Date.now() + timeoutMs;
118
+ while (Date.now() < deadline) {
119
+ const events = loginEvents();
120
+ const ev = events.find((e) => e.event === 'awaiting_user');
121
+ if (ev) return ev;
122
+ const bad = events.find((e) => e.event === 'login_failed');
123
+ if (bad) fail('login_failed', bad);
124
+ await new Promise((r) => setTimeout(r, 500));
125
+ }
126
+ const detail = existsSync(LOG) ? readFileSync(LOG, 'utf8').trim().slice(-1500) : '';
127
+ fail('login_failed', {
128
+ detail: detail || `wix login produced no device code within ${timeoutMs / 1000}s.`,
129
+ });
130
+ }
131
+
132
+ // The next move is the user's, so hand the code back and exit rather than holding
133
+ // the caller open for a browser round-trip. `message` is the sentence to relay.
134
+ function surrenderTo(ev) {
135
+ emit('awaiting_user', {
136
+ ...ev,
137
+ message:
138
+ `To connect your Wix account, open ${ev.verificationUri} and enter the code ` +
139
+ `${ev.userCode}. Tell me once you're done and I'll continue.`,
95
140
  });
141
+ process.exit(0);
96
142
  }
97
143
 
98
144
  // ── main ────────────────────────────────────────────────────────────────────
99
145
  checkCli();
100
- if (!hasExistingSession()) await login();
146
+
147
+ if (hasExistingSession()) {
148
+ emit('logged_in');
149
+ process.exit(0);
150
+ }
151
+
152
+ const pending = pendingLogin();
153
+ if (pending) surrenderTo(pending);
154
+
155
+ startLogin();
156
+ surrenderTo(await waitForCode());
@@ -36,9 +36,9 @@ If that errors (Node not installed) or prints a version below 20.11, install or
36
36
 
37
37
  ## Phase 1 — Run the bootstrap (deterministic)
38
38
 
39
- Download the bootstrap script, then run it. It verifies the Wix CLI and handles login, emitting **one JSON event per line** on stdout. **Run it as a background/streaming process and relay its events to the user.**
39
+ Download the bootstrap script, then run it — an ordinary foreground command that exits on its own within seconds. It verifies the Wix CLI and handles login, emitting **one JSON event per line** on stdout. **Run it and relay its events to the user.**
40
40
 
41
- The script is safe and inspectable: it only checks the Wix CLI via `npx` and drives `wix login` (a device-code flow) — no other network calls, no filesystem writes. Read it first if your sandbox flags externally-downloaded code — either at the URL below or after downloading; both are the same file.
41
+ The script is safe and inspectable: it only checks the Wix CLI via `npx` and drives `wix login` (a device-code flow) — no other network calls, and the only files it writes are the login's own output and pid under the OS temp dir. Read it first if your sandbox flags externally-downloaded code — either at the URL below or after downloading; both are the same file.
42
42
 
43
43
  ```bash
44
44
  # macOS/Linux:
@@ -56,10 +56,12 @@ The script emits one JSON object per line:
56
56
  | Event | What to do |
57
57
  |---|---|
58
58
  | `cli_ok` | Wix CLI reachable — continue. |
59
- | `awaiting_user` (`verificationUri`, `userCode`) | Show the URL and code in plain prose; wait for the user to finish the login in their browser. |
59
+ | `awaiting_user` (`verificationUri`, `userCode`, `message`) | The script has exited and the next step is the user's. Send them `message` as-is; the login keeps running on its own. |
60
60
  | `logged_in` / `success` | Login done — continue. |
61
61
  | `cli_unreachable` / `login_failed` (with `detail`) | Stop and show the user the `detail`. **Do not** improvise a parallel setup by hand. |
62
62
 
63
+ On `awaiting_user`, run the script again once the user says they've logged in: it reports `logged_in` and you continue. Re-running while they're still in the browser is harmless — it returns the same code rather than issuing a new one.
64
+
63
65
  ## Phase 2 — Install the skill and hand off
64
66
 
65
67
  Install the Wix Headless skills (`CI=1` forces plain non-interactive CLI output — keep it on every Wix CLI command):
@@ -13,12 +13,32 @@ Never emit them to stdout, stderr, logs, prompts, or tool results.
13
13
  npx @wix/cli@latest whoami # exits 0 when logged in; non-zero when logged out
14
14
  ```
15
15
 
16
- If it's non-zero, **log in yourself** — don't punt to the user and stop:
16
+ If it's non-zero, **log in yourself** — don't punt to the user and stop. A foreground command that exits in seconds, one JSON event per line:
17
17
 
18
- 1. Run `npx @wix/cli@latest login` with **`run_in_background: true`** (no shell `&`, no redirect of your own — the harness captures stdout to its task-output file and returns the path).
19
- 2. Poll that file for the first JSON event: `{"event":"awaiting_user","userCode":"…","verificationUri":"…"}`.
20
- 3. Surface it to the user in plain prose: *"Open `<verificationUri>` and enter the code `<userCode>` — I'll continue once you've logged in."* **Send the message; do not re-invoke login.**
21
- 4. Wait for the harness `task-notification` with `<status>completed</status>` (not a sleep loop). On exit 0, run `whoami` once to confirm, then proceed.
18
+ ```bash
19
+ node ../../entry/bootstrap.mjs # path relative to this file
20
+ ```
21
+
22
+ | Event | Do |
23
+ |---|---|
24
+ | `logged_in` | Session exists — continue. |
25
+ | `awaiting_user` (`verificationUri`, `userCode`, `message`) | Send `message` verbatim, then stop. Re-run the script when the user says they're done → `logged_in`. Re-running early returns the **same** code, not a new one. |
26
+ | `cli_unreachable` / `login_failed` (`detail`) | Show `detail` and stop. |
27
+
28
+ ### If you'd rather not execute it
29
+
30
+ Read `../../entry/bootstrap.mjs` and do the same thing yourself. What matters:
31
+
32
+ ```bash
33
+ # AI_AGENT must be set, or the CLI renders an interactive Ink TUI and emits no JSON.
34
+ # Detach, redirecting to a FILE — a pipe dies with your process and the CLI gets EPIPE.
35
+ AI_AGENT=my-agent nohup npx -y @wix/cli@latest login > /tmp/wix-login.log 2>&1 &
36
+
37
+ grep -m1 awaiting_user /tmp/wix-login.log
38
+ # {"event":"awaiting_user","expiresInSeconds":600,"userCode":"…","verificationUri":"…"}
39
+ ```
40
+
41
+ Send the user that URL + code, then stop — nothing moves until they act. The detached login lands the session on its own; resume with `whoami` (exit 0). Codes last ~10 min; after that, start a new login and surface the new code.
22
42
 
23
43
  ## 2 · Mint the token
24
44
 
@@ -11,6 +11,11 @@ layer, hooks, components, pages, and a seed script that are already correct —
11
11
  job narrows to brand, layout, copy, and wiring. The decisions live in the code; don't
12
12
  re-litigate them.
13
13
 
14
+ **Scope.** This is the bootstrap, tuned for Wix-managed Astro, and each vertical ships *one*
15
+ shape of its solution. Deploy it as-is when the brief doesn't contradict it. When the brief
16
+ asks for something that shape doesn't express — or once the site exists and the work turns to
17
+ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaround here.
18
+
14
19
  ## Relationship to sibling skills
15
20
 
16
21
  | Skill | Use when |
@@ -36,13 +41,14 @@ re-litigate them.
36
41
  this — nothing to wire by hand.
37
42
  - **Data as-is; presentation is yours.** The data layer, hooks, and cart chrome are wired
38
43
  as-is — never rewrite their internals, re-route them through API routes, or re-derive a
39
- request shape. For a genuine gap, first read the relevant official contract with the
40
- `wix-docs` skill; do not search generated SDK types, package files, or `node_modules` to
41
- infer it. A normal caller-permitted operation belongs in a new data-layer function. A
42
- privileged operation belongs in a validated server endpoint — see
43
- `references/shared/CUSTOM_OPERATIONS.md`. The presentation components ship only as
44
- **references**: the vertical's INSTRUCTIONS names the surfaces you design and implement
45
- yourself on the shipped hooks (for storefront: card, grid, shop + PDP surfaces, home).
44
+ request shape. When the brief needs something they don't express, read the shipped file that
45
+ owns it and confirm the contract with `wix-docs`; never infer one from generated SDK types,
46
+ package files, or `node_modules`. A normal caller-permitted operation belongs in a new
47
+ data-layer function. A privileged operation belongs in a validated server endpoint — see
48
+ `references/shared/CUSTOM_OPERATIONS.md`. The presentation **doesn't ship**: the vertical's
49
+ INSTRUCTIONS names the surfaces you design and implement yourself on the shipped hooks,
50
+ with a skeleton carrying each surface's contract (for storefront: the shop and PDP pages
51
+ with their islands, and home).
46
52
  - **Never mock, fail loudly, purchases via Wix.** Live data or an honest empty state; surfaced
47
53
  errors, not swallowed ones; checkout/purchase always through the Wix redirect session.
48
54
  - **Optional capabilities are deployed from the plan.** A vertical can opt into a shared
@@ -70,6 +76,17 @@ re-litigate them.
70
76
  `--vertical` is required and picks which shipped code deploys AND which seed runs — use
71
77
  the vertical you resolved from the Verticals table.
72
78
 
79
+ fast-path scaffolds with `--skip-git`: it composes its own steps and leaves version control to
80
+ you / the enclosing repo, so it does **not** create the scaffold's usual git repo + initial
81
+ commit (which would otherwise become a nested-repo gitlink if the project lands inside a repo).
82
+
83
+ Optional `--flatten`: by default the project is created in a **new subfolder** (named after the
84
+ business). Pass `--flatten` to create it **directly in the current directory** instead — a plain
85
+ move of the scaffold's files up into the CWD, done **before** the dependency install starts so
86
+ nothing is half-moved (and since git was skipped, there's no nested repo to reconcile). Use it
87
+ when bootstrapping into an existing repo that must stay a single flat tree; otherwise omit it
88
+ and keep the subfolder.
89
+
73
90
  It emits one JSON event per line and returns in **~35s**: **scaffolds** the project,
74
91
  **deploys** the shipped code (patching `package.json` with every dependency the code
75
92
  imports, and placing the pre-resolved lockfile), then **starts two detached background
@@ -156,6 +173,7 @@ references/<vertical>/
156
173
  app-astro/ # Astro overlay importing ONLY from the core:
157
174
  pages/… # SSR fetch → DTO props → client:load islands; item pages carry
158
175
  # wixMetadata + <SEO.Tags>; chrome islands are client:only
176
+ # (storefront ships no pages — its INSTRUCTIONS carries their skeletons)
159
177
  layouts/… # (reuse SiteLayout when it fits)
160
178
  seed/ # seed-<vertical>.mjs (REST, mints its own CLI token) + SEED.md
161
179
  ```
@@ -15,7 +15,7 @@ already reading the raw text, don't fetch it again.
15
15
 
16
16
  ```bash
17
17
  curl -fsSL -O https://www.wix.com/skills/headless/entry/bootstrap.mjs && node bootstrap.mjs
18
- CI=1 npx skills@latest add wix/skills/skills/wix-headless-fast --yes
18
+ CI=1 npx skills@latest add wix/skills --skill wix-headless-fast --skill wix-docs --skill wix-manage --yes
19
19
  # then open and follow: .agents/skills/wix-headless-fast/SKILL.md
20
20
  ```
21
21
 
@@ -50,15 +50,14 @@ version, install or upgrade Node first — do **not** work around it:
50
50
 
51
51
  ## Phase 1 — Run the bootstrap (deterministic, shared)
52
52
 
53
- Download and run the shared bootstrap script. It verifies the Wix CLI and handles login,
54
- emitting **one JSON event per line** on stdout. **Run it and relay its events** — it exits on
55
- its own once the CLI is verified and a session exists (seconds, when already logged in). Only
56
- when a login is actually needed does it pause on `awaiting_user`; surface the URL + code and
57
- let it keep running until the login completes.
53
+ Download and run the shared bootstrap script — an ordinary foreground command that exits on
54
+ its own within seconds. It verifies the Wix CLI and handles login, emitting **one JSON event
55
+ per line** on stdout. **Run it and relay its events.**
58
56
 
59
57
  The script is safe and inspectable: it only checks the Wix CLI via `npx` and drives
60
- `wix login` (a device-code flow) — no other network calls, no filesystem writes. Read it first
61
- if your sandbox flags externally-downloaded code.
58
+ `wix login` (a device-code flow) — no other network calls, and the only files it writes are
59
+ the login's own output and pid under the OS temp dir. Read it first if your sandbox flags
60
+ externally-downloaded code.
62
61
 
63
62
  ```bash
64
63
  # macOS/Linux:
@@ -74,20 +73,30 @@ node bootstrap.mjs
74
73
  | Event | What to do |
75
74
  |---|---|
76
75
  | `cli_ok` | Wix CLI reachable — continue. |
77
- | `awaiting_user` (`verificationUri`, `userCode`) | Show the URL and code in plain prose; wait for the user to finish the login in their browser. |
76
+ | `awaiting_user` (`verificationUri`, `userCode`, `message`) | The script has exited and the next step is the user's. Send them `message` as-is; the login keeps running on its own. |
78
77
  | `logged_in` / `success` | Login done — continue. |
79
78
  | `cli_unreachable` / `login_failed` (with `detail`) | Stop and show the user the `detail`. **Do not** improvise a parallel setup by hand. |
80
79
 
81
- ## Phase 2 — Install the skill and hand off
80
+ On `awaiting_user`, run the script again once the user says they've logged in: it reports
81
+ `logged_in` and you continue. Re-running while they're still in the browser is harmless — it
82
+ returns the same code rather than issuing a new one.
82
83
 
83
- Install the skill (`CI=1` forces plain non-interactive CLI output — keep it on every Wix CLI
84
- command):
84
+ ## Phase 2 — Install the skills and hand off
85
+
86
+ Install the skill and its two companions (`CI=1` forces plain non-interactive CLI output —
87
+ keep it on every Wix CLI command). Repeat `--skill` per skill; a comma-separated list is not
88
+ parsed:
85
89
 
86
90
  ```bash
87
- CI=1 npx skills@latest add wix/skills/skills/wix-headless-fast --yes
91
+ CI=1 npx skills@latest add wix/skills \
92
+ --skill wix-headless-fast --skill wix-docs --skill wix-manage --yes
88
93
  ```
89
94
 
90
- It lands at `.agents/skills/wix-headless-fast/`. Then **open
95
+ - **`wix-headless-fast`** — the build itself.
96
+ - **`wix-docs`** — the API reference the playbooks defer to for any contract they don't cover.
97
+ - **`wix-manage`** — management recipes, for admin work on the site after it exists.
98
+
99
+ They land under `.agents/skills/`. Then **open
91
100
  `.agents/skills/wix-headless-fast/SKILL.md` and follow it** — it owns the rest of the run:
92
101
  resolve the stack, scaffold, deploy the shipped code, seed, build the brand layer, release.
93
102
  (If the request needs a vertical the skill doesn't ship yet — see its SKILL.md § Verticals —
@@ -4,15 +4,18 @@
4
4
  // or for connect/iterate runs (which must NOT scaffold and don't use this script).
5
5
  //
6
6
  // node <SKILL_ROOT>/install/fast-path.mjs --business-name "<Brand>" --plan plan.json \
7
- // --vertical <storefront|bookings|…> [--stack astro] [--folder-name <npm-safe-name>]
7
+ // --vertical <storefront|bookings|…> [--stack astro] [--folder-name <npm-safe-name>] [--flatten]
8
+ //
9
+ // --flatten (opt-in): leave the project directly in the current directory instead of a subfolder,
10
+ // moving it before the background install starts. Default off — the normal output is a subfolder.
8
11
  //
9
12
  // It emits ONE JSON event per line and exits in ~35s with BOTH long steps — the dependency
10
13
  // install AND the seed — running detached in the background (logs + completion markers
11
14
  // reported in the final event), so the caller can build the brand layer while they finish.
12
15
  // Steps: scaffold (Wix CLI; requires a logged-in session) → deploy shipped code + deps +
13
- // lockfile → start `npm ci || npm install` detached → start the seed detached.
16
+ // lockfile → pin AGENTS.md → start `npm ci || npm install` detached → start the seed detached.
14
17
  import { spawn, spawnSync } from "node:child_process";
15
- import { existsSync, openSync, readFileSync, readdirSync } from "node:fs";
18
+ import { existsSync, openSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
16
19
  import { dirname, join, resolve } from "node:path";
17
20
  import { fileURLToPath } from "node:url";
18
21
 
@@ -34,6 +37,8 @@ const businessName = flag("business-name");
34
37
  const planPath = flag("plan");
35
38
  const vertical = flag("vertical");
36
39
  const stack = flag("stack") ?? "astro";
40
+ // Opt-in: leave the project in the current directory (flat) instead of a subfolder.
41
+ const flatten = argv.includes("--flatten");
37
42
  // --vertical is REQUIRED: a defaulted vertical deploys the wrong code and runs the wrong
38
43
  // seed against the plan — fail loudly with the discovered choices instead.
39
44
  const knownVerticals = readdirSync(join(SKILL_ROOT, "references"), { withFileTypes: true })
@@ -51,7 +56,10 @@ const plan = JSON.parse(readFileSync(planPath, "utf8"));
51
56
  const folderName =
52
57
  flag("folder-name") ??
53
58
  businessName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
54
- const projectDir = resolve(process.cwd(), folderName);
59
+ // `let`: after deploy we flatten the scaffold up into the repo root and repoint
60
+ // projectDir there, so the detached install/seed and every reported path use the
61
+ // final location.
62
+ let projectDir = resolve(process.cwd(), folderName);
55
63
 
56
64
  // ---- 1 · scaffold -------------------------------------------------------------------------------
57
65
  if (existsSync(join(process.cwd(), "wix.config.json"))) {
@@ -63,9 +71,14 @@ if (existsSync(join(projectDir, "wix.config.json"))) {
63
71
  emit("scaffolding", { folder: folderName });
64
72
  const scaffold = spawnSync(
65
73
  "npm",
74
+ // --skip-git: this wrapper composes its own steps and leaves version control to
75
+ // the caller / the enclosing repo; the scaffold's own `git init` + "Initial
76
+ // commit" is noise here, and becomes a nested-repo (submodule gitlink) hazard
77
+ // if the project is later placed inside an existing repo. --skip-install for the
78
+ // same reason: deps install in a detached step below.
66
79
  ["create", "@wix/new@latest", "--", "headless",
67
80
  "--folder-name", folderName, "--business-name", businessName,
68
- "--site-template", "--skip-install", "--no-publish"],
81
+ "--site-template", "--skip-install", "--skip-git", "--no-publish"],
69
82
  { env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 300_000 },
70
83
  );
71
84
  if (scaffold.status !== 0 || !existsSync(join(projectDir, "wix.config.json"))) {
@@ -95,6 +108,36 @@ try { deployResult = JSON.parse(deploy.stdout); } catch { /* keep going with raw
95
108
  if (deployResult.error) fail("deploy", deployResult.error);
96
109
  emit("deployed", deployResult);
97
110
 
111
+ // ---- 2b · record what this project is, for later sessions ---------------------------------------
112
+ const pin = spawnSync(
113
+ "node",
114
+ [join(SKILL_ROOT, "install", "pin-agents-md.mjs"), "--vertical", vertical, "--stack", stack],
115
+ { cwd: projectDir, encoding: "utf8", timeout: 10_000 },
116
+ );
117
+ try { emit("agents_md", JSON.parse(pin.stdout)); } catch { /* never block the build on the note */ }
118
+
119
+ // ---- 2c · optional --flatten: move the scaffold into the current directory ----------------------
120
+ // OFF by default — the normal output is a self-contained subfolder, unchanged. Opt in when the
121
+ // caller wants the project directly in the current directory (e.g. bootstrapping into an existing
122
+ // repo that must stay one flat tree). Done HERE, before the detached install below, on purpose:
123
+ // no node_modules exists yet, so the move is instant and cannot collide with a running install —
124
+ // the failure mode when a project is flattened by hand after the background install has started.
125
+ // A pure move: the scaffold was created with --skip-git (above), so there is no nested repo to
126
+ // reconcile — git is whatever the destination already is.
127
+ const targetDir = process.cwd();
128
+ if (flatten && projectDir !== targetDir) {
129
+ try {
130
+ for (const entry of readdirSync(projectDir)) {
131
+ renameSync(join(projectDir, entry), join(targetDir, entry));
132
+ }
133
+ rmSync(projectDir, { recursive: true, force: true });
134
+ projectDir = targetDir;
135
+ emit("flattened", { into: targetDir });
136
+ } catch (e) {
137
+ fail("flatten", e?.stack || e);
138
+ }
139
+ }
140
+
98
141
  // ---- 3 · start the dependency install, detached --------------------------------------------------
99
142
  const installLog = join(projectDir, "npm-install.log");
100
143
  const logFd = openSync(installLog, "a");
@@ -0,0 +1,57 @@
1
+ // Record what this project is in its agent instruction files, so a later session — which has
2
+ // none of the build conversation and may never open SKILL.md — still knows the Wix site is the
3
+ // source of truth and which skill answers which kind of question.
4
+ //
5
+ // node <SKILL_ROOT>/install/pin-agents-md.mjs --vertical <name> [--stack astro]
6
+ //
7
+ // Run from the project root. Kept out of deploy.mjs on purpose: "copy a vertical's files" and
8
+ // "record the project's ground rules" are separate concerns with separate reasons to change.
9
+ // APPENDS to each file — a template's or the user's own content is never overwritten — and is
10
+ // idempotent via the heading sentinel, so re-running fast-path.mjs writes at most once.
11
+ // Project facts that live in the repo (site id, app id) are NOT copied here; they would go
12
+ // stale against wix.config.json, which is the one place that owns them.
13
+ import { appendFileSync, existsSync, readFileSync } from "node:fs";
14
+ import { join } from "node:path";
15
+
16
+ const argv = process.argv.slice(2);
17
+ const flag = (n, d) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : d; };
18
+
19
+ const vertical = flag("vertical");
20
+ const stack = flag("stack", "astro");
21
+ // "astro" means the Wix-managed flavour (ambient auth), which is worth naming. Any other stack
22
+ // is echoed as given rather than mapped, so adding one can't silently mislabel the project.
23
+ const frontend = stack === "astro" ? "Wix-managed Astro" : stack.charAt(0).toUpperCase() + stack.slice(1);
24
+
25
+ const SENTINEL = "## This app — a Wix Headless frontend";
26
+ const note = `
27
+ ${SENTINEL}
28
+
29
+ A ${frontend} frontend for a live Wix site; \`wix.config.json\` identifies the site. Built on
30
+ the **${vertical}** Wix business solution — more can be added to this same app. **The Wix site
31
+ owns the content and commerce** — read and write it through the Wix APIs, never hardcode what
32
+ it holds.
33
+
34
+ The Wix skills are installed at \`.agents/skills/\` — read them from that exact path:
35
+
36
+ - **\`wix-headless-fast\`** — the code already deployed in \`src/\` and how to extend it. Every
37
+ business solution it ships sits under \`references/<solution>/\`: \`INSTRUCTIONS.md\` is that
38
+ solution's playbook, \`seed/\` creates its content. To add one to this app, run from the
39
+ project root:
40
+ \`node .agents/skills/wix-headless-fast/install/deploy.mjs <solution> --stack ${stack}\`
41
+ - **\`wix-docs\`** — the Wix API reference. Confirm any endpoint, request shape, field, or enum
42
+ here before writing code against it, frontend or backend. Never infer a contract from
43
+ generated SDK types or \`node_modules\`.
44
+ - **\`wix-manage\`** — recipes for managing the live site itself, as opposed to changing this
45
+ codebase. No recipe for the task? Fall back to \`wix-docs\`.
46
+ `;
47
+
48
+ // Both files, independently — a project may have either, both, or neither, and agents differ
49
+ // in which one they read.
50
+ const pinned = ["AGENTS.md", "CLAUDE.md"].map((name) => {
51
+ const file = join(process.cwd(), name);
52
+ const existing = existsSync(file) ? readFileSync(file, "utf8") : "";
53
+ const wrote = !existing.includes(SENTINEL);
54
+ if (wrote) appendFileSync(file, (existing && !existing.endsWith("\n") ? "\n" : "") + note);
55
+ return { file: name, wrote, created: wrote && !existing };
56
+ });
57
+ console.log(JSON.stringify({ event: "agents_md", pinned }));
@@ -17,9 +17,9 @@ custom business write, or a third-party service.
17
17
 
18
18
  ## The research rule
19
19
 
20
- Read the relevant shipped playbook first. For a genuine gap, use `wix-docs` to read the
21
- official method or feature guide, then implement that contract. One documentation lookup and
22
- one type check are enough before coding. Investigate further only after a concrete error.
20
+ Read the relevant shipped playbook first. For anything it doesn't cover, use `wix-docs` to
21
+ read the official method or feature guide, then implement that contract. One documentation
22
+ lookup and one type check are enough before coding. Investigate further only after a concrete error.
23
23
 
24
24
  ## User-created public content
25
25