@wix/agent-skills 1.14.0 → 1.15.0

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 (247) hide show
  1. package/README.md +4 -1
  2. package/package.json +1 -1
  3. package/skills/README.md +23 -0
  4. package/skills/wix-app/SKILL.md +10 -2
  5. package/skills/wix-app/references/AUTO_PATTERNS_DASHBOARD.md +326 -0
  6. package/skills/wix-app/references/CODE_QUALITY.md +1 -0
  7. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +124 -115
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +3 -0
  9. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +23 -9
  10. package/skills/wix-app/references/SITE_PLUGIN.md +1 -1
  11. package/skills/wix-app/references/auto-patterns-dashboard/action-cell.md +115 -0
  12. package/skills/wix-app/references/auto-patterns-dashboard/app-config-structure.md +264 -0
  13. package/skills/wix-app/references/auto-patterns-dashboard/app-context.md +37 -0
  14. package/skills/wix-app/references/auto-patterns-dashboard/bulk-actions.md +86 -0
  15. package/skills/wix-app/references/auto-patterns-dashboard/collection-page-actions.md +119 -0
  16. package/skills/wix-app/references/auto-patterns-dashboard/collection-page.md +103 -0
  17. package/skills/wix-app/references/auto-patterns-dashboard/custom-actions-override.md +76 -0
  18. package/skills/wix-app/references/auto-patterns-dashboard/custom-columns-override.md +82 -0
  19. package/skills/wix-app/references/auto-patterns-dashboard/custom-components-override.md +89 -0
  20. package/skills/wix-app/references/auto-patterns-dashboard/custom-header-override.md +124 -0
  21. package/skills/wix-app/references/auto-patterns-dashboard/custom-sections-override.md +91 -0
  22. package/skills/wix-app/references/auto-patterns-dashboard/custom-slots-override.md +71 -0
  23. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-actions.md +70 -0
  24. package/skills/wix-app/references/auto-patterns-dashboard/entity-page-view-actions.md +73 -0
  25. package/skills/wix-app/references/auto-patterns-dashboard/entity-page.md +98 -0
  26. package/skills/wix-app/references/auto-patterns-dashboard/example-patterns.json +278 -0
  27. package/skills/wix-app/references/auto-patterns-dashboard/pages-configuration.md +94 -0
  28. package/skills/wix-app/references/auto-patterns-dashboard/resolved-action.md +43 -0
  29. package/skills/wix-app/references/auto-patterns-dashboard/sdk-utilities.md +66 -0
  30. package/skills/wix-app/references/auto-patterns-dashboard/views.md +76 -0
  31. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +1 -14
  32. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +54 -29
  33. package/skills/wix-app/references/editor-react-component/COMPONENT-CONFIGURATION.md +2 -2
  34. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +17 -105
  35. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +127 -0
  36. package/skills/wix-app/references/editor-react-component/PARTS.md +2 -1
  37. package/skills/wix-app/references/editor-react-component/REACT-GUIDELINES.md +15 -15
  38. package/skills/wix-app/references/editor-react-component/REACT-PATTERNS.md +25 -53
  39. package/skills/wix-app/scripts/generate-auto-patterns.js +510 -0
  40. package/skills/wix-auth/SKILL.md +12 -0
  41. package/skills/wix-auth/references/device-flow.md +140 -0
  42. package/skills/wix-docs/SKILL.md +188 -0
  43. package/skills/wix-docs/references/API_SPEC_SEARCH.md +213 -0
  44. package/skills/wix-docs/references/EXTRACTING.md +120 -0
  45. package/skills/wix-headless/SKILL.md +84 -130
  46. package/skills/wix-headless/entry/bootstrap.mjs +93 -0
  47. package/skills/wix-headless/entry/skill.md +78 -0
  48. package/skills/wix-headless/references/AI_FEATURES.md +178 -0
  49. package/skills/wix-headless/references/CAPABILITIES.md +84 -0
  50. package/skills/wix-headless/references/DISCOVERY.md +34 -56
  51. package/skills/wix-headless/references/DOC_DISCOVERY.md +37 -0
  52. package/skills/wix-headless/references/FEEDBACK.md +118 -0
  53. package/skills/wix-headless/references/IMAGE_GENERATION.md +69 -0
  54. package/skills/wix-headless/references/SDK_HANDOFF.md +97 -0
  55. package/skills/wix-headless/references/SEED.md +44 -188
  56. package/skills/wix-headless/references/SETUP.md +32 -103
  57. package/skills/wix-headless/references/astro.md +98 -0
  58. package/skills/wix-headless/references/inline-recipes/how-to-code-a-blog.md +196 -0
  59. package/skills/wix-headless/references/inline-recipes/how-to-code-a-store.md +236 -0
  60. package/skills/wix-headless/references/inline-recipes/how-to-code-bookings.md +279 -0
  61. package/skills/wix-headless/references/inline-recipes/how-to-code-cms.md +163 -0
  62. package/skills/wix-headless/references/inline-recipes/how-to-code-events.md +203 -0
  63. package/skills/wix-headless/references/inline-recipes/how-to-code-forms.md +222 -0
  64. package/skills/wix-headless/references/inline-recipes/how-to-code-members-astro.md +124 -0
  65. package/skills/wix-headless/references/inline-recipes/how-to-code-members-custom-login.md +160 -0
  66. package/skills/wix-headless/references/inline-recipes/how-to-code-members-non-astro.md +118 -0
  67. package/skills/wix-headless/references/inline-recipes/how-to-code-portfolio.md +132 -0
  68. package/skills/wix-headless/references/inline-recipes/how-to-code-pricing-plans.md +124 -0
  69. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-orders.md +188 -0
  70. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +173 -0
  71. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurants.md +161 -0
  72. package/skills/wix-headless/references/inline-recipes/setup-blog.md +175 -0
  73. package/skills/wix-headless/references/inline-recipes/setup-bookings.md +228 -0
  74. package/skills/wix-headless/references/inline-recipes/setup-cms.md +200 -0
  75. package/skills/wix-headless/references/inline-recipes/setup-events.md +196 -0
  76. package/skills/wix-headless/references/inline-recipes/setup-forms.md +174 -0
  77. package/skills/wix-headless/references/inline-recipes/setup-online-store.md +176 -0
  78. package/skills/wix-headless/references/inline-recipes/setup-portfolio.md +153 -0
  79. package/skills/wix-headless/references/inline-recipes/setup-pricing-plans.md +229 -0
  80. package/skills/wix-headless/references/inline-recipes/setup-restaurant-orders.md +161 -0
  81. package/skills/wix-headless/references/inline-recipes/setup-restaurant-reservations.md +117 -0
  82. package/skills/wix-headless/references/inline-recipes/setup-restaurants.md +152 -0
  83. package/skills/wix-headless/references/managed/AUTHENTICATION.md +57 -0
  84. package/skills/wix-headless/references/managed/CONNECT.md +74 -0
  85. package/skills/wix-headless/references/managed/CREATE.md +130 -0
  86. package/skills/wix-headless/references/managed/DEPLOYMENT.md +57 -0
  87. package/skills/wix-headless/references/non-astro.md +59 -0
  88. package/skills/wix-headless/references/self-managed/AUTHENTICATION.md +22 -0
  89. package/skills/wix-headless/references/self-managed/DEPLOYMENT.md +7 -0
  90. package/skills/wix-headless/references/stripe/AUTHENTICATION.md +55 -0
  91. package/skills/wix-headless/references/stripe/DEPLOYMENT.md +63 -0
  92. package/skills/wix-manage/SKILL.md +24 -1
  93. package/skills/wix-manage/references/analytics/query-site-analytics.md +241 -0
  94. package/skills/wix-manage/references/bookings/create-and-update-booking-services.md +1 -1
  95. package/skills/wix-manage/references/bookings/diagnose-availability-issues.md +265 -0
  96. package/skills/wix-manage/references/marketing/create-and-publish-social-post.md +436 -0
  97. package/skills/wix-manage/references/marketing/generate-and-publish-marketing-plan.md +209 -0
  98. package/skills/wix-manage/references/rich-content/author-ricos-rich-content.md +249 -0
  99. package/skills/wix-manage/references/stores/create-product-with-options-catalog-v3.md +51 -0
  100. package/skills/wix-vibe-headless/SKILL.md +136 -0
  101. package/skills/wix-vibe-headless/references/blog/INSTRUCTIONS.md +128 -0
  102. package/skills/wix-vibe-headless/references/blog/wix-blog.js +217 -0
  103. package/skills/wix-vibe-headless/references/bookings/INSTRUCTIONS.md +153 -0
  104. package/skills/wix-vibe-headless/references/bookings/wix-bookings-checkout.js +147 -0
  105. package/skills/wix-vibe-headless/references/bookings/wix-bookings-services.js +215 -0
  106. package/skills/wix-vibe-headless/references/cms/INSTRUCTIONS.md +154 -0
  107. package/skills/wix-vibe-headless/references/cms/wix-cms.js +186 -0
  108. package/skills/wix-vibe-headless/references/events/INSTRUCTIONS.md +142 -0
  109. package/skills/wix-vibe-headless/references/events/wix-events-browse.js +135 -0
  110. package/skills/wix-vibe-headless/references/events/wix-events-registration.js +187 -0
  111. package/skills/wix-vibe-headless/references/members/INSTRUCTIONS.md +211 -0
  112. package/skills/wix-vibe-headless/references/members/wix-members-auth.js +387 -0
  113. package/skills/wix-vibe-headless/references/portfolio/INSTRUCTIONS.md +111 -0
  114. package/skills/wix-vibe-headless/references/portfolio/wix-portfolio.js +194 -0
  115. package/skills/wix-vibe-headless/references/pricing-plans/INSTRUCTIONS.md +127 -0
  116. package/skills/wix-vibe-headless/references/pricing-plans/wix-pricing-plans.js +157 -0
  117. package/skills/wix-vibe-headless/references/restaurants/INSTRUCTIONS.md +138 -0
  118. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-menu.js +224 -0
  119. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-ordering.js +156 -0
  120. package/skills/wix-vibe-headless/references/restaurants/wix-restaurants-reservations.js +107 -0
  121. package/skills/wix-vibe-headless/references/shared/wix-client.js +186 -0
  122. package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +155 -0
  123. package/skills/wix-vibe-headless/references/storefront/wix-store-cart.js +147 -0
  124. package/skills/wix-vibe-headless/references/storefront/wix-store-catalog.js +142 -0
  125. package/skills/wix-headless/references/BUILD-astro.md +0 -253
  126. package/skills/wix-headless/references/BUILD-own-build.md +0 -167
  127. package/skills/wix-headless/references/BUILD.md +0 -105
  128. package/skills/wix-headless/references/DESIGN_SYSTEM.md +0 -165
  129. package/skills/wix-headless/references/DISCOVERY-connect.md +0 -55
  130. package/skills/wix-headless/references/DISCOVERY-create.md +0 -238
  131. package/skills/wix-headless/references/PLAN-connect.md +0 -19
  132. package/skills/wix-headless/references/PLAN-create.md +0 -17
  133. package/skills/wix-headless/references/PLAN.md +0 -92
  134. package/skills/wix-headless/references/astro/blog/BLOG_PAGES.md +0 -300
  135. package/skills/wix-headless/references/astro/blog/BLOG_SETUP.md +0 -218
  136. package/skills/wix-headless/references/astro/bookings/COMPONENTS.md +0 -45
  137. package/skills/wix-headless/references/astro/bookings/SERVICES_PAGES.md +0 -77
  138. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +0 -214
  139. package/skills/wix-headless/references/astro/cms/FAQ_KNOWLEDGE_BASE.md +0 -231
  140. package/skills/wix-headless/references/astro/cms/PORTFOLIO.md +0 -453
  141. package/skills/wix-headless/references/astro/cms/RESOURCE_LIBRARY.md +0 -354
  142. package/skills/wix-headless/references/astro/cms/TEAM_DIRECTORY.md +0 -299
  143. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +0 -351
  144. package/skills/wix-headless/references/astro/ecom/CART_PAGES.md +0 -119
  145. package/skills/wix-headless/references/astro/ecom/CART_WIRING.md +0 -103
  146. package/skills/wix-headless/references/astro/ecom/COMPONENTS_CSS.md +0 -132
  147. package/skills/wix-headless/references/astro/forms/CONTACT_FORM.md +0 -463
  148. package/skills/wix-headless/references/astro/gift-cards/COMPONENTS.md +0 -119
  149. package/skills/wix-headless/references/astro/gift-cards/PAGES.md +0 -129
  150. package/skills/wix-headless/references/astro/stores/BACK_IN_STOCK.md +0 -178
  151. package/skills/wix-headless/references/astro/stores/CATEGORY_PAGES.md +0 -85
  152. package/skills/wix-headless/references/astro/stores/COMPONENTS_CSS.md +0 -129
  153. package/skills/wix-headless/references/astro/stores/HOME_AND_NAV.md +0 -229
  154. package/skills/wix-headless/references/astro/stores/PRODUCT_PAGES.md +0 -215
  155. package/skills/wix-headless/references/astro/stores/SHARED_WIRING.md +0 -165
  156. package/skills/wix-headless/references/astro/templates/Layout.astro +0 -132
  157. package/skills/wix-headless/references/astro/templates/astro.config.mjs +0 -41
  158. package/skills/wix-headless/references/astro/templates/bookings/AvailabilityCalendar.tsx +0 -374
  159. package/skills/wix-headless/references/astro/templates/bookings/BookingForm.tsx +0 -136
  160. package/skills/wix-headless/references/astro/templates/bookings/SeoTags.astro +0 -25
  161. package/skills/wix-headless/references/astro/templates/bookings/ServiceBookingFlow.tsx +0 -71
  162. package/skills/wix-headless/references/astro/templates/bookings/ServiceCard.astro +0 -54
  163. package/skills/wix-headless/references/astro/templates/bookings/booking-confirmation.astro +0 -49
  164. package/skills/wix-headless/references/astro/templates/bookings/bookingDriver.ts +0 -245
  165. package/skills/wix-headless/references/astro/templates/bookings/components-bookings.css +0 -424
  166. package/skills/wix-headless/references/astro/templates/bookings/services/[slug].astro +0 -176
  167. package/skills/wix-headless/references/astro/templates/bookings/services/index.astro +0 -181
  168. package/skills/wix-headless/references/astro/templates/ecom/CartBadge.tsx +0 -84
  169. package/skills/wix-headless/references/astro/templates/ecom/CartView.tsx +0 -543
  170. package/skills/wix-headless/references/astro/templates/ecom/cart.astro +0 -13
  171. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +0 -302
  172. package/skills/wix-headless/references/astro/templates/ecom/discounts.ts +0 -71
  173. package/skills/wix-headless/references/astro/templates/ecom/thank-you.astro +0 -24
  174. package/skills/wix-headless/references/astro/templates/gift-cards/GiftCardPurchase.tsx +0 -172
  175. package/skills/wix-headless/references/astro/templates/gift-cards/_home-teaser-snippet.astro +0 -42
  176. package/skills/wix-headless/references/astro/templates/gift-cards/_nav-snippet.astro +0 -11
  177. package/skills/wix-headless/references/astro/templates/gift-cards/components-gift-cards.css +0 -220
  178. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.astro +0 -51
  179. package/skills/wix-headless/references/astro/templates/gift-cards/gift-cards.ts +0 -75
  180. package/skills/wix-headless/references/astro/templates/global.css +0 -288
  181. package/skills/wix-headless/references/astro/templates/stores/AddToCartButton.tsx +0 -77
  182. package/skills/wix-headless/references/astro/templates/stores/BackInStockForm.tsx +0 -176
  183. package/skills/wix-headless/references/astro/templates/stores/CategoryRail.astro +0 -69
  184. package/skills/wix-headless/references/astro/templates/stores/ProductCard.astro +0 -79
  185. package/skills/wix-headless/references/astro/templates/stores/ProductPurchase.tsx +0 -397
  186. package/skills/wix-headless/references/astro/templates/stores/SeoTags.astro +0 -21
  187. package/skills/wix-headless/references/astro/templates/stores/back-in-stock.ts +0 -67
  188. package/skills/wix-headless/references/astro/templates/stores/categories.ts +0 -155
  189. package/skills/wix-headless/references/astro/templates/stores/category/[slug].astro +0 -130
  190. package/skills/wix-headless/references/astro/templates/stores/components-stores.css +0 -400
  191. package/skills/wix-headless/references/astro/templates/stores/products/[slug].astro +0 -128
  192. package/skills/wix-headless/references/astro/templates/stores/products/index.astro +0 -101
  193. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +0 -161
  194. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +0 -32
  195. package/skills/wix-headless/references/bookings/FLOW.md +0 -246
  196. package/skills/wix-headless/references/bookings/INSTRUCTIONS.md +0 -95
  197. package/skills/wix-headless/references/bookings/SERVICES_DATA.md +0 -355
  198. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +0 -176
  199. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +0 -71
  200. package/skills/wix-headless/references/commands/known-apps.json +0 -6
  201. package/skills/wix-headless/references/custom/CONNECTION_PLAN.md +0 -142
  202. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +0 -88
  203. package/skills/wix-headless/references/custom/blog/WIRING.md +0 -52
  204. package/skills/wix-headless/references/custom/bookings/WIRING.md +0 -197
  205. package/skills/wix-headless/references/custom/cms/WIRING.md +0 -97
  206. package/skills/wix-headless/references/custom/ecom/WIRING.md +0 -67
  207. package/skills/wix-headless/references/custom/forms/WIRING.md +0 -101
  208. package/skills/wix-headless/references/custom/stores/WIRING.md +0 -80
  209. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +0 -59
  210. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +0 -43
  211. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +0 -51
  212. package/skills/wix-headless/references/images/INSTRUCTIONS.md +0 -357
  213. package/skills/wix-headless/references/seed-recipes.md +0 -49
  214. package/skills/wix-headless/references/shared/AGENTS_MD.md +0 -47
  215. package/skills/wix-headless/references/shared/AUTHENTICATION.md +0 -108
  216. package/skills/wix-headless/references/shared/BUILD_NOISE.md +0 -8
  217. package/skills/wix-headless/references/shared/DESIGN_MD.md +0 -84
  218. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +0 -41
  219. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +0 -186
  220. package/skills/wix-headless/references/shared/IMPLEMENTER.md +0 -206
  221. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +0 -97
  222. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +0 -143
  223. package/skills/wix-headless/references/shared/STYLING.md +0 -140
  224. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +0 -126
  225. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +0 -252
  226. package/skills/wix-headless/references/verticals/_schema.json +0 -74
  227. package/skills/wix-headless/references/verticals/_schema.md +0 -95
  228. package/skills/wix-headless/references/verticals/blog.md +0 -31
  229. package/skills/wix-headless/references/verticals/bookings.md +0 -41
  230. package/skills/wix-headless/references/verticals/cms.md +0 -30
  231. package/skills/wix-headless/references/verticals/ecom.md +0 -25
  232. package/skills/wix-headless/references/verticals/forms.md +0 -26
  233. package/skills/wix-headless/references/verticals/gift-cards.md +0 -24
  234. package/skills/wix-headless/references/verticals/stores.md +0 -33
  235. package/skills/wix-headless/scripts/check-manifest.mjs +0 -335
  236. package/skills/wix-headless/scripts/compose.mjs +0 -520
  237. package/skills/wix-headless/scripts/emit-design-tokens.mjs +0 -157
  238. package/skills/wix-headless/scripts/flatten.sh +0 -61
  239. package/skills/wix-headless/scripts/generate-images.mjs +0 -242
  240. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +0 -154
  241. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +0 -190
  242. package/skills/wix-headless/scripts/scaffold.sh +0 -139
  243. package/skills/wix-headless/scripts/seed-utilities.sh +0 -93
  244. package/skills/wix-headless/scripts/write-decorative-json.mjs +0 -62
  245. package/skills/wix-headless/shared-utilities/analytics.ts +0 -40
  246. package/skills/wix-headless/shared-utilities/ricos.ts +0 -179
  247. package/skills/wix-headless/shared-utilities/wix-image.ts +0 -72
