@wix/agent-skills 1.5.0 → 1.7.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 (156) hide show
  1. package/README.md +2 -2
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +49 -42
  4. package/skills/wix-app/references/BACKEND_EVENT.md +10 -65
  5. package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +101 -406
  6. package/skills/wix-app/references/DASHBOARD_MENU_PLUGIN.md +14 -111
  7. package/skills/wix-app/references/DASHBOARD_MODAL.md +56 -205
  8. package/skills/wix-app/references/DASHBOARD_PAGE.md +19 -98
  9. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +6 -108
  10. package/skills/wix-app/references/DATA_COLLECTION.md +113 -198
  11. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +15 -1
  12. package/skills/wix-app/references/EMBEDDED_SCRIPT.md +41 -268
  13. package/skills/wix-app/references/EXTENSION_REGISTRATION.md +27 -76
  14. package/skills/wix-app/references/SERVICE_PLUGIN.md +35 -188
  15. package/skills/wix-app/references/SITE_PLUGIN.md +8 -92
  16. package/skills/wix-app/references/backend-event/COMMON-EVENTS.md +0 -44
  17. package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +19 -330
  18. package/skills/wix-app/references/dashboard-menu-plugin/bookings-slots.md +0 -12
  19. package/skills/wix-app/references/dashboard-menu-plugin/ecommerce-slots.md +7 -0
  20. package/skills/wix-app/references/dashboard-menu-plugin/restaurants-slots.md +0 -12
  21. package/skills/wix-app/references/dashboard-plugin/SLOTS.md +3 -15
  22. package/skills/wix-app/references/data-collection/WIX_DATA.md +1 -1
  23. package/skills/wix-app/references/editor-react-component/COMPONENT-API.md +1 -1
  24. package/skills/wix-app/references/service-plugin/ADDITIONAL-FEES.md +2 -18
  25. package/skills/wix-app/references/service-plugin/BOOKINGS-STAFF-SORTING.md +2 -42
  26. package/skills/wix-app/references/service-plugin/DISCOUNT-TRIGGERS.md +1 -11
  27. package/skills/wix-app/references/service-plugin/GIFT-CARDS.md +1 -11
  28. package/skills/wix-app/references/service-plugin/PAYMENT-SETTINGS.md +2 -40
  29. package/skills/wix-app/references/service-plugin/SHIPPING-RATES.md +2 -19
  30. package/skills/wix-app/references/service-plugin/VALIDATIONS.md +2 -18
  31. package/skills/wix-app/references/site-plugin/SLOTS.md +17 -68
  32. package/skills/wix-headless/SKILL.md +112 -368
  33. package/skills/wix-headless/references/BUILD.md +307 -0
  34. package/skills/wix-headless/references/DESIGN_SYSTEM.md +115 -0
  35. package/skills/wix-headless/references/DISCOVERY.md +237 -112
  36. package/skills/wix-headless/references/PLAN.md +82 -0
  37. package/skills/wix-headless/references/SEED.md +205 -0
  38. package/skills/wix-headless/references/SETUP.md +250 -94
  39. package/skills/wix-headless/references/astro/COMPOSE.md +142 -0
  40. package/skills/wix-headless/references/{blog → astro/blog}/BLOG_PAGES.md +1 -1
  41. package/skills/wix-headless/references/{blog → astro/blog}/BLOG_SETUP.md +2 -2
  42. package/skills/wix-headless/references/astro/cms/CMS_FOUNDATIONS.md +214 -0
  43. package/skills/wix-headless/references/{cms → astro/cms}/FAQ_KNOWLEDGE_BASE.md +6 -4
  44. package/skills/wix-headless/references/{cms → astro/cms}/PORTFOLIO.md +5 -5
  45. package/skills/wix-headless/references/{cms → astro/cms}/RESOURCE_LIBRARY.md +5 -5
  46. package/skills/wix-headless/references/{cms → astro/cms}/TEAM_DIRECTORY.md +5 -5
  47. package/skills/wix-headless/references/astro/designer/INSTRUCTIONS.md +331 -0
  48. package/skills/wix-headless/references/{ecom → astro/ecom}/CART_PAGES.md +1 -1
  49. package/skills/wix-headless/references/{ecom → astro/ecom}/CART_WIRING.md +1 -1
  50. package/skills/wix-headless/references/{ecom → astro/ecom}/COMPONENTS_CSS.md +3 -3
  51. package/skills/wix-headless/references/{forms → astro/forms}/CONTACT_FORM.md +13 -13
  52. package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/COMPONENTS.md +1 -1
  53. package/skills/wix-headless/references/{gift-cards → astro/gift-cards}/PAGES.md +1 -1
  54. package/skills/wix-headless/references/{stores → astro/stores}/BACK_IN_STOCK.md +1 -1
  55. package/skills/wix-headless/references/{stores → astro/stores}/CATEGORY_PAGES.md +12 -13
  56. package/skills/wix-headless/references/{stores → astro/stores}/COMPONENTS_CSS.md +4 -4
  57. package/skills/wix-headless/references/{stores → astro/stores}/HOME_AND_NAV.md +11 -9
  58. package/skills/wix-headless/references/{stores → astro/stores}/PRODUCT_PAGES.md +5 -3
  59. package/skills/wix-headless/references/{stores → astro/stores}/SHARED_WIRING.md +3 -3
  60. package/skills/wix-headless/references/astro/templates/Footer.astro +27 -0
  61. package/skills/wix-headless/references/astro/templates/Layout.astro +132 -0
  62. package/skills/wix-headless/references/astro/templates/Navigation.astro +38 -0
  63. package/skills/wix-headless/references/astro/templates/astro.config.mjs +41 -0
  64. package/skills/wix-headless/references/astro/templates/ecom/components-ecom.css +302 -0
  65. package/skills/wix-headless/references/astro/templates/global.css +286 -0
  66. package/skills/wix-headless/references/astro/templates/index.astro +56 -0
  67. package/skills/wix-headless/{templates → references/astro/templates}/stores/back-in-stock.ts +1 -1
  68. package/skills/wix-headless/{templates → references/astro/templates}/stores/components-stores.css +138 -33
  69. package/skills/wix-headless/references/blog/BLOG_CONTENT.md +10 -11
  70. package/skills/wix-headless/references/blog/INSTRUCTIONS.md +5 -5
  71. package/skills/wix-headless/references/cms/CMS_FOUNDATIONS.md +31 -230
  72. package/skills/wix-headless/references/cms/INSTRUCTIONS.md +13 -8
  73. package/skills/wix-headless/references/custom/INSTRUCTIONS.md +38 -0
  74. package/skills/wix-headless/references/ecom/INSTRUCTIONS.md +10 -10
  75. package/skills/wix-headless/references/forms/INSTRUCTIONS.md +5 -5
  76. package/skills/wix-headless/references/gift-cards/INSTRUCTIONS.md +11 -11
  77. package/skills/wix-headless/references/images/INSTRUCTIONS.md +47 -50
  78. package/skills/wix-headless/references/seed-recipes.md +49 -0
  79. package/skills/wix-headless/references/shared/AUTHENTICATION.md +110 -0
  80. package/skills/wix-headless/references/shared/BUILD_NOISE.md +2 -2
  81. package/skills/wix-headless/references/shared/DOCS_SEARCH.md +41 -0
  82. package/skills/wix-headless/references/shared/IMAGE_GENERATION.md +17 -17
  83. package/skills/wix-headless/references/shared/IMPLEMENTER.md +20 -29
  84. package/skills/wix-headless/references/shared/PRODUCTION_SHARP_EDGES.md +1 -1
  85. package/skills/wix-headless/references/shared/RETURN_CONTRACT.md +25 -16
  86. package/skills/wix-headless/references/shared/STYLING.md +47 -10
  87. package/skills/wix-headless/references/stores/INSTRUCTIONS.md +25 -25
  88. package/skills/wix-headless/references/stores/PRODUCT_CATALOG_DATA.md +17 -17
  89. package/skills/wix-headless/references/verticals/_schema.json +31 -117
  90. package/skills/wix-headless/references/verticals/_schema.md +60 -150
  91. package/skills/wix-headless/references/verticals/blog.md +5 -99
  92. package/skills/wix-headless/references/verticals/cms.md +9 -100
  93. package/skills/wix-headless/references/verticals/ecom.md +5 -76
  94. package/skills/wix-headless/references/verticals/forms.md +5 -85
  95. package/skills/wix-headless/references/verticals/gift-cards.md +6 -103
  96. package/skills/wix-headless/references/verticals/stores.md +10 -144
  97. package/skills/wix-headless/scripts/check-manifest.mjs +5 -5
  98. package/skills/wix-headless/scripts/emit-design-tokens.mjs +29 -23
  99. package/skills/wix-headless/scripts/init-site-json.mjs +59 -21
  100. package/skills/wix-headless/scripts/patch-decorative-slots.mjs +67 -43
  101. package/skills/wix-headless/scripts/plan-entity-image-waves.mjs +3 -3
  102. package/skills/wix-headless/scripts/preview.sh +3 -3
  103. package/skills/wix-headless/scripts/release.sh +4 -4
  104. package/skills/wix-headless/scripts/scaffold.sh +75 -20
  105. package/skills/wix-headless/scripts/seed-utilities.sh +50 -12
  106. package/skills/wix-manage/SKILL.md +4 -8
  107. package/skills/wix-manage/references/blog/how-to-create-blog-posts.md +70 -1
  108. package/skills/wix-manage/references/cms/cms-data-items-crud.md +4 -0
  109. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +2 -0
  110. package/skills/wix-manage/references/cms/cms-schema-management.md +4 -0
  111. package/skills/wix-manage/references/ecommerce/api-shipping.md +443 -0
  112. package/skills/wix-manage/references/ecommerce/flow-add-free-shipping.md +5 -6
  113. package/skills/wix-manage/references/ecommerce/flow-fix-coverage-gaps.md +4 -5
  114. package/skills/wix-manage/references/ecommerce/flow-optimize-shipping-rates.md +4 -5
  115. package/skills/wix-manage/references/ecommerce/goal-increase-aov.md +1 -1
  116. package/skills/wix-manage/references/ecommerce/goal-reduce-cart-abandonment.md +1 -2
  117. package/skills/wix-manage/references/ecommerce/guardrail-shipping-health.md +2 -2
  118. package/skills/wix-manage/references/ecommerce/recipe-apply-shipping-recommendations.md +4 -5
  119. package/skills/wix-manage/references/ecommerce/recommend-ecommerce-strategy.md +7 -7
  120. package/skills/wix-manage/references/ecommerce/setup-discount-rules.md +209 -72
  121. package/skills/wix-manage/references/ecommerce/setup-store-pickup-location.md +9 -10
  122. package/skills/wix-manage/references/ecommerce/skill-graph.md +4 -4
  123. package/skills/wix-manage/references/forms/create-form.md +2 -0
  124. package/skills/wix-manage/references/stores/bulk-create-products-with-options.md +2 -0
  125. package/skills/wix-manage/references/stores/create-product-from-image.md +91 -38
  126. package/skills/wix-manage/references/stores/setup-online-store-catalog-v3.md +14 -7
  127. package/skills/wix-app/references/data-collection/extension-template.ts +0 -107
  128. package/skills/wix-headless/references/ORCHESTRATION.md +0 -269
  129. package/skills/wix-headless/references/commands/install-app.md +0 -42
  130. package/skills/wix-headless/references/commands/mcp-bootstrap.md +0 -28
  131. package/skills/wix-headless/references/designer/INSTRUCTIONS.md +0 -828
  132. package/skills/wix-headless/references/shared/MCP_PREFIX.md +0 -105
  133. package/skills/wix-headless/scripts/copy-utility-templates.mjs +0 -153
  134. package/skills/wix-headless/templates/ecom/components-ecom.css +0 -215
  135. package/skills/wix-manage/references/events/list-events.md +0 -203
  136. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartBadge.tsx +0 -0
  137. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/CartView.tsx +0 -0
  138. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/cart.astro +0 -0
  139. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/discounts.ts +0 -0
  140. /package/skills/wix-headless/{templates → references/astro/templates}/ecom/thank-you.astro +0 -0
  141. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/GiftCardPurchase.tsx +0 -0
  142. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_home-teaser-snippet.astro +0 -0
  143. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/_nav-snippet.astro +0 -0
  144. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/components-gift-cards.css +0 -0
  145. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.astro +0 -0
  146. /package/skills/wix-headless/{templates → references/astro/templates}/gift-cards/gift-cards.ts +0 -0
  147. /package/skills/wix-headless/{templates → references/astro/templates}/stores/AddToCartButton.tsx +0 -0
  148. /package/skills/wix-headless/{templates → references/astro/templates}/stores/BackInStockForm.tsx +0 -0
  149. /package/skills/wix-headless/{templates → references/astro/templates}/stores/CategoryRail.astro +0 -0
  150. /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductCard.astro +0 -0
  151. /package/skills/wix-headless/{templates → references/astro/templates}/stores/ProductPurchase.tsx +0 -0
  152. /package/skills/wix-headless/{templates → references/astro/templates}/stores/SeoTags.astro +0 -0
  153. /package/skills/wix-headless/{templates → references/astro/templates}/stores/categories.ts +0 -0
  154. /package/skills/wix-headless/{templates → references/astro/templates}/stores/category/[slug].astro +0 -0
  155. /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/[slug].astro +0 -0
  156. /package/skills/wix-headless/{templates → references/astro/templates}/stores/products/index.astro +0 -0
