@wix/agent-skills 1.20.0 → 1.20.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 (152) hide show
  1. package/package.json +1 -1
  2. package/skills/wix-headless-fast/SKILL.md +200 -34
  3. package/skills/wix-headless-fast/entry/skill.md +10 -2
  4. package/skills/wix-headless-fast/install/attach.mjs +253 -0
  5. package/skills/wix-headless-fast/install/deploy.mjs +109 -9
  6. package/skills/wix-headless-fast/install/fast-path.mjs +29 -34
  7. package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +224 -60
  8. package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +2 -3
  9. package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +2 -3
  10. package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +24 -116
  11. package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +16 -64
  12. package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +137 -0
  13. package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +78 -0
  14. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +117 -0
  15. package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +13 -60
  16. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +47 -0
  17. package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +10 -28
  18. package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +2 -4
  19. package/skills/wix-headless-fast/references/blog/rest/posts.ts +42 -0
  20. package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +62 -0
  21. package/skills/wix-headless-fast/references/blog/seed/SEED.md +9 -0
  22. package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +179 -33
  23. package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +2 -1
  24. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +26 -132
  25. package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +18 -49
  26. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +218 -0
  27. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +169 -0
  28. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +57 -142
  29. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +92 -0
  30. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +90 -0
  31. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +22 -61
  32. package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +3 -1
  33. package/skills/wix-headless-fast/references/bookings/rest/booking.ts +113 -0
  34. package/skills/wix-headless-fast/references/bookings/rest/services.ts +55 -0
  35. package/skills/wix-headless-fast/references/bookings/seed/SEED.md +10 -0
  36. package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +236 -62
  37. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +19 -52
  38. package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +16 -36
  39. package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +158 -0
  40. package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +93 -0
  41. package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +177 -0
  42. package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +13 -41
  43. package/skills/wix-headless-fast/references/cms/rest/items.ts +125 -0
  44. package/skills/wix-headless-fast/references/cms/seed/SEED.md +9 -0
  45. package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +239 -83
  46. package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +1 -1
  47. package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +7 -5
  48. package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +2 -2
  49. package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +28 -110
  50. package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +19 -54
  51. package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +130 -0
  52. package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +97 -0
  53. package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +13 -94
  54. package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +113 -0
  55. package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +156 -0
  56. package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +38 -69
  57. package/skills/wix-headless-fast/references/events/rest/events.ts +57 -0
  58. package/skills/wix-headless-fast/references/events/rest/registration.ts +83 -0
  59. package/skills/wix-headless-fast/references/events/seed/SEED.md +9 -0
  60. package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +214 -26
  61. package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +35 -267
  62. package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +332 -0
  63. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +238 -0
  64. package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +28 -243
  65. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +216 -0
  66. package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +32 -204
  67. package/skills/wix-headless-fast/references/forms/rest/forms.ts +34 -0
  68. package/skills/wix-headless-fast/references/forms/rest/submissions.ts +56 -0
  69. package/skills/wix-headless-fast/references/forms/seed/SEED.md +8 -0
  70. package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +15 -8
  71. package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +314 -63
  72. package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +1 -1
  73. package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +105 -0
  74. package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +36 -65
  75. package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +1 -1
  76. package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +43 -0
  77. package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +11 -32
  78. package/skills/wix-headless-fast/references/members/rest/auth.ts +215 -0
  79. package/skills/wix-headless-fast/references/members/rest/members.ts +26 -0
  80. package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +238 -59
  81. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +2 -2
  82. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +2 -2
  83. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +35 -0
  84. package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +8 -38
  85. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +23 -50
  86. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +16 -34
  87. package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +22 -49
  88. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +82 -0
  89. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +58 -0
  90. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +177 -0
  91. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +31 -118
  92. package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +81 -0
  93. package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +75 -0
  94. package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +8 -0
  95. package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +212 -51
  96. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +9 -20
  97. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +7 -32
  98. package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +41 -0
  99. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +12 -33
  100. package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +17 -34
  101. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +54 -0
  102. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +106 -0
  103. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +64 -0
  104. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +7 -55
  105. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +43 -0
  106. package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +11 -34
  107. package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +31 -0
  108. package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +20 -0
  109. package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +8 -0
  110. package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +240 -47
  111. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +2 -2
  112. package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +2 -2
  113. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +14 -35
  114. package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +28 -171
  115. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +155 -0
  116. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +89 -0
  117. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +23 -117
  118. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +124 -0
  119. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +45 -114
  120. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +193 -0
  121. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +94 -0
  122. package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +15 -87
  123. package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +60 -0
  124. package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +169 -0
  125. package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +68 -0
  126. package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +8 -0
  127. package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +23 -0
  128. package/skills/wix-headless-fast/references/shared/app/wix/media.ts +45 -4
  129. package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +13 -0
  130. package/skills/wix-headless-fast/references/shared/rest/client.ts +155 -0
  131. package/skills/wix-headless-fast/references/shared/rest/config.ts +3 -0
  132. package/skills/wix-headless-fast/references/shared/rest/media.ts +65 -0
  133. package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +182 -63
  134. package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +95 -0
  135. package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +29 -58
  136. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +29 -163
  137. package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +26 -300
  138. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +121 -0
  139. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +49 -162
  140. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +309 -0
  141. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +47 -370
  142. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +147 -0
  143. package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +289 -0
  144. package/skills/wix-headless-fast/references/storefront/rest/cart.ts +132 -0
  145. package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +123 -0
  146. package/skills/wix-headless-fast/references/storefront/seed/SEED.md +25 -9
  147. package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +6 -35
  148. package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
  149. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -40
  150. package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +3 -0
  151. package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +6 -0
  152. package/skills/wix-headless-fast/install/pin-agents-md.mjs +0 -57
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wix/agent-skills",
3
- "version": "1.20.0",
3
+ "version": "1.20.1",
4
4
  "description": "Wix Skills for AI coding agents — versioned npm distribution of the wix/skills content.",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -5,24 +5,19 @@ description: "Build a Wix Headless site fast by wiring SHIPPED, verified @wix/sd
5
5
 
6
6
  # Wix Headless Fast
7
7
 