@@ -1,157 +0,0 @@
1
- #!/usr/bin/env node
2
- // Project .wix/design-tokens.css + .wix/site.d.ts from the Designer's DESIGN.md.
3
- //
4
- // The Designer authors DESIGN.md directly (the run is single-folder — CWD is the
5
- // project — so the Designer can write it during Phase 2). This script does NOT
6
- // write DESIGN.md; it reads the frontmatter the Designer wrote and projects the
7
- // two MECHANICAL artifacts an LLM must not hand-write (a malformed :root block or
8
- // divergent type union is a silent build/drift bug):
9
- // .wix/design-tokens.css — :root custom properties (build-consumed; any frontend imports it).
10
- // .wix/site.d.ts — typed token-name unions.
11
- //
12
- // DESIGN.md frontmatter is the single design source — compose.mjs (astro) reads
13
- // the same file. Runs in the Setup-window bridge, after the Designer returns. On
14
- // the own-build (SPA) path it runs too (the SPA imports design-tokens.css);
15
- // compose.mjs does NOT run there (it is astro-only).
16
- //
17
- // Usage:
18
- // node emit-design-tokens.mjs <project-dir> # reads <project-dir>/DESIGN.md
19
- // node emit-design-tokens.mjs <project-dir> --design-md <path> # explicit DESIGN.md path
20
-
21
- import { readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
22
- import { join } from "node:path";
23
-
24
- // ── args ─────────────────────────────────────────────────────────────────────
25
- const argv = process.argv.slice(2);
26
- let designMdPath = null;
27
- const positional = [];
28
- for (let i = 0; i < argv.length; i++) {
29
- if (argv[i] === "--design-md") designMdPath = argv[++i];
30
- else if (argv[i].startsWith("--design-md=")) designMdPath = argv[i].slice("--design-md=".length);
31
- else positional.push(argv[i]);
32
- }
33
- const projectDir = positional[0] ?? process.cwd();
34
- const designMd = designMdPath ?? join(projectDir, "DESIGN.md");
35
-
36
- // ── DESIGN.md frontmatter parser ──────────────────────────────────────────────
37
- // Mirrors compose.mjs's parser for the restricted DESIGN.md shape (groups of
38
- // `key: scalar`, typography tokens with an indented or flow-style fontFamily).
39
- // The format contract is references/shared/DESIGN_MD.md; both readers must agree.
40
- // Color values are quoted because an unquoted `#hex` after `: ` is a YAML comment.
41
- function unquote(v) {
42
- v = v.trim();
43
- const qc = v[0];
44
- if (qc === '"' || qc === "'") {
45
- let out = "";
46
- for (let i = 1; i < v.length; i++) {
47
- const c = v[i];
48
- if (qc === '"' && c === "\\" && i + 1 < v.length) { out += v[++i]; continue; }
49
- if (c === qc) break;
50
- out += c;
51
- }
52
- return out;
53
- }
54
- const hash = v.search(/\s#/); // strip trailing inline comment on bare scalars
55
- if (hash !== -1) v = v.slice(0, hash).trim();
56
- return v;
57
- }
58
- function parseInlineObject(s) {
59
- const inner = s.trim().replace(/^\{/, "").replace(/\}$/, "");
60
- const obj = {};
61
- let buf = "", inq = null;
62
- const parts = [];
63
- for (const ch of inner) {
64
- if (inq) { if (ch === inq) inq = null; buf += ch; continue; }
65
- if (ch === '"' || ch === "'") { inq = ch; buf += ch; continue; }
66
- if (ch === ",") { parts.push(buf); buf = ""; continue; }
67
- buf += ch;
68
- }
69
- if (buf.trim()) parts.push(buf);
70
- for (const p of parts) {
71
- const ci = p.indexOf(":");
72
- if (ci === -1) continue;
73
- obj[p.slice(0, ci).trim()] = unquote(p.slice(ci + 1));
74
- }
75
- return obj;
76
- }
77
- function parseFrontmatter(text) {
78
- const m = text.match(/^---\r?\n([\s\S]*?)\r?\n---/);
79
- if (!m) return null;
80
- const root = {};
81
- let group = null, sub = null;
82
- for (const raw of m[1].split(/\r?\n/)) {
83
- if (!raw.trim() || /^\s*#/.test(raw)) continue;
84
- const indent = raw.match(/^ */)[0].length;
85
- const ci = raw.indexOf(":");
86
- if (ci === -1) continue;
87
- const key = raw.slice(0, ci).trim();
88
- const rest = raw.slice(ci + 1).trim();
89
- if (indent === 0) {
90
- if (rest === "") { root[key] = {}; group = key; sub = null; }
91
- else if (rest.startsWith("{")) { root[key] = parseInlineObject(rest); group = null; sub = null; }
92
- else { root[key] = unquote(rest); group = null; sub = null; }
93
- } else if (indent === 2 && group) {
94
- if (rest === "") { root[group][key] = {}; sub = key; }
95
- else if (rest.startsWith("{")) { root[group][key] = parseInlineObject(rest); sub = null; }
96
- else { root[group][key] = unquote(rest); sub = null; }
97
- } else if (indent >= 4 && group && sub) {
98
- if (typeof root[group][sub] !== "object") root[group][sub] = {};
99
- root[group][sub][key] = unquote(rest);
100
- }
101
- }
102
- return root;
103
- }
104
-
105
- // ── read + validate the Designer's DESIGN.md ──────────────────────────────────
106
- if (!existsSync(designMd)) {
107
- console.error(`emit-design-tokens: no DESIGN.md at ${designMd} — the Designer must author it first`);
108
- process.exit(2);
109
- }
110
- const design = parseFrontmatter(readFileSync(designMd, "utf8"));
111
- if (!design || typeof design !== "object" || Object.keys(design).length === 0) {
112
- console.error(`emit-design-tokens: could not parse YAML frontmatter from ${designMd} (check it starts with '---', values are quoted, 2-space indent)`);
113
- process.exit(2);
114
- }
115
-
116
- const colors = design.colors ?? {};
117
- const typography = design.typography ?? {};
118
- const rounded = design.rounded ?? {};
119
- const containers = design.containers ?? {};
120
- const fontFamilyOf = (lvl) => (typography[lvl] && typeof typography[lvl] === "object" ? typography[lvl].fontFamily : typography[lvl]);
121
-
122
- if (Object.keys(colors).length === 0) {
123
- console.error(`emit-design-tokens: DESIGN.md frontmatter has no 'colors' group — cannot project tokens (${designMd})`);
124
- process.exit(2);
125
- }
126
-
127
- const wixDir = join(projectDir, ".wix");
128
- mkdirSync(wixDir, { recursive: true });
129
-
130
- // ── .wix/design-tokens.css ────────────────────────────────────────────────────
131
- const cssLines = ["/* Generated by emit-design-tokens.mjs. Do not edit. */", ":root {"];
132
- for (const [k, v] of Object.entries(colors)) cssLines.push(` --color-${k}: ${v};`);
133
- for (const lvl of Object.keys(typography)) {
134
- const fam = fontFamilyOf(lvl);
135
- if (fam) cssLines.push(` --font-${lvl}: ${fam};`);
136
- }
137
- for (const [k, v] of Object.entries(rounded)) cssLines.push(` --radius-${k}: ${v};`);
138
- for (const [k, v] of Object.entries(containers)) cssLines.push(` --container-${k}: ${v};`);
139
- cssLines.push("}", "");
140
- writeFileSync(join(wixDir, "design-tokens.css"), cssLines.join("\n"));
141
-
142
- // ── .wix/site.d.ts ────────────────────────────────────────────────────────────
143
- const recordType = (keys) => {
144
- if (keys.length === 0) return "Record<string, string>";
145
- return `Record<${keys.map((k) => JSON.stringify(k)).join(" | ")}, string>`;
146
- };
147
- const dts = `// Generated by emit-design-tokens.mjs. Do not edit.
148
- export type DesignTokens = {
149
- colors: ${recordType(Object.keys(colors))};
150
- fonts: ${recordType(Object.keys(typography))};
151
- radii: ${recordType(Object.keys(rounded))};
152
- containers: ${recordType(Object.keys(containers))};
153
- };
154
- `;
155
- writeFileSync(join(wixDir, "site.d.ts"), dts);
156
-
157
- console.log(`emit-design-tokens: projected ${join(wixDir, "design-tokens.css")} and ${join(wixDir, "site.d.ts")} from ${designMd}`);
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env bash
2
- # Flatten a scaffolded subdir up into the current directory — the framework-
3
- # agnostic half of single-folder bootstrap for the own-build path.
4
- #
5
- # WHY THIS EXISTS: every framework scaffolder (Vite, Vue, SvelteKit, …) insists
6
- # on creating a *named subdir* and offers no reliable in-place option. The skill
7
- # wants the project in CWD (one `.wix`, CWD == project == site-root — same layout
8
- # astro's scaffold.sh produces). The flatten is identical regardless of framework,
9
- # so it lives here as ONE tested script instead of being re-derived in prose every
10
- # run (the orchestrator otherwise burns a long turn agonizing over mv -n vs rsync
11
- # vs cp --no-clobber and BSD-vs-GNU portability). Scaffold into a temp subdir with
12
- # the framework's own command, then call this.
13
- #
14
- # astro's scaffold.sh ALSO delegates its in-place flatten here (same logic), so
15
- # this is the single source for the flatten. Remove the flatten entirely when the
16
- # CLI gains `--folder-name .` (or --cwd) — the in-place flag obviates it.
17
- #
18
- # Usage:
19
- # bash <SKILL_ROOT>/scripts/flatten.sh <scaffold-subdir>
20
- #
21
- # <scaffold-subdir>: the transient directory the framework scaffolder created
22
- # (e.g. "_scaffold_tmp"). Everything inside it — including
23
- # dotfiles — is moved up into CWD; its `.wix/*` is merged into
24
- # the existing `.wix/` WITHOUT overwriting any pre-existing
25
- # `.wix/` entries (mv -n); then the subdir is removed.
26
- #
27
- # Exit codes:
28
- # 0 — ok (or subdir absent / already flat — no-op)
29
- # 2 — argument missing
30
- # 5 — could not fully flatten (name collision left files behind)
31
-
32
- set -euo pipefail
33
-
34
- SUBDIR="${1:-}"
35
- if [[ -z "$SUBDIR" ]]; then
36
- echo "flatten.sh: scaffold-subdir argument required." >&2
37
- echo "Usage: bash flatten.sh <scaffold-subdir>" >&2
38
- exit 2
39
- fi
40
-
41
- # Absent or not a dir → nothing to flatten (scaffolder may have gone in-place).
42
- [[ -d "$SUBDIR" ]] || { echo "flatten.sh: '$SUBDIR' is not a directory — nothing to flatten."; exit 0; }
43
-
44
- shopt -s dotglob nullglob
45
- mkdir -p .wix
46
- if [[ -d "$SUBDIR/.wix" ]]; then
47
- for f in "$SUBDIR"/.wix/*; do
48
- mv -n "$f" .wix/ # -n: never overwrite pre-existing `.wix/` entries
49
- done
50
- rmdir "$SUBDIR/.wix" 2>/dev/null || rm -rf "$SUBDIR/.wix"
51
- fi
52
- for f in "$SUBDIR"/*; do
53
- mv -n "$f" ./
54
- done
55
- shopt -u dotglob nullglob
56
-
57
- if ! rmdir "$SUBDIR" 2>/dev/null; then
58
- echo "flatten.sh: could not fully flatten '$SUBDIR' into CWD — leftover:" >&2
59
- ls -A "$SUBDIR" >&2
60
- exit 5
61
- fi
@@ -1,242 +0,0 @@
1
- #!/usr/bin/env node
2
- // Parallel image generator + Wix Media importer.
3
- //
4
- // WHY THIS EXISTS. Image generation is N independent (generate → import) chains
5
- // with no cross-image dependency, so it is embarrassingly parallel. When the
6
- // agent drives it by hand the chains serialize into minutes of avoidable wall.
7
- // The agent ALSO cannot use the
8
- // single-array batch form on the default model (`google:4@2` 504s when one
9
- // request carries N≥3 tasks). This script resolves both: it fires N *separate
10
- // single-task* requests CONCURRENTLY in-process (Promise.all), so there is no
11
- // per-turn serialization to lose and no N≥3-in-one-request 504 to hit. It is
12
- // the deterministic analog of the "one concurrent batch" rule the prose docs
13
- // ask the agent to follow by hand.
14
- //
15
- // SCOPE. Generation + Wix Media import ONLY — the parallelizable, recipe-free
16
- // half. It returns a key→{url,fileId} map. Attachment stays where the recipe
17
- // knowledge lives: Phase 1 decorative URLs flow into write-decorative-json.mjs;
18
- // Phase 2 entity URLs flow into the per-entity PATCH/PUT/publish plan from
19
- // plan-entity-image-waves.mjs. This script never touches frontend source or
20
- // Wix entities.
21
- //
22
- // Phase-agnostic: `key` is a slot name for Phase 1 (`hero`, `about`) or an
23
- // entityId for Phase 2. The caller interprets the returned map.
24
- //
25
- // AUTH (references/shared/AUTHENTICATION.md). Site-scoped REST token, minted
26
- // once per run and reused. Pass it in via the WIX_TOKEN env var (preferred —
27
- // honors mint-once) or the stdin `token` field. As a last resort the script
28
- // mints one itself with `npx @wix/cli@latest token --site <siteId>` in
29
- // <projectDir>; that costs a CLI spin-up, so prefer passing the cached token.
30
- //
31
- // USAGE
32
- // echo '{
33
- // "siteId": "ed57...",
34
- // "model": "google:4@2",
35
- // "projectDir": "/abs/path/frenchiesgoodies", // only needed if minting
36
- // "images": [
37
- // {"key":"hero","positivePrompt":"...","width":1376,"height":768},
38
- // {"key":"about","positivePrompt":"...","width":1200,"height":896}
39
- // ]
40
- // }' | WIX_TOKEN="$TOKEN" node generate-images.mjs
41
- //
42
- // OUTPUT (stdout, JSON)
43
- // {
44
- // "status": "complete" | "partial" | "failed",
45
- // "model": "google:4@2",
46
- // "count": { "requested": 2, "ok": 2, "failed": 0 },
47
- // "map": { "hero": {"url":"https://static.wixstatic.com/...","fileId":"..~mv2.png"} },
48
- // "slots": { "hero": "https://static.wixstatic.com/..." }, // key→url, ready for write-decorative-json.mjs
49
- // "results": [ {"key":"hero","taskUUID":"..","status":"ok","genReqId":"..","importReqId":".."} ],
50
- // "errors": [ {"key":"about","stage":"generate","code":"504","message":".."} ]
51
- // }
52
- // Exit 0 on complete/partial, 1 on failed (zero images succeeded) or bad input.
53
- // x-wix-request-id is captured per call into results[] for trace analysis — it
54
- // is never printed to stderr or narrated (CLAUDE.md run policy).
55
-
56
- import { readFileSync } from "node:fs";
57
- import { execFileSync } from "node:child_process";
58
- import { randomUUID } from "node:crypto";
59
-
60
- const RUNWARE_URL = "https://www.wixapis.com/runwareschemaless/v1/request";
61
- const MEDIA_IMPORT_URL = "https://www.wixapis.com/site-media/v1/files/import";
62
- const GEN_TIMEOUT_MS = 120_000; // google:4@2 is slow; generous per-task cap
63
- const IMPORT_TIMEOUT_MS = 60_000;
64
- const ALLOWED_DIMS = [[1024, 1024], [1376, 768], [1200, 896]];
65
-
66
- function fail(reason) {
67
- console.log(JSON.stringify({ status: "failed", reason }, null, 2));
68
- process.exit(1);
69
- }
70
-
71
- // ---- input ----------------------------------------------------------------
72
- let input;
73
- try {
74
- input = JSON.parse(readFileSync(0, "utf8"));
75
- } catch (e) {
76
- fail(`stdin is not valid JSON (${e.message})`);
77
- }
78
- const siteId = input.siteId;
79
- const model = input.model || "google:4@2";
80
- const images = Array.isArray(input.images) ? input.images : [];
81
- if (!siteId) fail("siteId is required");
82
- if (images.length === 0) fail("no images in input — nothing to generate");
83
-
84
- // ---- token (mint-once: prefer env/field, mint only as fallback) -----------
85
- let token = process.env.WIX_TOKEN || input.token;
86
- if (!token) {
87
- try {
88
- token = execFileSync("npx", ["@wix/cli@latest", "token", "--site", siteId], {
89
- cwd: input.projectDir || process.cwd(),
90
- encoding: "utf8",
91
- stdio: ["ignore", "pipe", "ignore"],
92
- }).trim();
93
- } catch (e) {
94
- fail(`no token: pass WIX_TOKEN env or stdin "token", or run where the CLI can mint (${e.message})`);
95
- }
96
- }
97
- if (!token) fail("token minting returned empty");
98
-
99
- const baseHeaders = {
100
- Authorization: `Bearer ${token}`,
101
- "wix-site-id": siteId,
102
- "Content-Type": "application/json",
103
- };
104
-
105
- // ---- one fetch with timeout + reqid capture -------------------------------
106
- async function call(url, body, timeoutMs) {
107
- const ctrl = new AbortController();
108
- const t = setTimeout(() => ctrl.abort(), timeoutMs);
109
- try {
110
- const res = await fetch(url, {
111
- method: "POST",
112
- headers: baseHeaders,
113
- body: JSON.stringify(body),
114
- signal: ctrl.signal,
115
- });
116
- const reqId = res.headers.get("x-wix-request-id") || null;
117
- const text = await res.text();
118
- let json = null;
119
- try { json = text ? JSON.parse(text) : null; } catch { /* keep raw */ }
120
- return { ok: res.ok, status: res.status, reqId, json, raw: text };
121
- } finally {
122
- clearTimeout(t);
123
- }
124
- }
125
-
126
- function normalizeDims(w, h) {
127
- if (ALLOWED_DIMS.some(([aw, ah]) => aw === w && ah === h)) return [w, h];
128
- return [1024, 1024]; // safe default — Runware rejects free-form sizes (400)
129
- }
130
-
131
- // Generate one image (single-task body — the form the default model accepts),
132
- // then import it to Wix Media. Returns a per-image record; never throws.
133
- async function genAndImport(img) {
134
- const key = img.key;
135
- const taskUUID = img.taskUUID || randomUUID();
136
- const [width, height] = normalizeDims(img.width ?? 1024, img.height ?? 1024);
137
- const outputFormat = (img.outputFormat || "PNG").toUpperCase();
138
- const rec = { key, taskUUID, status: "failed", genReqId: null, importReqId: null };
139
-
140
- if (!img.positivePrompt) {
141
- return { ...rec, error: { key, stage: "input", code: "NO_PROMPT", message: "positivePrompt missing" } };
142
- }
143
-
144
- // --- generate (retry once on transient 5xx/504/timeout) ---
145
- const task = {
146
- taskType: "imageInference",
147
- taskUUID,
148
- outputType: "URL",
149
- outputFormat,
150
- positivePrompt: img.positivePrompt,
151
- width,
152
- height,
153
- model,
154
- numberResults: 1,
155
- // NB: never send `steps`/`CFGScale` — google:4@2 rejects them (400).
156
- };
157
- let gen, imageURL;
158
- for (let attempt = 0; attempt < 2; attempt++) {
159
- try {
160
- gen = await call(RUNWARE_URL, [task], GEN_TIMEOUT_MS);
161
- } catch (e) {
162
- gen = { ok: false, status: e.name === "AbortError" ? 504 : 0, reqId: null, json: null, raw: String(e) };
163
- }
164
- rec.genReqId = gen.reqId;
165
- const data = Array.isArray(gen.json?.data) ? gen.json.data : null;
166
- imageURL = data?.[0]?.imageURL;
167
- if (gen.ok && imageURL) break;
168
- // transient → retry once; hard 4xx (except 504-shaped) → give up
169
- const transient = gen.status >= 500 || gen.status === 504 || gen.status === 0;
170
- if (!transient || attempt === 1) {
171
- return {
172
- ...rec,
173
- error: {
174
- key, stage: "generate", code: String(gen.status),
175
- message: gen.json?.errors?.[0]?.message || gen.json?.errorMessage || gen.raw?.slice(0, 200) || "no imageURL in response",
176
- reqId: gen.reqId,
177
- },
178
- };
179
- }
180
- }
181
-
182
- // --- import to Wix Media ---
183
- let imp;
184
- try {
185
- imp = await call(MEDIA_IMPORT_URL, {
186
- url: imageURL,
187
- mimeType: outputFormat === "JPG" ? "image/jpeg" : "image/png",
188
- displayName: img.displayName || `${key}.${outputFormat.toLowerCase()}`,
189
- }, IMPORT_TIMEOUT_MS);
190
- } catch (e) {
191
- imp = { ok: false, status: 0, reqId: null, json: null, raw: String(e) };
192
- }
193
- rec.importReqId = imp.reqId;
194
- const file = imp.json?.file;
195
- if (!imp.ok || !file?.url) {
196
- return {
197
- ...rec,
198
- error: {
199
- key, stage: "import", code: String(imp.status),
200
- message: imp.json?.message || imp.raw?.slice(0, 200) || "no file.url in import response",
201
- reqId: imp.reqId,
202
- },
203
- // generation succeeded — surface the raw URL so the caller can decide
204
- genURL: imageURL,
205
- };
206
- }
207
-
208
- return { ...rec, status: "ok", url: file.url, fileId: file.fileUrl }; // Wix returns the file ID in .fileUrl (not .url)
209
- }
210
-
211
- // ---- run all in parallel --------------------------------------------------
212
- const settled = await Promise.allSettled(images.map(genAndImport));
213
- const results = settled.map((s, i) =>
214
- s.status === "fulfilled" ? s.value
215
- : { key: images[i].key, status: "failed", error: { key: images[i].key, stage: "internal", message: String(s.reason) } });
216
-
217
- const map = {};
218
- const slots = {};
219
- const errors = [];
220
- let okCount = 0;
221
- for (const r of results) {
222
- if (r.status === "ok") {
223
- okCount++;
224
- map[r.key] = { url: r.url, fileId: r.fileId };
225
- slots[r.key] = r.url;
226
- } else if (r.error) {
227
- errors.push(r.error);
228
- }
229
- }
230
-
231
- const status = okCount === images.length ? "complete" : okCount === 0 ? "failed" : "partial";
232
- console.log(JSON.stringify({
233
- status,
234
- model,
235
- count: { requested: images.length, ok: okCount, failed: images.length - okCount },
236
- map,
237
- slots,
238
- results: results.map(({ url, fileId, genURL, ...keep }) => keep), // keep slim; urls live in map
239
- errors,
240
- }, null, 2));
241
-
242
- process.exit(status === "failed" ? 1 : 0);
@@ -1,154 +0,0 @@
1
- #!/usr/bin/env node
2
- // Inject Image-Phase-1 decorative URLs into the page shells.
3
- //
4
- // The design-system Composer (index.astro) and the Phase 4 page designers write
5
- // `<el data-decorative-slot="<key>">…</el>` placeholders in src/pages/*.astro
6
- // (any element — <div>, <section>, <figure>…). Image Phase 1 returns a slot→URL
7
- // map in its JSON return; the orchestrator pipes that map (as JSON) into this
8
- // script's stdin. This script reads the map + walks src/pages/*.astro,
9
- // then injects an <img> as the first child of each matching empty slot element —
10
- // pure string substitution, no LLM, no file rewrites.
11
- //
12
- // Usage:
13
- // echo '{"hero":"https://...","about":"https://..."}' \
14
- // | node patch-decorative-slots.mjs <scaffold-dir>
15
- //
16
- // <scaffold-dir> is the dir that contains src/pages/. The orchestrator
17
- // passes the scaffold subdir explicitly — no eval-dir vs scaffold-dir
18
- // auto-detection is needed for the URL map (it's on stdin), but src/pages
19
- // is still resolved per-input since the orchestrator may call this from
20
- // either the scaffold subdir or the eval root.
21
- //
22
- // Behavior:
23
- // - Empty stdin or no slot→URL pairs → exit 0 status="skipped" (themed-
24
- // blocks mode or Image Phase 1 had no URLs to publish).
25
- // - src/pages missing at <scaffold-dir> and in any single subdir one
26
- // level down → exit 2 status="error".
27
- // - For each `data-decorative-slot="<key>"` element in src/pages/*.astro:
28
- // * Key has no URL in the input map → leave the element alone.
29
- // * Element already has a non-comment child → skip with a warning.
30
- // * Otherwise inject <img …> as the first child.
31
- // - Output JSON summary of patches/skips/warnings to stdout.
32
-
33
- import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "node:fs";
34
- import { join } from "node:path";
35
-
36
- const projectDir = process.argv[2] ?? process.cwd();
37
-
38
- let urlMap = {};
39
- if (!process.stdin.isTTY) {
40
- const raw = readFileSync(0, "utf8").trim();
41
- if (raw) {
42
- try {
43
- urlMap = JSON.parse(raw);
44
- } catch (e) {
45
- console.error(JSON.stringify({ status: "error", reason: `stdin is not valid JSON (${e.message})` }));
46
- process.exit(2);
47
- }
48
- if (!urlMap || typeof urlMap !== "object" || Array.isArray(urlMap)) {
49
- console.error(JSON.stringify({ status: "error", reason: "expected a JSON object mapping slot keys to URLs" }));
50
- process.exit(2);
51
- }
52
- }
53
- }
54
-
55
- if (Object.keys(urlMap).length === 0) {
56
- console.log(JSON.stringify({ status: "skipped", reason: "no slot→URL pairs on stdin (themed-blocks mode or Image Phase 1 produced none)" }));
57
- process.exit(0);
58
- }
59
-
60
- function findPagesDir() {
61
- const direct = join(projectDir, "src/pages");
62
- if (existsSync(direct)) return { path: direct, source: "projectDir" };
63
- const hits = [];
64
- for (const entry of readdirSync(projectDir, { withFileTypes: true })) {
65
- if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
66
- const candidate = join(projectDir, entry.name, "src/pages");
67
- if (existsSync(candidate)) hits.push(candidate);
68
- }
69
- if (hits.length === 1) return { path: hits[0], source: "scaffold-subdir" };
70
- if (hits.length > 1) return { path: null, source: "ambiguous", hits };
71
- return { path: null, source: "missing" };
72
- }
73
-
74
- const pagesResolved = findPagesDir();
75
- if (!pagesResolved.path) {
76
- console.error(JSON.stringify({
77
- status: "error",
78
- reason: "src/pages not found at <projectDir>/src/pages or in any scaffold subdir; pass the scaffold dir explicitly",
79
- projectDir,
80
- pagesSearchedAt: pagesResolved.source === "ambiguous" ? pagesResolved.hits : [join(projectDir, "src/pages")],
81
- }, null, 2));
82
- process.exit(2);
83
- }
84
-
85
- const pagesDir = pagesResolved.path;
86
-
87
- const candidates = [];
88
- const walk = (dir) => {
89
- if (!existsSync(dir)) return;
90
- for (const entry of readdirSync(dir)) {
91
- const full = join(dir, entry);
92
- const stat = statSync(full);
93
- if (stat.isDirectory()) walk(full);
94
- else if (stat.isFile() && entry.endsWith(".astro")) candidates.push(full);
95
- }
96
- };
97
- walk(pagesDir);
98
-
99
- const patched = [];
100
- const skipped = [];
101
- const warnings = [];
102
-
103
- // Match ANY element carrying data-decorative-slot, not just <div>. Full-page
104
- // LLM generation routinely puts the slot on a <section>/<figure>/<picture>;
105
- // the closing tag is matched by backreference (\2) to the opening tag name.
106
- // Slots are empty placeholders by contract, so the non-greedy body cannot
107
- // straddle a sibling element of the same tag.
108
- const SLOT_REGEX = /(<([a-zA-Z][\w-]*)[^>]*\bdata-decorative-slot="([^"]+)"[^>]*>)([\s\S]*?)(<\/\2>)/g;
109
-
110
- for (const file of candidates) {
111
- const original = readFileSync(file, "utf8");
112
- let modified = original;
113
- let fileChanged = false;
114
-
115
- modified = modified.replace(SLOT_REGEX, (match, openTag, _tagName, slotKey, inner, closeTag) => {
116
- const url = urlMap[slotKey];
117
- if (!url) {
118
- skipped.push({ file, slot: slotKey, reason: "no URL for this slot in input map" });
119
- return match;
120
- }
121
-
122
- const stripped = inner.replace(/<!--[\s\S]*?-->/g, "").trim();
123
-
124
- if (/<img\b/i.test(stripped)) {
125
- skipped.push({ file, slot: slotKey, reason: "div already contains an <img> tag (idempotent skip)" });
126
- return match;
127
- }
128
-
129
- if (stripped.length > 0) {
130
- warnings.push({ file, slot: slotKey, reason: "slot element has existing non-image child content; not patching to avoid clobber" });
131
- return match;
132
- }
133
-
134
- fileChanged = true;
135
- const img = `<img src="${url}" alt="" loading="lazy" decoding="async" class="decorative-slot-img" />`;
136
- patched.push({ file, slot: slotKey });
137
- return `${openTag}\n ${img}${inner}${closeTag}`;
138
- });
139
-
140
- if (fileChanged) {
141
- writeFileSync(file, modified);
142
- }
143
- }
144
-
145
- const summary = {
146
- status: warnings.length > 0 ? "partial" : "ok",
147
- slots: Object.keys(urlMap),
148
- filesScanned: candidates.length,
149
- patched,
150
- skipped,
151
- warnings,
152
- pagesFrom: pagesResolved.source,
153
- };
154
- console.log(JSON.stringify(summary, null, 2));