@@ -3,7 +3,7 @@
3
3
  //
4
4
  // Verifies that every file declared in each loaded pack's `creates:` array
5
5
  // for the named phase exists on disk. If a file is missing, attempts to
6
- // recover by copying the canonical template from <SKILL_ROOT>/templates/<pack>/.
6
+ // recover by copying the canonical template from <SKILL_ROOT>/references/astro/templates/<pack>/.
7
7
  // Outputs a JSON summary of present / recovered / errored files.
8
8
  //
9
9
  // Usage (both modes work):
@@ -26,7 +26,7 @@
26
26
  // `creates:` array.
27
27
  // - For each `creates:` entry where phase matches:
28
28
  // * If file exists in the project → "present"
29
- // * If missing AND a template exists at `templates/<pack>/<tail>`
29
+ // * If missing AND a template exists at `references/astro/templates/<pack>/<tail>`
30
30
  // (where <tail> is the path under src/pages/ for page files, or the
31
31
  // basename for everything else) → copy/fetch it; record as "recovered".
32
32
  // * Otherwise → record as "missing" with a remediation hint.
@@ -101,12 +101,12 @@ if (phase !== "components" && phase !== "pages") {
101
101
  const packs = packsCsv.split(",").map((p) => p.trim()).filter(Boolean);
102
102
 
103
103
  // Map a `creates:` file path to its template path (relative to skill root).
104
- // Heuristic: `src/pages/<X>` preserves <X> under templates/<pack>/; everything
105
- // else uses basename only.
104
+ // Heuristic: `src/pages/<X>` preserves <X> under references/astro/templates/<pack>/;
105
+ // everything else uses basename only.
106
106
  function templateRelPath(packName, srcPath) {
107
107
  const pagesMatch = srcPath.match(/^src\/pages\/(.+)$/);
108
108
  const tail = pagesMatch ? pagesMatch[1] : basename(srcPath);
109
- return `templates/${packName}/${tail}`;
109
+ return `references/astro/templates/${packName}/${tail}`;
110
110
  }
111
111
 
112
112
  // Parse `creates:` block from a vertical pack's markdown frontmatter.
@@ -1,45 +1,58 @@
1
1
  #!/usr/bin/env node
2
- // Generate .wix/design-tokens.css and .wix/site.d.ts deterministically from
3
- // .wix/site.json.designTokens. Invoked by the wix-headless skill orchestrator
4
- // after the Phase 2 Design System agent's return is merged into site.json.
2
+ // Generate .wix/design-tokens.css and .wix/site.d.ts deterministically from a
3
+ // designTokens JSON payload piped in on stdin. Invoked by the wix-headless
4
+ // skill orchestrator immediately after the Phase 2 Design System agent
5
+ // returns — the orchestrator pipes designerReturn.data.designTokens directly,
6
+ // no site.json round-trip.
5
7
  //
6
8
  // Why a script (not agent work): both files are pure projections of the
7
9
  // designTokens JSON — no judgment, no brand-context dependence. An LLM
8
10
  // emitting them is wasted tokens AND a drift risk (malformed :root blocks,
9
11
  // missing token groups, divergent .d.ts skeletons).
12
+ //
13
+ // Usage:
14
+ // echo '<designTokens-json>' | node emit-design-tokens.mjs <project-dir>
10
15
 
11
16
  import { readFileSync, writeFileSync, mkdirSync } from "node:fs";
12
17
  import { join } from "node:path";
13
18
 
14
19
  const projectDir = process.argv[2] ?? process.cwd();
15
- const sitePath = join(projectDir, ".wix/site.json");
16
20
 
17
- let site;
21
+ if (process.stdin.isTTY) {
22
+ console.error("emit-design-tokens: expected designTokens JSON on stdin (pipe in the designer's data.designTokens block)");
23
+ process.exit(2);
24
+ }
25
+
26
+ let tokens;
18
27
  try {
19
- site = JSON.parse(readFileSync(sitePath, "utf8"));
28
+ const raw = readFileSync(0, "utf8").trim();
29
+ if (!raw) {
30
+ console.error("emit-design-tokens: stdin was empty — pass the designTokens JSON object");
31
+ process.exit(2);
32
+ }
33
+ tokens = JSON.parse(raw);
20
34
  } catch (e) {
21
- console.error(`emit-design-tokens: cannot read ${sitePath} (${e.message}). Has Phase 2 Design System returned and merged?`);
22
- process.exit(1);
35
+ console.error(`emit-design-tokens: stdin is not valid JSON (${e.message})`);
36
+ process.exit(2);
23
37
  }
24
38
 
25
- const tokens = site.designTokens ?? {};
26
- if (!tokens || Object.keys(tokens).length === 0) {
27
- console.error("emit-design-tokens: site.json.designTokens is empty — designer return not merged.");
28
- process.exit(1);
39
+ if (!tokens || typeof tokens !== "object" || Array.isArray(tokens) || Object.keys(tokens).length === 0) {
40
+ console.error("emit-design-tokens: designTokens payload must be a non-empty JSON object");
41
+ process.exit(2);
29
42
  }
30
43
 
31
44
  const wixDir = join(projectDir, ".wix");
32
45
  mkdirSync(wixDir, { recursive: true });
33
46
 
34
- // .wix/design-tokens.css ----------------------------------------------------
35
47
  const PREFIX = {
36
48
  colors: "--color-",
37
49
  fonts: "--font-",
38
50
  radii: "--radius-",
39
51
  spacing: "--spacing-",
52
+ containers: "--container-",
40
53
  };
41
54
 
42
- const cssLines = ["/* Generated from .wix/site.json.designTokens. Do not edit. */", ":root {"];
55
+ const cssLines = ["/* Generated by emit-design-tokens.mjs. Do not edit. */", ":root {"];
43
56
  for (const [group, prefix] of Object.entries(PREFIX)) {
44
57
  const entries = tokens[group] ?? {};
45
58
  for (const [key, value] of Object.entries(entries)) {
@@ -49,7 +62,6 @@ for (const [group, prefix] of Object.entries(PREFIX)) {
49
62
  cssLines.push("}", "");
50
63
  writeFileSync(join(wixDir, "design-tokens.css"), cssLines.join("\n"));
51
64
 
52
- // .wix/site.d.ts ------------------------------------------------------------
53
65
  const typeFor = (group) => {
54
66
  const keys = Object.keys(tokens[group] ?? {});
55
67
  if (keys.length === 0) return "Record<string, string>";
@@ -57,19 +69,13 @@ const typeFor = (group) => {
57
69
  return `Record<${literal}, string>`;
58
70
  };
59
71
 
60
- const dts = `// Generated from .wix/site.json.designTokens. Do not edit.
61
- export type Brand = { name: string; description: string };
72
+ const dts = `// Generated by emit-design-tokens.mjs. Do not edit.
62
73
  export type DesignTokens = {
63
74
  colors: ${typeFor("colors")};
64
75
  fonts: ${typeFor("fonts")};
65
76
  radii: ${typeFor("radii")};
66
77
  spacing: ${typeFor("spacing")};
67
- };
68
- export type Product = { id: string; name: string; slug: string; price: number; variantId: string };
69
- export declare const site: {
70
- brand: Brand;
71
- designTokens: DesignTokens;
72
- seeded: { products?: Product[]; posts?: unknown[]; collections?: Record<string, unknown[]> };
78
+ containers: ${typeFor("containers")};
73
79
  };
74
80
  `;
75
81
  writeFileSync(join(wixDir, "site.d.ts"), dts);
@@ -1,30 +1,70 @@
1
1
  #!/usr/bin/env node
2
- // Write the initial .wix/site.json shape during Setup. Site.json is the
3
- // single source of truth that every downstream phase reads from / appends
4
- // to. This script writes the canonical initial structure deterministically
5
- // so the orchestrator never has to compose the shape inline (which used
6
- // to be LLM-emitted JSON, with the obvious drift risks).
2
+ // Write the initial .wix/site.json — slim metadata snapshot for resume
3
+ // detection + run.json composition. Subagents do NOT read this file during
4
+ // the run; the orchestrator is the sole reader/writer and inlines anything
5
+ // downstream phases need into their prompts.
7
6
  //
8
- // Usage:
9
- // node init-site-json.mjs <project-dir> <brand-name> <brand-description> <verticals-csv>
7
+ // Final shape:
8
+ // {
9
+ // "brand": { "name": "...", "description": "..." },
10
+ // "frontend": "astro" | "custom",
11
+ // "verticals": ["stores", "cms", ...],
12
+ // "siteId": "..." (optional — Setup Step 1 may patch later),
13
+ // "appId": "..." (optional — Setup Step 1 may patch later)
14
+ // }
10
15
  //
11
- // Example:
12
- // node init-site-json.mjs /tmp/proj "Acme Coffee" "Warm cream-and-forest editorial" "stores,ecom,cms,gift-cards"
16
+ // Usage:
17
+ // node init-site-json.mjs <project-dir> <brand-name> <brand-description> \
18
+ // <verticals-csv> --frontend <astro|custom> \
19
+ // [--site-id <id>] [--app-id <id>]
20
+ // (In practice only "astro" reaches this step — custom frontends route to the
21
+ // not-available stub before Discovery's approval/site.json write.)
13
22
  //
14
23
  // Behavior:
15
24
  // - Refuses to overwrite an existing .wix/site.json (exit 2). The orchestrator
16
- // should call this exactly once during Setup. To re-init for testing, the
17
- // caller can rm the file first.
25
+ // should call this exactly once during Discovery's "After Approval" step.
26
+ // To re-init for testing, the caller can rm the file first.
18
27
  // - Creates .wix/ if it doesn't exist.
19
28
  // - Outputs a JSON summary to stdout.
20
29
 
21
30
  import { writeFileSync, existsSync, mkdirSync } from "node:fs";
22
31
  import { join } from "node:path";
23
32
 
24
- const [, , projectDir, brandName, brandDescription, verticalsCsv] = process.argv;
33
+ const ALLOWED_FRONTENDS = new Set(["astro", "custom"]);
34
+
35
+ let frontend = "astro";
36
+ let siteId;
37
+ let appId;
38
+ const positional = [];
39
+ const rawArgs = process.argv.slice(2);
40
+ for (let i = 0; i < rawArgs.length; i++) {
41
+ const arg = rawArgs[i];
42
+ if (arg === "--frontend") {
43
+ frontend = rawArgs[++i];
44
+ } else if (arg.startsWith("--frontend=")) {
45
+ frontend = arg.slice("--frontend=".length);
46
+ } else if (arg === "--site-id") {
47
+ siteId = rawArgs[++i];
48
+ } else if (arg.startsWith("--site-id=")) {
49
+ siteId = arg.slice("--site-id=".length);
50
+ } else if (arg === "--app-id") {
51
+ appId = rawArgs[++i];
52
+ } else if (arg.startsWith("--app-id=")) {
53
+ appId = arg.slice("--app-id=".length);
54
+ } else {
55
+ positional.push(arg);
56
+ }
57
+ }
58
+
59
+ if (!ALLOWED_FRONTENDS.has(frontend)) {
60
+ console.error(`init-site-json: --frontend must be one of ${[...ALLOWED_FRONTENDS].join(", ")}; got ${JSON.stringify(frontend)}`);
61
+ process.exit(2);
62
+ }
63
+
64
+ const [projectDir, brandName, brandDescription, verticalsCsv] = positional;
25
65
 
26
66
  if (!projectDir || !brandName || brandDescription === undefined || !verticalsCsv) {
27
- console.error("usage: init-site-json.mjs <project-dir> <brand-name> <brand-description> <verticals-csv>");
67
+ console.error("usage: init-site-json.mjs <project-dir> <brand-name> <brand-description> <verticals-csv> --frontend <astro|custom> [--site-id <id>] [--app-id <id>]");
28
68
  process.exit(2);
29
69
  }
30
70
 
@@ -42,22 +82,20 @@ const wixDir = join(projectDir, ".wix");
42
82
  const sitePath = join(wixDir, "site.json");
43
83
 
44
84
  if (existsSync(sitePath)) {
45
- console.error(`init-site-json: ${sitePath} already exists — refusing to overwrite. Setup should call this exactly once.`);
85
+ console.error(`init-site-json: ${sitePath} already exists — refusing to overwrite. Discovery should call this exactly once.`);
46
86
  process.exit(2);
47
87
  }
48
88
 
49
89
  mkdirSync(wixDir, { recursive: true });
50
90
 
51
91
  const site = {
52
- brand: {
53
- name: brandName,
54
- description: brandDescription,
55
- },
56
- seeded: {},
57
- designTokens: {},
92
+ brand: { name: brandName, description: brandDescription },
93
+ frontend,
58
94
  verticals,
59
95
  };
96
+ if (siteId) site.siteId = siteId;
97
+ if (appId) site.appId = appId;
60
98
 
61
99
  writeFileSync(sitePath, JSON.stringify(site, null, 2) + "\n");
62
100
 
63
- console.log(JSON.stringify({ status: "ok", path: sitePath, verticals }, null, 2));
101
+ console.log(JSON.stringify({ status: "ok", path: sitePath, frontend, verticals, ...(siteId ? { siteId } : {}), ...(appId ? { appId } : {}) }, null, 2));
@@ -1,59 +1,88 @@
1
1
  #!/usr/bin/env node
2
- // Inject Image-Phase-1 decorative URLs into the designer-emitted page shells.
2
+ // Inject Image-Phase-1 decorative URLs into the page shells.
3
3
  //
4
- // The Phase 2 designer writes `<div data-decorative-slot="<key>">…</div>`
5
- // placeholders in src/pages/*.astro. Image Phase 1 writes resolved URLs to
6
- // .wix/image-urls.md. This script reads both, then injects an <img> element
7
- // as the first child of each matching slot div — pure string substitution,
8
- // no LLM, no file rewrites.
4
+ // The design-system Composer (index.astro) and the Phase 4 page designers write
5
+ // `<div data-decorative-slot="<key>">…</div>` placeholders in src/pages/*.astro. Image Phase 1 returns a slot→URL map
6
+ // in its JSON return; the orchestrator pipes that map (as JSON) into this
7
+ // script's stdin. This script reads the map + walks src/pages/*.astro,
8
+ // then injects an <img> as the first child of each matching slot div —
9
+ // pure string substitution, no LLM, no file rewrites.
9
10
  //
10
11
  // Usage:
11
- // node patch-decorative-slots.mjs <project-dir>
12
+ // echo '{"hero":"https://...","about":"https://..."}' \
13
+ // | node patch-decorative-slots.mjs <scaffold-dir>
14
+ //
15
+ // <scaffold-dir> is the dir that contains src/pages/. The orchestrator
16
+ // passes the scaffold subdir explicitly — no eval-dir vs scaffold-dir
17
+ // auto-detection is needed for the URL map (it's on stdin), but src/pages
18
+ // is still resolved per-input since the orchestrator may call this from
19
+ // either the scaffold subdir or the eval root.
12
20
  //
13
21
  // Behavior:
14
- // - If .wix/image-urls.md doesn't exist, exit 0 silently (Image Phase 1
15
- // timed out / failed; the slot placeholders look complete on their own).
22
+ // - Empty stdin or no slot→URL pairs → exit 0 status="skipped" (themed-
23
+ // blocks mode or Image Phase 1 had no URLs to publish).
24
+ // - src/pages missing at <scaffold-dir> and in any single subdir one
25
+ // level down → exit 2 status="error".
16
26
  // - For each `data-decorative-slot="<key>"` in src/pages/*.astro:
17
- // * If <key> has no URL in image-urls.md → leave the div alone.
18
- // * If the div is self-closing or already has a child element other
19
- // than whitespace/comments → skip with a warning (don't clobber).
27
+ // * Key has no URL in the input map → leave the div alone.
28
+ // * Div already has a non-comment child → skip with a warning.
20
29
  // * Otherwise inject <img …> as the first child.
21
- // - Output a JSON summary of patches/skips/warnings to stdout.
22
- // - Exit 0 even when some slots couldn't be patched — placeholders fall
23
- // back to the designer's aspect-ratio + background-color rendering.
30
+ // - Output JSON summary of patches/skips/warnings to stdout.
24
31
 
25
32
  import { readFileSync, writeFileSync, existsSync, readdirSync, statSync } from "node:fs";
26
33
  import { join } from "node:path";
27
34
 
28
35
  const projectDir = process.argv[2] ?? process.cwd();
29
36
 
30
- const imageUrlsPath = join(projectDir, ".wix/image-urls.md");
31
- if (!existsSync(imageUrlsPath)) {
32
- console.log(JSON.stringify({ status: "skipped", reason: "no .wix/image-urls.md (Image Phase 1 didn't write — failed or timed out)" }));
37
+ let urlMap = {};
38
+ if (!process.stdin.isTTY) {
39
+ const raw = readFileSync(0, "utf8").trim();
40
+ if (raw) {
41
+ try {
42
+ urlMap = JSON.parse(raw);
43
+ } catch (e) {
44
+ console.error(JSON.stringify({ status: "error", reason: `stdin is not valid JSON (${e.message})` }));
45
+ process.exit(2);
46
+ }
47
+ if (!urlMap || typeof urlMap !== "object" || Array.isArray(urlMap)) {
48
+ console.error(JSON.stringify({ status: "error", reason: "expected a JSON object mapping slot keys to URLs" }));
49
+ process.exit(2);
50
+ }
51
+ }
52
+ }
53
+
54
+ if (Object.keys(urlMap).length === 0) {
55
+ console.log(JSON.stringify({ status: "skipped", reason: "no slot→URL pairs on stdin (themed-blocks mode or Image Phase 1 produced none)" }));
33
56
  process.exit(0);
34
57
  }
35
58
 
36
- // Parse `## <key>\n- url: <url>` blocks from .wix/image-urls.md.
37
- const imageUrlsRaw = readFileSync(imageUrlsPath, "utf8");
38
- const urlMap = {};
39
- // Match `## key\n…- url: <url>` per section. The inner `(?:(?!^##\s)[\s\S])*?`
40
- // forbids the lazy span from crossing into the next `## ` line — without
41
- // that guard, an empty `- url:` for one slot silently steals the next
42
- // section's URL on partial-failure runs.
43
- const sectionRegex = /^##\s+(\S+)\s*\n(?:(?!^##\s)[\s\S])*?-\s*url:\s*(https?:\/\/\S+)/gm;
44
- let m;
45
- while ((m = sectionRegex.exec(imageUrlsRaw)) !== null) {
46
- urlMap[m[1]] = m[2];
59
+ function findPagesDir() {
60
+ const direct = join(projectDir, "src/pages");
61
+ if (existsSync(direct)) return { path: direct, source: "projectDir" };
62
+ const hits = [];
63
+ for (const entry of readdirSync(projectDir, { withFileTypes: true })) {
64
+ if (!entry.isDirectory() || entry.name.startsWith(".")) continue;
65
+ const candidate = join(projectDir, entry.name, "src/pages");
66
+ if (existsSync(candidate)) hits.push(candidate);
67
+ }
68
+ if (hits.length === 1) return { path: hits[0], source: "scaffold-subdir" };
69
+ if (hits.length > 1) return { path: null, source: "ambiguous", hits };
70
+ return { path: null, source: "missing" };
47
71
  }
48
72
 
49
- if (Object.keys(urlMap).length === 0) {
50
- console.log(JSON.stringify({ status: "skipped", reason: ".wix/image-urls.md exists but has no parseable `## key` + `- url:` blocks" }));
51
- process.exit(0);
73
+ const pagesResolved = findPagesDir();
74
+ if (!pagesResolved.path) {
75
+ console.error(JSON.stringify({
76
+ status: "error",
77
+ reason: "src/pages not found at <projectDir>/src/pages or in any scaffold subdir; pass the scaffold dir explicitly",
78
+ projectDir,
79
+ pagesSearchedAt: pagesResolved.source === "ambiguous" ? pagesResolved.hits : [join(projectDir, "src/pages")],
80
+ }, null, 2));
81
+ process.exit(2);
52
82
  }
53
83
 
54
- // Find candidate page files: src/pages/*.astro, recursively (designer may emit
55
- // /about.astro at top level OR /pages/about.astro).
56
- const pagesDir = join(projectDir, "src/pages");
84
+ const pagesDir = pagesResolved.path;
85
+
57
86
  const candidates = [];
58
87
  const walk = (dir) => {
59
88
  if (!existsSync(dir)) return;
@@ -80,22 +109,17 @@ for (const file of candidates) {
80
109
  modified = modified.replace(SLOT_DIV_REGEX, (match, openTag, slotKey, inner, closeTag) => {
81
110
  const url = urlMap[slotKey];
82
111
  if (!url) {
83
- skipped.push({ file, slot: slotKey, reason: "no URL for this slot in image-urls.md" });
112
+ skipped.push({ file, slot: slotKey, reason: "no URL for this slot in input map" });
84
113
  return match;
85
114
  }
86
115
 
87
- // Strip HTML comments before any content checks — the designer's placeholder
88
- // is a comment like `<!-- orchestrator injects <img> here -->`, which would
89
- // otherwise false-positive both the idempotency and clobber-guard checks.
90
116
  const stripped = inner.replace(/<!--[\s\S]*?-->/g, "").trim();
91
117
 
92
- // Skip if div already contains an <img> element (idempotency).
93
118
  if (/<img\b/i.test(stripped)) {
94
119
  skipped.push({ file, slot: slotKey, reason: "div already contains an <img> tag (idempotent skip)" });
95
120
  return match;
96
121
  }
97
122
 
98
- // Skip if div has substantive child content beyond whitespace/comments.
99
123
  if (stripped.length > 0) {
100
124
  warnings.push({ file, slot: slotKey, reason: "div has existing non-image child content; not patching to avoid clobber" });
101
125
  return match;
@@ -104,7 +128,6 @@ for (const file of candidates) {
104
128
  fileChanged = true;
105
129
  const img = `<img src="${url}" alt="" loading="lazy" decoding="async" class="decorative-slot-img" />`;
106
130
  patched.push({ file, slot: slotKey });
107
- // Preserve any whitespace inside the div (e.g., the newline + indent).
108
131
  return `${openTag}\n ${img}${inner}${closeTag}`;
109
132
  });
110
133
 
@@ -115,10 +138,11 @@ for (const file of candidates) {
115
138
 
116
139
  const summary = {
117
140
  status: warnings.length > 0 ? "partial" : "ok",
118
- imageUrls: Object.keys(urlMap),
141
+ slots: Object.keys(urlMap),
119
142
  filesScanned: candidates.length,
120
143
  patched,
121
144
  skipped,
122
145
  warnings,
146
+ pagesFrom: pagesResolved.source,
123
147
  };
124
148
  console.log(JSON.stringify(summary, null, 2));
@@ -9,8 +9,8 @@
9
9
  //
10
10
  // waves — Runware generation requests, grouped into waves of <waveSize>
11
11
  // (default 3) to match the google:4@2 throttle. Each wave is
12
- // one orchestration step with N parallel CallWixSiteAPI tool
13
- // calls; each tool call sends one task in the body array.
12
+ // one orchestration step with N parallel curls in one shell,
13
+ // backgrounded with `&`; each curl sends one task in the body array.
14
14
  //
15
15
  // imports — POST /site-media/v1/files/import requests, one per generated
16
16
  // image. Indexed by taskUUID so the orchestrator threads the
@@ -155,7 +155,7 @@ for (const p of prompts) {
155
155
  process.exit(1);
156
156
  }
157
157
  // Read-merge-PUT: echo every existing data field, set the image field
158
- // to the placeholder URL. Wipes nothing (the bug observed in 2026-04-19).
158
+ // to the placeholder URL. Echoing every existing field is what prevents wiping the rest of the record.
159
159
  const mergedData = { ...detail.data, [detail.imageField]: "__IMAGE_URL__" };
160
160
  patches.push({
161
161
  taskUUID: p.taskUUID,
@@ -6,7 +6,7 @@
6
6
  #
7
7
  # Run from the project directory (CWD = <project>/). Requires wix.config.json.
8
8
  #
9
- # Output: stdout is the preview URL that `npx @wix/cli preview` printed; the
9
+ # Output: stdout is the preview URL that `npx @wix/cli@latest preview` printed; the
10
10
  # orchestrator captures it and records as outcome.previewUrl in run.json. All
11
11
  # other CLI output goes to stderr.
12
12
  #
@@ -23,14 +23,14 @@
23
23
  set -euo pipefail
24
24
 
25
25
  # Build first; surface compile errors verbatim if it fails.
26
- npx @wix/cli build 1>&2
26
+ npx @wix/cli@latest build 1>&2
27
27
 
28
28
  # Preview captures the deployed URL on stdout. We tee its output to stderr for
29
29
  # the user-visible log AND parse the URL out for the orchestrator's stdout.
30
30
  PREVIEW_OUTPUT="$(mktemp)"
31
31
  trap 'rm -f "$PREVIEW_OUTPUT"' EXIT
32
32
 
33
- npx @wix/cli preview 2>&1 | tee "$PREVIEW_OUTPUT" 1>&2
33
+ npx @wix/cli@latest preview 2>&1 | tee "$PREVIEW_OUTPUT" 1>&2
34
34
 
35
35
  # Wix CLI prints `Site preview URL: <url>` (or similar) — extract the first
36
36
  # https URL from the output. If multiple are printed, the first is the canonical
@@ -9,7 +9,7 @@
9
9
  #
10
10
  # Run from the project directory (CWD = <project>/). Requires wix.config.json.
11
11
  #
12
- # Output: stdout is the released URL that `npx @wix/cli release` printed
12
+ # Output: stdout is the released URL that `npx @wix/cli@latest release` printed
13
13
  # (extracted from the line `Site published on <url>`); the orchestrator
14
14
  # captures it as outcome.releaseUrl in run.json. All other CLI output goes
15
15
  # to stderr.
@@ -19,12 +19,12 @@
19
19
  # <other> — build or release failed; stderr surfaces the underlying error.
20
20
  # Build failures are code bugs (TypeScript / Astro / missing
21
21
  # module) — the orchestrator does NOT retry. Release auth failures
22
- # surface as `Run npx @wix/cli login and retry.`
22
+ # surface as `Run npx @wix/cli@latest login and retry.`
23
23
  # Known transient release failures are retried by this script.
24
24
 
25
25
  set -euo pipefail
26
26
 
27
- npx @wix/cli build 1>&2
27
+ npx @wix/cli@latest build 1>&2
28
28
 
29
29
  RELEASE_OUTPUT="$(mktemp)"
30
30
  trap 'rm -f "$RELEASE_OUTPUT"' EXIT
@@ -39,7 +39,7 @@ RELEASE_STATUS=1
39
39
  for ((attempt = 1; attempt <= MAX_RELEASE_ATTEMPTS; attempt++)); do
40
40
  : >"$RELEASE_OUTPUT"
41
41
  set +e
42
- npx @wix/cli release 2>&1 | tee "$RELEASE_OUTPUT" 1>&2
42
+ npx @wix/cli@latest release 2>&1 | tee "$RELEASE_OUTPUT" 1>&2
43
43
  RELEASE_STATUS=${PIPESTATUS[0]}
44
44
  set -e
45
45
 
@@ -1,53 +1,108 @@
1
1
  #!/usr/bin/env bash
2
- # Scaffold a new Wix Managed Headless project with the pure-headless blank template.
2
+ # Scaffold a new Wix Managed Headless project using the CLI's preset blank template.
3
3
  #
4
4
  # Usage:
5
- # bash <SKILL_ROOT>/scripts/scaffold.sh <project-slug> "<Brand Name>"
5
+ # bash <SKILL_ROOT>/scripts/scaffold.sh <project-slug> "<Brand Name>" [--frontend astro|custom]
6
6
  #
7
7
  # <project-slug>: 3-20 lowercase alphanumeric chars (no hyphens, no spaces) — the
8
8
  # Wix CLI rejects anything else. Becomes the project directory name.
9
9
  # <Brand Name>: human-readable business name; quote if it contains spaces.
10
+ # --frontend: the frontend axis. Defaults to "astro" — the only supported (and
11
+ # only scaffolded) frontend. "custom" (any non-astro frontend) is
12
+ # NOT scaffolded yet: Discovery routes custom to the not-available
13
+ # stub (references/custom/INSTRUCTIONS.md) before scaffold ever runs,
14
+ # so this script should only ever be invoked with "astro". If it is
15
+ # invoked with "custom" it exits 4 (recognized, not staged).
10
16
  #
11
17
  # After scaffold succeeds, read <project-slug>/wix.config.json to extract appId
12
- # (project's appId) and siteId (used for MCP calls). The orchestrator does that
13
- # read; this script just runs the npm create.
18
+ # (project's appId) and siteId (used as --site for `wix token` and in REST call
19
+ # bodies). The orchestrator does that read; this script just runs the npm create.
14
20
  #
15
21
  # Behavior:
16
22
  # - Pre-flight validates the slug (regex ^[a-z0-9]{3,20}$).
17
- # - Pre-flight requires both args.
23
+ # - Pre-flight requires both positional args.
18
24
  # - Runs `npm create @wix/new@latest headless` with --no-publish + --skip-install
19
25
  # so the orchestrator can deferred-install with its own package set.
20
- # - Template ID is the pure-headless blank (vibe-compatible templates trigger
21
- # an interactive prompt that blocks non-TTY runs).
26
+ # - Passes bare `--site-template` so non-interactive scaffolding stays on the
27
+ # blank starter. Without it, @wix/create-new (>=0.0.72) prompts for a template
28
+ # choice and aborts in a non-TTY agent shell ("not supported in non-interactive
29
+ # terminals").
22
30
  #
23
31
  # Exit codes:
24
32
  # 0 — ok
25
- # 2 — argument validation failed
33
+ # 2 — argument validation failed (bad slug, missing args, unknown --frontend value)
34
+ # 3 — Wix CLI not logged in (defensive; the Discovery pre-flight is the primary check)
35
+ # 4 — frontend value recognized but not scaffolded yet (custom today)
26
36
  # <other> — npm create failed; stderr surfaced to caller for orchestrator-side
27
- # recovery (auth / invalid-template ladder lives in the orchestrator).
37
+ # recovery (auth / other scaffold failures live in the orchestrator).
28
38
 
29
39
  set -euo pipefail
30
40
 
31
- if [[ $# -lt 2 || -z "${1:-}" || -z "${2:-}" ]]; then
32
- echo "scaffold.sh: both args required. Got project-slug='${1:-}' brand-name='${2:-}'." >&2
33
- echo "Usage: bash scaffold.sh <slug> \"<Brand Name>\" — slug first, brand quoted." >&2
41
+ FRONTEND="astro"
42
+ POSITIONAL=()
43
+ while [[ $# -gt 0 ]]; do
44
+ case "$1" in
45
+ --frontend)
46
+ if [[ $# -lt 2 ]]; then
47
+ echo "scaffold.sh: --frontend requires a value (astro|custom)." >&2
48
+ exit 2
49
+ fi
50
+ FRONTEND="$2"
51
+ shift 2
52
+ ;;
53
+ --frontend=*)
54
+ FRONTEND="${1#--frontend=}"
55
+ shift
56
+ ;;
57
+ *)
58
+ POSITIONAL+=("$1")
59
+ shift
60
+ ;;
61
+ esac
62
+ done
63
+
64
+ if [[ ${#POSITIONAL[@]} -lt 2 || -z "${POSITIONAL[0]:-}" || -z "${POSITIONAL[1]:-}" ]]; then
65
+ echo "scaffold.sh: both positional args required. Got project-slug='${POSITIONAL[0]:-}' brand-name='${POSITIONAL[1]:-}'." >&2
66
+ echo "Usage: bash scaffold.sh <slug> \"<Brand Name>\" [--frontend astro|custom] — slug first, brand quoted." >&2
34
67
  exit 2
35
68
  fi
36
69
 
37
- if [[ ! "$1" =~ ^[a-z0-9]{3,20}$ ]]; then
38
- echo "scaffold.sh: project-slug='$1' is not valid." >&2
70
+ case "$FRONTEND" in
71
+ astro)
72
+ ;;
73
+ custom)
74
+ echo "scaffold.sh: --frontend=custom is not scaffolded yet — astro is the only supported frontend." >&2
75
+ echo "Discovery routes custom (non-astro) frontends to references/custom/INSTRUCTIONS.md (the not-available stub) before scaffold; this script should not be invoked with 'custom'." >&2
76
+ exit 4
77
+ ;;
78
+ *)
79
+ echo "scaffold.sh: unknown --frontend value '$FRONTEND'. Allowed: astro, custom." >&2
80
+ exit 2
81
+ ;;
82
+ esac
83
+
84
+ if [[ ! "${POSITIONAL[0]}" =~ ^[a-z0-9]{3,20}$ ]]; then
85
+ echo "scaffold.sh: project-slug='${POSITIONAL[0]}' is not valid." >&2
39
86
  echo "Slug must be 3-20 lowercase alphanumeric chars (no hyphens, no spaces)." >&2
40
87
  echo "Derive from the brand: lowercase, strip non-[a-z0-9], truncate to 20." >&2
41
88
  exit 2
42
89
  fi
43
90
 
44
- # Pure-headless blank template. Vibe-compatible templates show an interactive
45
- # vibe setup prompt that blocks non-TTY contexts — do not substitute.
46
- TEMPLATE_ID="blank"
91
+ # Defensive auth check — DISCOVERY.md's pre-flight should have caught this
92
+ # already, but scaffold.sh is also a documented standalone entry point
93
+ # (SKILL.md § "When NOT to use this skill"). `npm create @wix/new` requires
94
+ # an active CLI session and otherwise fails mid-run with an opaque error.
95
+ # `wix whoami` exits non-zero on a logged-out session and prints the
96
+ # authenticated email + user id when logged in.
97
+ if ! npx @wix/cli@latest whoami >/dev/null 2>&1; then
98
+ echo "scaffold.sh: not logged in to Wix CLI." >&2
99
+ echo "Run 'npx @wix/cli@latest login' and retry." >&2
100
+ exit 3
101
+ fi
47
102
 
48
103
  npm create @wix/new@latest headless -- \
49
- --business-name "$2" \
50
- --project-name "$1" \
51
- --site-template "$TEMPLATE_ID" \
104
+ --business-name "${POSITIONAL[1]}" \
105
+ --project-name "${POSITIONAL[0]}" \
106
+ --site-template \
52
107
  --no-publish \
53
108
  --skip-install