8
- Build a Wix Headless site by **deploying shipped code, not authoring it**. Where `wix-headless`
9
- hands the agent recipes to code from, this skill ships the integration itself — a typed data
10
- layer, hooks, components, pages, and a seed script that are already correct — and the agent's
11
- job narrows to brand, layout, copy, and wiring. The decisions live in the code; don't
8
+ Build a Wix Headless site on **shipped, verified code instead of authoring the integration**.
9
+ Each vertical ships the integration itself — a typed data layer, hooks, components, pages, and a
10
+ seed script that are already correct. On a stack that runs it (Wix-managed Astro, any React
11
+ project) the code is **deployed** and the agent's job narrows to brand, layout, copy, and wiring.
12
+ On a stack that can't run it (a static site with no bundler, a server-rendered app in another
13
+ language) the same code is the **reference**: its REST twin deploys for the browser side, and the
14
+ rules it encodes are what the agent ports. Either way the decisions live in the code; don't
12
15
  re-litigate them.
13
16
 
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
-
19
- ## Relationship to sibling skills
20
-
21
- | Skill | Use when |
22
- | ---------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
23
- | **wix-headless-fast** (this) | A supported vertical fits the request and the frontend is Astro or React — the fast path. |
24
- | `wix-headless` | A vertical this skill doesn't ship yet, a non-React frontend, backend-only runs, or stripe/self-managed project types. |
25
- | `wix-vibe-headless` | Client-only REST over a `WIX_CLIENT_ID` inside a vibe platform (Base44 etc.) — no SDK, no CLI. |
17
+ **Scope.** Tuned for Wix-managed Astro, and each vertical ships *one* shape of its solution. Use
18
+ it as-is when the brief doesn't contradict it. When the brief asks for something that shape
19
+ doesn't express — or once the site exists and the work turns to managing or extending it — that's
20
+ `wix-docs` and `wix-manage`, not a workaround here.
26
21
 
27
22
  ## The model
28
23
 
@@ -34,7 +29,7 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
34
29
  islands, with owner-editable item-page SEO pre-wired.
35
30
  - `seed/` — a build-time REST seed script (plain-data plan in, created content out) plus its
36
31
  `SEED.md` contract.
37
- - `INSTRUCTIONS.md` — the vertical's playbook: file map, what you build, hard rules, verify.
32
+ - `INSTRUCTIONS.md` — the vertical's playbook: file map, what you build, hard rules.
38
33
  - **One auth seam.** All shipped code calls Wix through `src/wix/sdk.ts`: on Wix-managed Astro
39
34
  auth is ambient (no client, no id); on any other React setup the same file runs a manual
40
35
  visitor client off the public client id in `src/wix/config.ts`. The deploy step configures
@@ -49,6 +44,14 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
49
44
  INSTRUCTIONS names the surfaces you design and implement yourself on the shipped hooks,
50
45
  with a skeleton carrying each surface's contract (for storefront: the shop and PDP pages
51
46
  with their islands, and home).
47
+ - **NEVER work from training data or memory about the Wix APIs.** Not a URL, a path, a version,
48
+ a header, a field name, a filter key, or a body. Every Wix call you make or write — in the
49
+ frontend, in a seed, in a build-time read of a site — comes from a source you have **open in
50
+ front of you**: this skill's shipped code, a `wix-manage` recipe, or a `wix-docs` page. No
51
+ source open, no call. A guessed call that returns 400 or an empty page is not a step toward
52
+ the answer; it is the failure this rule exists to prevent, and trying the next variant is
53
+ still guessing. The shipped code is tested against live sites; a body that looks similar is
54
+ the one that returns nothing, and the API rarely says why.
52
55
  - **Never mock, fail loudly, purchases via Wix.** Live data or an honest empty state; surfaced
53
56
  errors, not swallowed ones; checkout/purchase always through the Wix redirect session.
54
57
  - **Optional capabilities are deployed from the plan.** A vertical can opt into a shared
@@ -60,11 +63,28 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
60
63
 
61
64
  ## The run
62
65
 
63
- 1. **Resolve the stack.** Default is **Wix-managed Astro** — take it unless the user names a
64
- different React framework or the directory already holds a non-Astro React project. A
65
- non-React frontend is out of scope → `wix-headless`.
66
- 2. **Draft the seed plan** (read only the vertical's `SEED.md` for this — it depends only on
67
- the brief; save the vertical's `INSTRUCTIONS.md` for step 4, where it's needed). Requires from here on: Node ≥ 20.11 and a logged-in Wix CLI
66
+ 1. **Resolve the stack.** Default is **Wix-managed Astro** — take it unless the user names
67
+ another framework or the directory already holds one. Then, by what the shipped code can run
68
+ there:
69
+ - **React** (Vite, Next, …) runs everything shipped — data layer, hooks, components:
70
+ `--stack react`. The agent's own files may be JS.
71
+ - **Another bundled JS framework** (Vue, Svelte, Solid, plain Vite): the data layer and the
72
+ framework-free stores run (`src/wix/` has no React in it), the React hooks and components
73
+ don't apply: `--stack lib`. The agent binds the stores and writes its framework's components
74
+ against the same contracts.
75
+ - **No bundler, or another language** — a static site (plain HTML/CSS/JS), a server-rendered
76
+ app (Flask, Laravel, Rails, …): **reference mode** (below). Nothing from `app/` deploys; the
77
+ REST layer deploys for the browser side, and the server side ports it for its reads.
78
+ 2. **The seed plan.** When the brief supplies the content in any form — a CSV, JSON or
79
+ spreadsheet, a list in the prompt, a PDF price list, a folder of photos and a text file, a
80
+ link to their current catalog, anything else that names the content — that IS the plan: map
81
+ it into `plan.json` per `references/shared/SUPPLIED-CONTENT.md` and the vertical's `SEED.md`
82
+ ("Supplied content"), every entry, names and prices verbatim, their images and no others.
83
+ Draft a plan from the brief only when nothing was supplied (read only the vertical's
84
+ `SEED.md` for this — it depends only on the brief; save the vertical's `INSTRUCTIONS.md` for
85
+ step 4, where it's needed). **An existing site has no plan**: when the brief names a site by
86
+ its id, the site holds the content already — nothing is seeded, and the frontend reads what
87
+ is there (step 3's attach path). Requires from here on: Node ≥ 20.11 and a logged-in Wix CLI
68
88
  (`npx @wix/cli@latest whoami`; login via the device-code flow — surface the URL+code, never
69
89
  read tokens into context).
70
90
  3. **Create runs (empty directory): run the fast path** — one deterministic call:
@@ -80,12 +100,11 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
80
100
  you / the enclosing repo, so it does **not** create the scaffold's usual git repo + initial
81
101
  commit (which would otherwise become a nested-repo gitlink if the project lands inside a repo).
82
102
 
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.
103
+ The project is created **in the current directory** — the folder the entry had you work from,
104
+ which already holds the installed skills — so the project is self-contained and a later session
105
+ opened in it finds everything. It refuses if the folder already holds a file the scaffold would
106
+ write. `--subfolder` creates it in a new folder named after the business instead, for a current
107
+ folder that must stay as it is; the skills then sit one level above the project.
89
108
 
90
109
  It emits one JSON event per line and returns in **~35s**: **scaffolds** the project,
91
110
  **deploys** the shipped code (patching `package.json` with every dependency the code
@@ -96,6 +115,59 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
96
115
  and both markers. Relay notable events. On an `error` event, recover just that step via the
97
116
  manual path below, then continue.
98
117
 
118
+ **Existing-site runs (the brief names a Wix site by its id — a new frontend for a site that
119
+ already has its content): read the site, then run attach.** First, one call tells you what
120
+ the site is — its name, currency, and the Wix apps installed on it:
121
+
122
+ ```bash
123
+ curl -sS -X POST 'https://www.wixapis.com/_api/dynamic-context/v1/dynamic-context/markdown' \
124
+ -H "Authorization: $(npx -y @wix/cli@latest token)" -H 'Content-Type: application/json' \
125
+ -d '{"siteId": "<siteId>"}'
126
+ ```
127
+
128
+ The installed apps name the verticals (Wix Stores → storefront, Wix Bookings → bookings, and
129
+ so on per the Verticals table); the brief picks among them. Then one deterministic call, same
130
+ shape as the fast path:
131
+
132
+ ```bash
133
+ node <SKILL_ROOT>/install/attach.mjs --site <siteId> --business-name "<site name>" --vertical <vertical>[,<vertical>]
134
+ ```
135
+
136
+ `init`/`wix create` always create a site, so they are not used here. attach does what they do
137
+ after creating one — the site's OAuth app, Wix hosting, `wix.config.json` — against the site
138
+ given, scaffolds the CLI's Astro template, deploys the shipped code, and starts the install
139
+ detached. No seed runs and nothing on the site changes: the content is the site's own, read
140
+ live through the deployed data layer. When the site already has a headless frontend,
141
+ `attached` says so (`hosting: "reused"`) with its URL — `wix release` from this project
142
+ replaces that frontend; say so when you close.
143
+
144
+ **Get the measure of the site before you design.** Enough to know what you are building
145
+ for: what the chosen verticals will render, roughly how much of it, and what it is like — a
146
+ bakery with six products in three categories designs differently from six hundred. Go
147
+ deeper only where the brief points (a flash sale on cakes: is there a Cakes category, do the
148
+ cakes carry a sale price). Everything else the pages read live through the deployed data
149
+ layer; you are sizing the content, not collecting it. These are build-time reads with the
150
+ **site's** token, `npx -y @wix/cli@latest token --site <siteId>`, sent raw as the
151
+ `Authorization` header (the account token from the call above does not scope to a site).
152
+ **The rule above applies in full: not one of these calls comes from memory.** Open the
153
+ source first, then call. Three sources, in this order:
154
+ - **The vertical's shipped `rest/` module**, at
155
+ `.agents/skills/wix-headless-fast/references/<vertical>/rest/` — the same reads the pages
156
+ make, written as literal `fetch` calls: URL, body, `fields`, filter keys. It is not in
157
+ `src/` (the Astro stack deploys the SDK layer, which hides the body behind a method), so
158
+ read it from the skill.
159
+ - **`wix-manage`**, at `.agents/skills/wix-manage/` — REST recipes for managing a site's
160
+ business solutions: exact endpoint, method and payload per operation, curl included. Its
161
+ SKILL.md is the index, by solution; open the recipe for the vertical's solution.
162
+ - **`wix-docs`**, at `.agents/skills/wix-docs/` — the Wix API reference, reached by
163
+ **search, not by browsing files**: the skill folder holds the how-to, not the pages. Open
164
+ its SKILL.md; it gives one `curl` to semantic search (`POST
165
+ /mcp-docs-search/v1/docs/search/markdown`, natural-language `search_term`) that returns
166
+ condensed method docs — endpoint, request example, response shape — and the rule that any
167
+ `dev.wix.com/docs/…` URL plus `.md` is the full page. Progressive: search first, read the
168
+ full page only when the hit lacks what you need.
169
+ If the source you opened does not have the call, go to the next one; do not try a variant.
170
+
99
171
  **Connect/iterate runs (a project already on disk): never scaffold — use the manual path:**
100
172
  `CI=1 npm create @wix/new@latest init` in place if there is no `wix.config.json` yet; then
101
173
  `node <SKILL_ROOT>/install/deploy.mjs <vertical…> --stack astro|react --plan plan.json` from the project root
@@ -122,7 +194,12 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
122
194
  5. **When both background jobs have completed** — the install's marker
123
195
  (`node_modules/.package-lock.json`) and the seed's (`.seed-exit`) both exist — **verify the
124
196
  seed succeeded** (`.seed-exit` contains `0`; `seed-result.json` has the created counts for
125
- your summary — if non-zero, read `seed.log` and re-run the seed module manually). Then
197
+ your summary — if non-zero, read `seed.log` and re-run the seed module manually). Those two
198
+ seed files exist **only when fast-path started the seed** (attach runs none: only the install
199
+ marker is waited on). When you ran `seed-store.mjs`
200
+ yourself (connect/iterate runs, reference mode), there is no marker to wait for: the process's
201
+ exit code is the result and its stdout is the JSON — wait on the process (a foreground run,
202
+ or `wait` on its pid), not on a file. Then
126
203
  **build & release once** (managed):
127
204
  `npx @wix/cli@latest build` then `npx @wix/cli@latest release` (if the install failed, run
128
205
  it once more and then build). Don't build+release mid-flow; backend content is fetched at
@@ -132,8 +209,91 @@ managing or extending it — that's `wix-docs` and `wix-manage`, not a workaroun
132
209
  `wix release` output — never retype it from memory** (a mistyped subdomain hands the user
133
210
  a 404).
134
211
 
135
- Don't smoke-test with a dev server unless the user explicitly asks to verify — correctness
136
- comes from the shipped code, and real errors surface at build/release.
212
+ ## Reference mode — a static site, or a server-rendered app in another language
213
+
214
+ The data layer ships a second time as a **REST layer**: `references/shared/rest/` (the auth seam
215
+ `client.ts`, `media.ts`, `config.ts`) and `references/<vertical>/rest/` (the same exports as the
216
+ vertical's `app/wix/<vertical>/` data layer, over `fetch`), typed against the same `types.ts` and
217
+ importing the same `*-core.ts` rule files as the SDK layer — one implementation of the rules, two
218
+ transports. The vertical's `INSTRUCTIONS.md` names its modules and what each surface does with
219
+ them; this section is the mechanics, the same for every vertical.
220
+
221
+ - **Static site (no bundler).** `npm create @wix/new@latest init` in the project folder (site,
222
+ OAuth app, `wix.config.json`). The site lives in a **subfolder** — `site/` — holding only the
223
+ pages, styles, and `js/`; set `site.outputDirectory` in the config to `"./site"`. `wix release`
224
+ uploads that directory whole, so the project root (config, `plan.json`, seed output, anything
225
+ else) must not be it. Then `node <SKILL_ROOT>/install/deploy.mjs <vertical> --stack static --out
226
+ site` composes the REST layer and the vertical's framework-free stores flat into `site/js/wix/`
227
+ and strips them to browser ESM (comments kept, the `.ts` kept beside the `.js` to read). Pages
228
+ import the vertical's modules from `./js/wix/` in a `<script type="module">`: the stores hold
229
+ the state machines (subscribe, render from `getState()`, call actions), the page holds the
230
+ rendering. The visitor token lives in `localStorage` and is the
231
+ visitor's identity across Wix — never mint one per page. A route is a page plus a query-string
232
+ slug (`item.html?slug=…`). Wix static hosting serves files, not directories: `/shop` does not
233
+ resolve to `shop/index.html`, and there is no routes configuration — name the file and link
234
+ to it. Seed per the vertical's `SEED.md` (Node + the CLI token, no project dependencies).
235
+ Release with `npx @wix/cli@latest release` — no build. Item-page tags come from the entity's
236
+ `seoData`, set after the fetch (`document.title`, the meta description).
237
+ - **Server-rendered, another language (Flask, Laravel, Rails, …).** The same shape as managed
238
+ Astro — pages rendered on the server, the interactive surfaces in the browser — with hosting and
239
+ SEO plumbing theirs. `init` still runs in the project folder; run `deploy.mjs <vertical> --stack
240
+ static` there too: it only needs `wix.config.json` and writes `js/wix/`. Split by where the call
241
+ runs:
242
+ - **Reads render on the server.** Port the vertical's `rest/` read module and its `*-core.ts` to
243
+ the server language: each function is one HTTP call with a literal URL and JSON body, and the
244
+ core carries the rules. That code is tested and proven against live sites — carry its bodies
245
+ over as they are, `fields` arrays and filter keys included (they are not guessable, and a
246
+ near-miss returns empty or unformatted data with no error), and render what Wix returns
247
+ (`formattedAmount`, never a number you format yourself). The shipped JS runs: when in doubt,
248
+ run the module with Node against the same site and compare one entity with your port. Public
249
+ reads need no visitor identity — one anonymous visitor token per server process, refreshed per
250
+ `client.ts`, is enough for them.
251
+ - **Visitor-specific state runs in the browser.** Whatever the vertical does on the visitor's
252
+ behalf (a store's cart and checkout, a booking, an RSVP, a form submit) loads the vertical's
253
+ `js/wix/` module in the templates and talks to Wix from the page, exactly as a static site
254
+ does: the browser owns the visitor token in `localStorage`, so the server handles no
255
+ per-visitor tokens. If that state must run server-side anyway, `client.ts`'s header applies:
256
+ one token set per visitor in the visitor's session, never one process-wide token (that is one
257
+ identity shared by everyone).
258
+ - **Pre-rendered → Wix-hosted.** If the project builds to static HTML (Frozen-Flask, Pelican,
259
+ Hugo, Eleventy, any static-site generator), Wix can host the output: run `deploy.mjs
260
+ <vertical> --stack static --out <build dir>` so `js/wix/` lands inside the build output (or
261
+ copy it there after each build), make the generator emit a page for **every** entity slug the
262
+ vertical's list read returns (walk it by cursor, never only the first page), point
263
+ `site.outputDirectory` at the build folder, `wix release`. The build's own reads use one
264
+ anonymous visitor token for the duration of the build. What Wix hosts is exactly the contents
265
+ of that folder after your last build, served as files: every asset a page references must be
266
+ in there and current — if the pipeline has more than one build step (templates, then a CSS or
267
+ asset bundle), they all run, in order, on every rebuild, or the release carries a stale piece.
268
+ `/shop` does not resolve to `shop/index.html`; name the file and link to it. Generated pages
269
+ sit at different depths, so reference `js/wix/` through one base path (a template variable,
270
+ or root-relative `/js/wix/…`), never `./js/wix/` — a relative path breaks one level down. **The generated page
271
+ is the first paint, not the whole surface**: the vertical's interactive behaviour (a store's
272
+ sort, filters, and cart; a blog's search; a booking flow) still runs client-side on top of it
273
+ from the same `js/wix/` modules, so the vertical's surface contracts in `INSTRUCTIONS.md`
274
+ apply unchanged. Close with the rebuild + release command and one line for the owner: content
275
+ edits made in the dashboard reach the site when that command runs; the browser-side flows are
276
+ live regardless. A running server (live reads on every request) stays theirs to host.
277
+ Then read the vertical's `INSTRUCTIONS.md` for the surfaces and hard rules, and the shared
278
+ `DESIGN.md`/`CONTENT.md`. **Before writing any surface, read the vertical's shipped hooks and
279
+ components** — its `INSTRUCTIONS.md` lists which files and what to take from each. They don't
280
+ deploy on this stack, and they are working, tested code for exactly the behaviour you are about
281
+ to write in your own; rewriting them from prose is where the bugs come from (the runs that
282
+ skipped them shipped a broken quick-add, the run that read them didn't). Both were written for
283
+ the stacks that receive the code, so they speak that stack's dialect: the Tailwind classes in the
284
+ skeletons and the components are one spelling of layout and behaviour rules that hold everywhere
285
+ (a bounded image band on phones, name and price on separate lines, the buy control pinned to the
286
+ tile's bottom, an overlay that locks scroll and returns focus). Take the rules; write them in the
287
+ CSS your stack uses, on a token set you define — nothing here asks you to add Tailwind. Close with run (or
288
+ rebuild) instructions, the live URL when Wix hosts the output, the dashboard link, and — when
289
+ hosting is theirs — the allowed-domain step (add the public https origin to the OAuth app
290
+ before a Wix-hosted flow such as checkout can return).
291
+ - Both: the calls in `rest/` are the ones a **visitor token** may make from a page — public reads
292
+ and the visitor's own actions. Anything elevated (writes to content, other people's data) runs
293
+ server-side per `references/shared/CUSTOM_OPERATIONS.md`; the seed's CLI token never belongs in
294
+ a page. A static site has no SSR; neither case has owner-editable item-page SEO through
295
+ `@wix/seo` (tags come from the entity's `seoData`) — say so in the closing message; managed
296
+ Astro stays the recommendation for a public site.
137
297
 
138
298
  ## Verticals
139
299
 
@@ -164,10 +324,11 @@ New verticals follow the same layout — the deploy script discovers them automa
164
324
 
165
325
  ```
166
326
  references/<vertical>/
167
- INSTRUCTIONS.md # playbook: file map, wiring per stack, what you build, hard rules, verify
327
+ INSTRUCTIONS.md # playbook: file map, wiring per stack, what you build, hard rules
168
328
  app/ # framework-agnostic core — disjoint paths so verticals never collide:
169
329
  wix/<vertical>/ # types.ts (DTOs) + data layer (calls via ../sdk, images via ../media)
170
- hooks/<vertical>/ # React hooks (SSR-friendly: accept initial data)
330
+ # + *-store.ts: the state machines, framework-free (ship on every stack)
331
+ hooks/<vertical>/ # React hooks — thin bindings of the stores (SSR-friendly: accept initial data)
171
332
  components/<vertical>/ # routing-free components (plain <a> default + LinkComponent prop)
172
333
  styles/global.css # Tailwind v4 + the @theme design tokens (shared token family)
173
334
  app-astro/ # Astro overlay importing ONLY from the core:
@@ -176,9 +337,14 @@ references/<vertical>/
176
337
  # (storefront ships no pages — its INSTRUCTIONS carries their skeletons)
177
338
  layouts/… # (reuse SiteLayout when it fits)
178
339
  seed/ # seed-<vertical>.mjs (REST, mints its own CLI token) + SEED.md
340
+ rest/ # the REST twin of app/wix/<vertical>/: same exports over fetch, importing
341
+ # the same *-core.ts (rules + DTO mappers, type-only imports) and types.ts;
342
+ # flat ./x.js imports — deploy --stack static composes and strips it
179
343
  ```
180
344
 
181
- Core rules the structure encodes: raw API entities never leave the data layer (DTOs only);
345
+ Core rules the structure encodes: a rule or mapper lives once, in `app/wix/<vertical>/*-core.ts`,
346
+ imported by both transports (a call added to `app/` gets its `rest/` twin in the same PR; `tsc`
347
+ over both is the parity check); raw API entities never leave the data layer (DTOs only);
182
348
  client-shared state uses a module-scope store (never React context — it can't span Astro
183
349
  islands); every image URL is resolved through `src/wix/media.ts`; every money value is a
184
350
  formatted string by the time a component sees it.
@@ -28,18 +28,26 @@ This entry gets a cold environment to the point where the real skill can run, th
28
28
  2. **Hand off (agentic).** Install the skills, then open `wix-headless-fast/SKILL.md` and
29
29
  follow it — it resolves the stack and operation and owns the whole build.
30
30
 
31
- Three starting points come through here, all handled the same way — run the bootstrap, then
31
+ Four starting points come through here, all handled the same way — run the bootstrap, then
32
32
  hand off:
33
33
 
34
34
  - **new** — a prompt with no project (empty CWD)
35
35
  - **connect** — an existing frontend/design not yet on Wix (a project on disk without
36
36
  `wix.config.json`, or a brought-in zip/URL)
37
37
  - **iterate** — a project already connected to Wix (`.wix/` or `wix.config.json` present)
38
+ - **existing site** — the prompt names a Wix site that already exists (by its site id) and asks
39
+ for a new frontend for it; the site keeps its content, the frontend is built here
38
40
 
39
41
  The bootstrap only verifies the CLI and logs you in, so it's fine to run in every case (an
40
42
  existing session just reports `logged_in`).
41
43
 
42
- ## Phase 0 — Node (the one manual prerequisite)
44
+ ## Phase 0 — The project's folder, and Node
45
+
46
+ **Work from the folder that will hold the project.** For a **new** build: an empty folder is the
47
+ project; if the current folder is not empty, make one named for the business and `cd` into it.
48
+ For **connect**, **iterate** and **existing site**: you are already in it. Everything below — the
49
+ bootstrap, the skills, the scaffold — lands in this folder, so a later session opened in the project
50
+ finds all of it.
43
51
 
44
52
  The Wix CLI requires **Node ≥ 20.11**. Check `node -v`; if it errors or prints a lower
45
53
  version, install or upgrade Node first — do **not** work around it:
@@ -0,0 +1,253 @@
1
+ // Attach a NEW headless frontend to an EXISTING Wix site — the fast path for a site that already
2
+ // exists (named in the prompt by its id). `init`/`wix create` cannot do this: they always create
3
+ // a site. This script does what they do AFTER creating one — OAuth app, managed hosting, env
4
+ // vars, `wix.config.json` — against the site id given, then scaffolds and deploys like
5
+ // fast-path.mjs. Nothing on the site is created, changed or deleted; there is no seed step.
6
+ //
7
+ // node <SKILL_ROOT>/install/attach.mjs --site <metaSiteId> --business-name "<Brand>" \
8
+ // --vertical <a>[,<b>…] [--stack astro|react|static] [--subfolder [--folder-name <name>]]
9
+ //
10
+ // --business-name the site's name (names the folder, the hosting slug and the app project).
11
+ // --vertical which shipped code deploys (no seed runs — the site owns its content). Both are
12
+ // the caller's decision, read off the site before calling this (SKILL.md step 3).
13
+ // --stack astro (default): scaffolds the CLI's blank Astro template with the hosting adapter,
14
+ // ready for `wix build` / `wix release`. react|static: writes wix.config.json into the
15
+ // folder and stops — the caller scaffolds (Vite / plain HTML) per SKILL.md.
16
+ // The project is created in the CURRENT DIRECTORY — the folder that already holds the installed
17
+ // skills — so it is self-contained. `--subfolder` (opt-in) creates it in a new folder named after
18
+ // the business instead.
19
+ //
20
+ // Emits ONE JSON event per line (attached, scaffolded, deployed, install_started,
21
+ // ready_for_brand_layer, or error). Requires a logged-in Wix CLI (`npx @wix/cli@latest whoami`)
22
+ // whose account owns or co-manages the site.
23
+ import { spawn, spawnSync, execFileSync } from "node:child_process";
24
+ import { cpSync, existsSync, mkdirSync, mkdtempSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
25
+ import { tmpdir } from "node:os";
26
+ import { dirname, join, resolve } from "node:path";
27
+ import { fileURLToPath } from "node:url";
28
+
29
+ const SKILL_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
30
+ const MANAGE = "https://manage.wix.com";
31
+ const HEADLESS_PROJECT_TYPE_ID = "eb363dea-85a0-4159-9b05-949542be5079";
32
+ const TEMPLATES_REPO = "https://github.com/wix/headless-templates.git";
33
+ const TEMPLATE_PATH = "astro/blank";
34
+
35
+ const emit = (event, extra = {}) => console.log(JSON.stringify({ event, ...extra }));
36
+ const fail = (step, detail) => {
37
+ emit("error", { step, detail: String(detail).slice(0, 600) });
38
+ process.exit(1);
39
+ };
40
+
41
+ // ---- args ---------------------------------------------------------------------------------------
42
+ const argv = process.argv.slice(2);
43
+ const flag = (name) => {
44
+ const i = argv.indexOf(`--${name}`);
45
+ return i !== -1 && argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[i + 1] : null;
46
+ };
47
+ const siteId = flag("site");
48
+ const stack = flag("stack") ?? "astro";
49
+ const subfolder = argv.includes("--subfolder");
50
+ const knownVerticals = readdirSync(join(SKILL_ROOT, "references"), { withFileTypes: true })
51
+ .filter((d) => d.isDirectory() && d.name !== "shared" && existsSync(join(SKILL_ROOT, "references", d.name, "app")))
52
+ .map((d) => d.name);
53
+ const verticals = argv
54
+ .flatMap((a, i) => (a === "--vertical" && argv[i + 1] ? argv[i + 1].split(",") : []))
55
+ .map((v) => v.trim())
56
+ .filter(Boolean);
57
+ const businessName = flag("business-name");
58
+ if (!siteId || !/^[0-9a-f-]{36}$/i.test(siteId) || !businessName || !verticals.length) {
59
+ fail("args", `usage: attach.mjs --site <metaSiteId> --business-name "<Brand>" --vertical <${knownVerticals.join("|")}>[,…] [--stack astro|react|static]`);
60
+ }
61
+ for (const v of verticals) if (!knownVerticals.includes(v)) fail("args", `unknown vertical "${v}" — shipped verticals: ${knownVerticals.join(", ")}`);
62
+ if (!["astro", "react", "static"].includes(stack)) fail("args", `unknown stack "${stack}" — astro, react or static`);
63
+ if (existsSync(join(process.cwd(), "wix.config.json"))) {
64
+ fail("args", "the current directory is already a Wix project (wix.config.json) — attach creates a new frontend; run deploy.mjs here instead");
65
+ }
66
+
67
+ // ---- http ---------------------------------------------------------------------------------------
68
+ const cliToken = (site) => {
69
+ const r = spawnSync("npx", ["-y", "@wix/cli@latest", "token", ...(site ? ["--site", site] : [])], { encoding: "utf8", timeout: 120_000 });
70
+ const t = (r.stdout || "").trim();
71
+ if (r.status !== 0 || !t) fail("auth", (r.stderr || r.stdout || "no token — is the Wix CLI logged in? (npx @wix/cli@latest whoami)").slice(-400));
72
+ return t;
73
+ };
74
+ async function call(base, path, { method = "POST", token, site, body, query } = {}) {
75
+ const url = new URL(base + path);
76
+ for (const [k, v] of Object.entries(query ?? {})) url.searchParams.set(k, String(v));
77
+ const res = await fetch(url, {
78
+ method,
79
+ headers: {
80
+ Authorization: token,
81
+ "Content-Type": "application/json",
82
+ "X-XSRF-TOKEN": "nocheck",
83
+ Cookie: "XSRF-TOKEN=nocheck",
84
+ "User-Agent": "wix-cli",
85
+ ...(site ? { "wix-site-id": site } : {}),
86
+ },
87
+ body: body === undefined ? undefined : JSON.stringify(body),
88
+ });
89
+ const text = await res.text();
90
+ if (!res.ok) throw new Error(`${method} ${url.pathname} ${res.status}: ${text.slice(0, 300)}`);
91
+ try { return JSON.parse(text); } catch { return {}; }
92
+ }
93
+
94
+ const folderName = flag("folder-name") ?? (businessName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40) || "site");
95
+ const projectDir = subfolder ? resolve(process.cwd(), folderName) : process.cwd();
96
+ if (subfolder && existsSync(join(projectDir, "wix.config.json"))) fail("args", `${folderName}/ is already a Wix project — pick --folder-name, or run deploy.mjs there`);
97
+
98
+ // ---- 1 · attach: OAuth app + hosting + env, on the manage host with a site token -----------------
99
+ // The same calls `init` makes after it has created a site, in the same order.
100
+ const siteToken = cliToken(siteId);
101
+ const opts = { token: siteToken, site: siteId };
102
+ let appId, appProject, instanceId, secrets, hosting = "created";
103
+ try {
104
+ const comp = await call(MANAGE, "/_api/companion-apps/v1/companion-apps/get-or-create", { ...opts, body: {} });
105
+ appId = comp.companionApp?.id;
106
+ if (!appId) throw new Error("get-or-create returned no companion app id");
107
+ const slug = folderName;
108
+ try { appProject = (await call(MANAGE, `/_api/wix-code-app-projects/v1/app-projects/${appId}`, { ...opts, method: "GET" })).appProject; } catch { appProject = null; }
109
+ if (appProject) {
110
+ hosting = "reused";
111
+ } else {
112
+ try { await call(MANAGE, `/apps-service/v1/apps/${appId}/set-namespace`, { ...opts, method: "PATCH", body: { appId, appName: businessName, namespace: slug } }); }
113
+ catch (e) { emit("note", { step: "set-namespace", detail: String(e.message).slice(0, 200) }); }
114
+ }
115
+ try {
116
+ const inst = await call(MANAGE, "/apps-installer-service/v1/app-instance/install", { ...opts, body: { tenant: { id: siteId, tenantType: "SITE" }, appInstance: { appDefId: appId, version: "latest" } } });
117
+ instanceId = inst.appInstance?.id;
118
+ } catch (e) { emit("note", { step: "install", detail: String(e.message).slice(0, 200) }); }
119
+ secrets = (await call(MANAGE, `/apps-service/v1/apps/${appId}`, { ...opts, method: "GET", query: { withSecrets: true } })).app?.appSecrets;
120
+ if (!secrets?.appSecret) throw new Error("app secrets unavailable for this app");
121
+ if (!appProject) {
122
+ appProject = (await call(MANAGE, "/_api/wix-code-app-projects/v1/app-projects", { ...opts, body: { appProject: { id: appId, displayName: businessName.slice(0, 50), slug, appProjectTypeId: HEADLESS_PROJECT_TYPE_ID } } })).appProject;
123
+ }
124
+ if (!appProject?.baseUrl) throw new Error("app project has no baseUrl");
125
+ const prod = String(appProject.baseUrl).replace(/\/$/, "");
126
+ const host = new URL(appProject.baseUrl).hostname;
127
+ const local = "http://localhost:4321";
128
+ await call(MANAGE, `/oauth-app-service/v1/oauth-apps/${appId}`, { ...opts, method: "PATCH", body: {
129
+ oAuthApp: {
130
+ id: appId,
131
+ allowedDomains: [local, `https://(.*)-${host}`, prod],
132
+ allowedRedirectUris: [`${local}/api/auth/callback`, `${local}/api/auth/logout-callback`, `https://*-${host}/api/auth/callback`, `https://*-${host}/api/auth/logout-callback`, `${prod}/api/auth/callback`, `${prod}/api/auth/logout-callback`],
133
+ redirectUrlWixPages: prod,
134
+ origin: "other",
135
+ },
136
+ mask: { paths: ["allowedDomains", "allowedRedirectUris", "redirectUrlWixPages", "origin"] },
137
+ } });
138
+ if (!instanceId) {
139
+ // reused app: the instance id is on the existing env, keep it
140
+ try {
141
+ const env = await call(MANAGE, `/_api/wix-code-app-environments/v2/app-projects/${appProject.id}/app-environment-variables`, { ...opts, method: "GET", query: { environment: "system_global" } });
142
+ instanceId = (env.appEnvironmentVariables ?? []).find((v) => v.key === "WIX_CLIENT_INSTANCE_ID")?.value ?? null;
143
+ } catch { /* fall through */ }
144
+ }
145
+ const variables = { WIX_CLIENT_ID: appId, WIX_CLIENT_SECRET: secrets.appSecret, WIX_CLIENT_PUBLIC_KEY: secrets.webhookPublicKey, ...(instanceId ? { WIX_CLIENT_INSTANCE_ID: instanceId } : {}) };
146
+ await call(MANAGE, `/_api/wix-code-app-environments/v2/bulk/app-projects/${appProject.id}/app-environment-variables/upsert`, { ...opts, body: {
147
+ appProjectId: appProject.id, environment: "system_global", mutability: "STATIC", returnEntity: true, returnAllEnvironment: true, variables,
148
+ } });
149
+ } catch (e) {
150
+ fail("attach", e?.message || e);
151
+ }
152
+ const baseUrl = String(appProject.baseUrl).replace(/\/$/, "");
153
+ emit("attached", { siteId, appId, baseUrl, hosting, note: hosting === "reused" ? "this site already has a headless frontend at baseUrl; `wix release` from this project replaces it" : undefined });
154
+
155
+ // ---- 2 · scaffold -------------------------------------------------------------------------------
156
+ mkdirSync(projectDir, { recursive: true });
157
+ if (stack === "astro") {
158
+ // The CLI's own blank template (what `wix create` copies), then what its extender adds: the
159
+ // hosting adapter, React islands, the `wix` scripts. Pinned like a freshly created project.
160
+ const tmp = mkdtempSync(join(tmpdir(), "wix-template-"));
161
+ const clone = spawnSync("git", ["clone", "--quiet", "--depth", "1", "--filter=blob:none", "--sparse", TEMPLATES_REPO, tmp], { encoding: "utf8", timeout: 120_000 });
162
+ if (clone.status !== 0) fail("scaffold", clone.stderr || "git clone of the template repo failed");
163
+ const sparse = spawnSync("git", ["-C", tmp, "sparse-checkout", "set", TEMPLATE_PATH], { encoding: "utf8", timeout: 60_000 });
164
+ if (sparse.status !== 0 || !existsSync(join(tmp, TEMPLATE_PATH, "package.json"))) fail("scaffold", sparse.stderr || `template ${TEMPLATE_PATH} not found in ${TEMPLATES_REPO}`);
165
+ cpSync(join(tmp, TEMPLATE_PATH), projectDir, { recursive: true, force: false, errorOnExist: false });
166
+ rmSync(tmp, { recursive: true, force: true });
167
+ const pkgPath = join(projectDir, "package.json");
168
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
169
+ pkg.name = folderName;
170
+ pkg.scripts = { astro: "astro", dev: "wix dev", build: "wix build", wix: "wix", preview: "wix preview", release: "wix release", generate: "wix generate", env: "wix env", skills: "wix skills" };
171
+ pkg.devDependencies = {
172
+ ...(pkg.devDependencies ?? {}),
173
+ "@astrojs/react": "^4.3.0",
174
+ "@types/react": "^18.3.1",
175
+ "@types/react-dom": "^18.3.1",
176
+ "@wix/cli": "^1.1.92",
177
+ "@wix/astro-wix-hosting-adapter": "^2.0.0",
178
+ react: "18.3.1",
179
+ "react-dom": "18.3.1",
180
+ };
181
+ writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n");
182
+ writeFileSync(join(projectDir, "astro.config.mjs"), `// @ts-check
183
+ import { defineConfig } from "astro/config";
184
+ import wix from "@wix/astro";
185
+ import wixPages from "@wix/astro-pages";
186
+ import react from "@astrojs/react";
187
+ import wixHostingAdapter from "@wix/astro-wix-hosting-adapter";
188
+
189
+ // https://astro.build/config
190
+ export default defineConfig({
191
+ integrations: [wix(), wixPages(), react()],
192
+ security: { checkOrigin: false },
193
+ adapter: wixHostingAdapter(),
194
+ image: { domains: ["static.wixstatic.com"] },
195
+ output: "server",
196
+ });
197
+ `);
198
+ const gi = join(projectDir, ".gitignore");
199
+ const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
200
+ if (!/\.env/.test(cur)) writeFileSync(gi, cur + "\n# local env (pulled from Wix)\n.env.local\n.env\n");
201
+ }
202
+ writeFileSync(join(projectDir, "wix.config.json"), JSON.stringify({ appId, siteId }, null, 2) + "\n");
203
+ // The env the CLI's build reads. Same content `wix env pull` writes; written here so the build
204
+ // needs no extra call.
205
+ const quote = (v) => `"${String(v ?? "").replace(/"/g, '\\"')}"`;
206
+ writeFileSync(join(projectDir, ".env.local"), [
207
+ `WIX_CLOUD_PROVIDER=${quote("wix")}`,
208
+ `WIX_CLIENT_ID=${quote(appId)}`,
209
+ ...(instanceId ? [`WIX_CLIENT_INSTANCE_ID=${quote(instanceId)}`] : []),
210
+ `WIX_CLIENT_PUBLIC_KEY=${quote(secrets.webhookPublicKey)}`,
211
+ `WIX_CLIENT_SECRET=${quote(secrets.appSecret)}`,
212
+ "",
213
+ ].join("\n"));
214
+ emit("scaffolded", { folder: folderName, stack, template: stack === "astro" ? `${TEMPLATES_REPO}#${TEMPLATE_PATH}` : null });
215
+
216
+ if (stack !== "astro") {
217
+ emit("ready", { projectDir, siteId, appId, baseUrl, hosting, stack, dashboardUrl: `https://manage.wix.com/dashboard/${siteId}`,
218
+ next: `scaffold the ${stack} project in this folder per SKILL.md, then deploy.mjs <vertical…> --stack ${stack} (the client id is read from wix.config.json); no seed — the site owns its content` });
219
+ process.exit(0);
220
+ }
221
+
222
+ // ---- 3 · deploy shipped code + deps + lockfile ---------------------------------------------------
223
+ let deployResult = {};
224
+ {
225
+ const deploy = spawnSync("node", [join(SKILL_ROOT, "install", "deploy.mjs"), ...verticals, "--stack", stack], { cwd: projectDir, encoding: "utf8", timeout: 60_000 });
226
+ if (deploy.status !== 0) fail("deploy", deploy.stderr || deploy.stdout);
227
+ try { deployResult = JSON.parse(deploy.stdout); } catch { /* keep going */ }
228
+ if (deployResult.error) fail("deploy", deployResult.error);
229
+ emit("deployed", deployResult);
230
+ }
231
+
232
+ // ---- 4 · dependency install, detached -----------------------------------------------------------
233
+ const installLog = join(projectDir, "npm-install.log");
234
+ const logFd = openSync(installLog, "a");
235
+ const install = spawn("sh", ["-c", "npm ci --ignore-scripts || npm install --ignore-scripts"], { cwd: projectDir, detached: true, stdio: ["ignore", logFd, logFd] });
236
+ install.unref();
237
+ emit("install_started", { log: installLog, doneMarker: "node_modules/.package-lock.json" });
238
+
239
+ // ---- done ----------------------------------------------------------------------------------------
240
+ emit("ready_for_brand_layer", {
241
+ projectDir,
242
+ siteId,
243
+ appId,
244
+ baseUrl,
245
+ hosting,
246
+ verticals,
247
+ dashboardUrl: `https://manage.wix.com/dashboard/${siteId}`,
248
+ productsUrl: deployResult.productsUrl,
249
+ categoriesUrl: deployResult.categoriesUrl,
250
+ install: { log: installLog, doneMarker: "node_modules/.package-lock.json" },
251
+ seed: null,
252
+ next: "the site's content is live already — nothing to seed; read what it holds through the deployed data layer, theme SiteLayout + write the pages; wait for the install marker, build, release",
253
+ });