@wix/agent-skills 1.20.0 → 1.21.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.
- package/package.json +1 -1
- package/skills/wix-app/SKILL.md +14 -112
- package/skills/wix-app/references/APP_TOOLS.md +2 -1
- package/skills/wix-app/references/BACKEND_EVENT.md +3 -3
- package/skills/wix-app/references/CUSTOM_ELEMENT_WIDGET.md +41 -152
- package/skills/wix-app/references/DASHBOARD_MODAL.md +4 -4
- package/skills/wix-app/references/SDK_MODULE_MAP.md +44 -0
- package/skills/wix-app/references/SITE_PLUGIN.md +36 -190
- package/skills/wix-app/references/app-tools/TOOLS.md +3 -1
- package/skills/wix-app/references/custom-element-widget/SETTINGS_PANEL.md +21 -0
- package/skills/wix-app/references/dashboard-page/ECOM_NAVIGATION.md +17 -0
- package/skills/wix-app/references/dashboard-plugin/SLOTS.md +4 -18
- package/skills/wix-app/references/site-plugin/EXAMPLES.md +1 -1
- package/skills/wix-headless-fast/SKILL.md +302 -51
- package/skills/wix-headless-fast/entry/skill.md +11 -2
- package/skills/wix-headless-fast/install/AGENTS.md +32 -0
- package/skills/wix-headless-fast/install/agents-md.mjs +39 -0
- package/skills/wix-headless-fast/install/attach.mjs +360 -0
- package/skills/wix-headless-fast/install/deploy.mjs +109 -9
- package/skills/wix-headless-fast/install/setup.mjs +239 -0
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +226 -60
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +2 -3
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +2 -3
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +24 -116
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +16 -64
- package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +137 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +78 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +117 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +13 -60
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +47 -0
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +10 -28
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +2 -4
- package/skills/wix-headless-fast/references/blog/rest/posts.ts +42 -0
- package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +62 -0
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/blog/seed/read-site.mjs +22 -0
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +181 -33
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +2 -1
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +26 -132
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +18 -49
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +218 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +169 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +57 -142
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +92 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +90 -0
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +22 -61
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +3 -1
- package/skills/wix-headless-fast/references/bookings/rest/booking.ts +113 -0
- package/skills/wix-headless-fast/references/bookings/rest/services.ts +55 -0
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +10 -0
- package/skills/wix-headless-fast/references/bookings/seed/read-site.mjs +28 -0
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +238 -62
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +19 -52
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +16 -36
- package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +158 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +93 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +177 -0
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +13 -41
- package/skills/wix-headless-fast/references/cms/rest/items.ts +125 -0
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/cms/seed/read-site.mjs +20 -0
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +241 -83
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +1 -1
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +7 -5
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +2 -2
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +28 -110
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +19 -54
- package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +130 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +97 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +13 -94
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +113 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +156 -0
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +38 -69
- package/skills/wix-headless-fast/references/events/rest/events.ts +57 -0
- package/skills/wix-headless-fast/references/events/rest/registration.ts +83 -0
- package/skills/wix-headless-fast/references/events/seed/SEED.md +9 -0
- package/skills/wix-headless-fast/references/events/seed/read-site.mjs +26 -0
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +216 -26
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +35 -267
- package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +332 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +238 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +28 -243
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +216 -0
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +32 -204
- package/skills/wix-headless-fast/references/forms/rest/forms.ts +34 -0
- package/skills/wix-headless-fast/references/forms/rest/submissions.ts +56 -0
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/forms/seed/read-site.mjs +20 -0
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +15 -8
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +316 -63
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +1 -1
- package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +105 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +36 -65
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +1 -1
- package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +43 -0
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +11 -32
- package/skills/wix-headless-fast/references/members/rest/auth.ts +215 -0
- package/skills/wix-headless-fast/references/members/rest/members.ts +26 -0
- package/skills/wix-headless-fast/references/members/seed/read-site.mjs +15 -0
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +240 -59
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +2 -2
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +2 -2
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +35 -0
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +8 -38
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +23 -50
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +16 -34
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +22 -49
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +82 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +58 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +177 -0
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +31 -118
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +81 -0
- package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +75 -0
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/portfolio/seed/read-site.mjs +20 -0
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +19 -21
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +214 -51
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +9 -20
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +7 -32
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +41 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +12 -33
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +17 -34
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +54 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +106 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +64 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +7 -55
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +43 -0
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +11 -34
- package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +31 -0
- package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +20 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/pricing-plans/seed/read-site.mjs +21 -0
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +242 -47
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +2 -2
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +2 -2
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +14 -35
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +28 -171
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +155 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +89 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +23 -117
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +124 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +45 -114
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +193 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +94 -0
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +15 -87
- package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +60 -0
- package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +169 -0
- package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +68 -0
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +8 -0
- package/skills/wix-headless-fast/references/restaurants/seed/read-site.mjs +25 -0
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +25 -5
- package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +23 -0
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +45 -4
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +13 -0
- package/skills/wix-headless-fast/references/shared/rest/client.ts +155 -0
- package/skills/wix-headless-fast/references/shared/rest/config.ts +3 -0
- package/skills/wix-headless-fast/references/shared/rest/media.ts +65 -0
- package/skills/wix-headless-fast/references/shared/seed/read-site.mjs +99 -0
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +186 -63
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +95 -0
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +29 -58
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +29 -163
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +26 -300
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +121 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +49 -162
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +314 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +47 -370
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +147 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +289 -0
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +2 -0
- package/skills/wix-headless-fast/references/storefront/rest/cart.ts +132 -0
- package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +123 -0
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +31 -9
- package/skills/wix-headless-fast/references/storefront/seed/read-site.mjs +38 -0
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +57 -50
- package/skills/wix-manage/references/ecommerce/ecom-shipping.md +1 -1
- package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-coupon.md +10 -19
- package/skills/wix-manage/references/events/create-wix-event.md +5 -1
- package/skills/wix-vibe-headless/references/storefront/INSTRUCTIONS.md +0 -40
- package/skills/wix-vibe-headless/references/storefront/seed/SEED.md +3 -0
- package/skills/wix-vibe-headless/references/storefront/seed/seed-store.cjs +6 -0
- package/skills/wix-headless-fast/install/fast-path.mjs +0 -186
- package/skills/wix-headless-fast/install/pin-agents-md.mjs +0 -57
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Storefront seed — a BUILD-TIME script, never shipped in the app. Run it from the project
|
|
2
2
|
// root (where wix.config.json lives) with a plan file:
|
|
3
3
|
//
|
|
4
|
-
// node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json
|
|
4
|
+
// node <SKILL_ROOT>/references/storefront/seed/seed-store.mjs plan.json
|
|
5
5
|
//
|
|
6
6
|
// It mints its own site token via the Wix CLI (the token never leaves this process), installs
|
|
7
7
|
// the Wix Stores app if needed, waits for the V3 catalog, bulk-creates products (variants
|
|
@@ -367,23 +367,6 @@ export async function createCategories(ctx, names) {
|
|
|
367
367
|
return out;
|
|
368
368
|
}
|
|
369
369
|
|
|
370
|
-
// The Stores install drops sample products ("I'm a product") into every new catalog. On a site the
|
|
371
|
-
// fast path JUST created nothing else can exist, so they are deleted before seeding — otherwise the
|
|
372
|
-
// storefront ships with the samples between the real products. Only called with --fresh-site; a
|
|
373
|
-
// seed against an existing site never deletes anything (SEED.md: seeding is additive).
|
|
374
|
-
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-delete-products.md
|
|
375
|
-
export async function deleteAllProducts(ctx) {
|
|
376
|
-
let deleted = 0;
|
|
377
|
-
for (let pass = 0; pass < 10; pass++) {
|
|
378
|
-
const r = await req(ctx, "/stores/v3/products/query", { body: { query: { cursorPaging: { limit: 100 } } } });
|
|
379
|
-
const ids = (r.products ?? []).map((p) => p.id).filter(Boolean);
|
|
380
|
-
if (!ids.length) break;
|
|
381
|
-
await req(ctx, "/stores/v3/bulk/products/delete", { body: { productIds: ids } });
|
|
382
|
-
deleted += ids.length;
|
|
383
|
-
if (ids.length < 100) break;
|
|
384
|
-
}
|
|
385
|
-
return deleted;
|
|
386
|
-
}
|
|
387
370
|
|
|
388
371
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/categories/bulk-add-items-to-category.md
|
|
389
372
|
export async function addProductsToCategories(ctx, mapping) {
|
|
@@ -401,19 +384,47 @@ export async function addProductsToCategories(ctx, mapping) {
|
|
|
401
384
|
// current revision is read right before the update, so attach any number of times, any pass.
|
|
402
385
|
// Wix re-hosts each url server-side; the media can take a little while to appear on read-back
|
|
403
386
|
// (propagation) — normal, not a failure.
|
|
387
|
+
// The bulk update returns 200 on PARTIAL failure, like the bulk create: each item's outcome is
|
|
388
|
+
// in results[].itemMetadata (success, error, originalIndex). Seen live (runs 85 and 86,
|
|
389
|
+
// 2026-09-26): one product of three came back with no media while the call succeeded — most
|
|
390
|
+
// likely its revision moved between the read and the update (variant stocking runs just
|
|
391
|
+
// before). So: pair results to inputs, retry the misses once with fresh revisions, and report
|
|
392
|
+
// what actually persisted. Returns { attached: [id], failures: [{ id, error }] }.
|
|
404
393
|
// docs: https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/bulk-update-products.md
|
|
405
394
|
export async function attachProductImages(ctx, items) {
|
|
406
|
-
if (!items?.length) return;
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
395
|
+
if (!items?.length) return { attached: [], failures: [] };
|
|
396
|
+
const send = async (batch) => {
|
|
397
|
+
const ids = batch.map((it) => it.id);
|
|
398
|
+
const q = await req(ctx, "/stores/v3/products/query", { body: { query: { filter: { id: { $in: ids } }, paging: { limit: ids.length } } } });
|
|
399
|
+
const revById = new Map((q.products ?? []).map((p) => [p.id, p.revision]));
|
|
400
|
+
const r = await req(ctx, "/stores/v3/bulk/products/update", {
|
|
401
|
+
body: {
|
|
402
|
+
products: batch.map((it) => ({
|
|
403
|
+
product: { id: it.id, revision: revById.get(it.id), media: { itemsInfo: { items: [{ url: it.url, altText: it.altText }] } } },
|
|
404
|
+
})),
|
|
405
|
+
},
|
|
406
|
+
});
|
|
407
|
+
const ok = new Set();
|
|
408
|
+
const failed = [];
|
|
409
|
+
for (const x of r.results ?? []) {
|
|
410
|
+
const src = batch[x.itemMetadata?.originalIndex];
|
|
411
|
+
if (!src) continue;
|
|
412
|
+
if (x.itemMetadata?.success) ok.add(src.id);
|
|
413
|
+
else failed.push({ id: src.id, error: x.itemMetadata?.error?.description ?? x.itemMetadata?.error?.code ?? "unknown" });
|
|
414
|
+
}
|
|
415
|
+
// an input with no result at all did not persist either
|
|
416
|
+
for (const it of batch) if (!ok.has(it.id) && !failed.some((f) => f.id === it.id)) failed.push({ id: it.id, error: "no result for item" });
|
|
417
|
+
return { ok, failed };
|
|
418
|
+
};
|
|
419
|
+
const first = await send(items);
|
|
420
|
+
const attached = new Set(first.ok);
|
|
421
|
+
let failures = first.failed;
|
|
422
|
+
if (failures.length) {
|
|
423
|
+
const retry = await send(items.filter((it) => failures.some((f) => f.id === it.id)));
|
|
424
|
+
for (const id of retry.ok) attached.add(id);
|
|
425
|
+
failures = retry.failed;
|
|
426
|
+
}
|
|
427
|
+
return { attached: [...attached], failures };
|
|
417
428
|
}
|
|
418
429
|
|
|
419
430
|
// Reject plans the API would reject halfway through, while nothing has been created yet —
|
|
@@ -462,22 +473,12 @@ async function setSiteCurrency(ctx, currency) {
|
|
|
462
473
|
* threaded in memory. This is the default path — call it once instead of the individual
|
|
463
474
|
* functions.
|
|
464
475
|
*/
|
|
465
|
-
export async function setupStore(ctx, { products = [], categories = {}, currency } = {}
|
|
476
|
+
export async function setupStore(ctx, { products = [], categories = {}, currency } = {}) {
|
|
466
477
|
validateProducts(products);
|
|
467
478
|
await installStoresApp(ctx);
|
|
468
479
|
// Before any product exists: a product's price is stored in the site currency at create time,
|
|
469
480
|
// so switching afterwards leaves the catalog priced in the old one.
|
|
470
481
|
if (currency) await setSiteCurrency(ctx, currency);
|
|
471
|
-
// A just-created site holds only the install's sample products — clear them (never on an
|
|
472
|
-
// existing site: the flag is the fast path's, set only when it created the site itself).
|
|
473
|
-
let samplesDeleted = 0;
|
|
474
|
-
if (freshSite) {
|
|
475
|
-
try {
|
|
476
|
-
samplesDeleted = await deleteAllProducts(ctx);
|
|
477
|
-
} catch (e) {
|
|
478
|
-
console.error(`sample cleanup failed (seeding on top): ${String(e.message).slice(0, 120)}`);
|
|
479
|
-
}
|
|
480
|
-
}
|
|
481
482
|
|
|
482
483
|
// Idempotent by name: an errored bulk create (429/5xx) may still have applied server-side,
|
|
483
484
|
// and SKILL.md tells the agent to re-run a failed seed — creating only the names that don't
|
|
@@ -518,34 +519,40 @@ export async function setupStore(ctx, { products = [], categories = {}, currency
|
|
|
518
519
|
const imageItems = withNames
|
|
519
520
|
.map((p, i) => (files[i] && p.id ? { id: p.id, url: files[i].url, altText: products[i]?.altText ?? p.slug } : null))
|
|
520
521
|
.filter(Boolean);
|
|
522
|
+
// imagesAttached counts the attaches the API CONFIRMED (per-item results), not the ones sent;
|
|
523
|
+
// imageFailures names the products left text-only and why. Neither blocks the seed: a re-run of
|
|
524
|
+
// the same plan reuses the products and attaches again.
|
|
521
525
|
let imagesAttached = 0;
|
|
526
|
+
const imageFailures = [];
|
|
527
|
+
const nameOf = (id) => withNames.find((p) => p.id === id)?.name;
|
|
522
528
|
try {
|
|
523
|
-
if (imageItems.length)
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
529
|
+
if (imageItems.length) {
|
|
530
|
+
const r = await attachProductImages(ctx, imageItems);
|
|
531
|
+
imagesAttached = r.attached.length;
|
|
532
|
+
for (const f of r.failures) imageFailures.push({ name: nameOf(f.id), error: f.error });
|
|
533
|
+
}
|
|
534
|
+
} catch (e) {
|
|
535
|
+
for (const it of imageItems) imageFailures.push({ name: nameOf(it.id), error: e?.message ?? String(e) });
|
|
527
536
|
}
|
|
528
537
|
|
|
529
538
|
// failures is part of the result, not an exception: a partial seed still leaves a usable
|
|
530
539
|
// store, and the agent needs the names to report rather than silently shipping a short
|
|
531
540
|
// catalog. Re-run the seed to retry them — existing names are skipped, not duplicated.
|
|
532
|
-
return { products: withNames, categories: cats, imagesAttached,
|
|
541
|
+
return { products: withNames, categories: cats, imagesAttached, imageFailures, failures };
|
|
533
542
|
}
|
|
534
543
|
|
|
535
544
|
// ---- CLI entry ----------------------------------------------------------------------------------
|
|
536
545
|
|
|
537
546
|
const invokedDirectly = process.argv[1] && import.meta.url.endsWith(process.argv[1].split("/").pop());
|
|
538
547
|
if (invokedDirectly) {
|
|
539
|
-
const
|
|
540
|
-
const freshSite = args.includes("--fresh-site");
|
|
541
|
-
const planPath = args.find((a) => !a.startsWith("--"));
|
|
548
|
+
const planPath = process.argv[2];
|
|
542
549
|
if (!planPath) {
|
|
543
|
-
console.error("usage: node seed-store.mjs <plan.json>
|
|
550
|
+
console.error("usage: node seed-store.mjs <plan.json> (run from the project root)");
|
|
544
551
|
process.exit(1);
|
|
545
552
|
}
|
|
546
553
|
const plan = JSON.parse(readFileSync(planPath, "utf8"));
|
|
547
554
|
const ctx = makeCtx();
|
|
548
|
-
setupStore(ctx, plan
|
|
555
|
+
setupStore(ctx, plan)
|
|
549
556
|
.then((result) => console.log(JSON.stringify(result, null, 2)))
|
|
550
557
|
.catch((e) => {
|
|
551
558
|
console.error(e.message);
|
|
@@ -29,7 +29,7 @@ Set up and tune how a store ships — what rates to charge, which regions are co
|
|
|
29
29
|
|
|
30
30
|
> - [Set up shipping rates / rules](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/shipping-set-up-rates) — tags: `[intent:setup-rates]` · priority 0
|
|
31
31
|
> - [Set up delivery regions / coverage](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/shipping-set-up-regions) — tags: `[intent:setup-regions]` · priority 0
|
|
32
|
-
> - [Set up store pickup / local delivery](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/local-delivery) — tags: `[intent:setup-pickup]` · priority 0
|
|
32
|
+
> - [Set up store pickup / local delivery](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/shipping-set-up-pickup-local-delivery) — tags: `[intent:setup-pickup]` · priority 0
|
|
33
33
|
> - [Add free shipping over $X](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/shipping-add-free-shipping) — tags: `[intent:free-shipping]` · priority 0
|
|
34
34
|
|
|
35
35
|
### Optimize & fix
|
package/skills/wix-manage/references/ecommerce/pricing-promotions/ecom-pricing-create-coupon.md
CHANGED
|
@@ -47,10 +47,7 @@ Before creating a coupon, check for code conflicts and existing promotions on th
|
|
|
47
47
|
"code": "SUMMER20",
|
|
48
48
|
"percentOffRate": 20,
|
|
49
49
|
"scope": {
|
|
50
|
-
"namespace": "stores"
|
|
51
|
-
"group": {
|
|
52
|
-
"name": "product"
|
|
53
|
-
}
|
|
50
|
+
"namespace": "stores"
|
|
54
51
|
},
|
|
55
52
|
"startTime": 1717200000000,
|
|
56
53
|
"expirationTime": 1719792000000,
|
|
@@ -91,10 +88,7 @@ Check for: duplicate codes, overlapping scopes with active coupons, and cross-me
|
|
|
91
88
|
"code": "SPRING15",
|
|
92
89
|
"percentOffRate": 15,
|
|
93
90
|
"scope": {
|
|
94
|
-
"namespace": "stores"
|
|
95
|
-
"group": {
|
|
96
|
-
"name": "product"
|
|
97
|
-
}
|
|
91
|
+
"namespace": "stores"
|
|
98
92
|
},
|
|
99
93
|
"startTime": 1714521600000,
|
|
100
94
|
"usageLimit": 200,
|
|
@@ -167,10 +161,7 @@ Check for: duplicate codes, overlapping scopes with active coupons, and cross-me
|
|
|
167
161
|
"code": "SAVE10",
|
|
168
162
|
"moneyOffAmount": 10,
|
|
169
163
|
"scope": {
|
|
170
|
-
"namespace": "stores"
|
|
171
|
-
"group": {
|
|
172
|
-
"name": "product"
|
|
173
|
-
}
|
|
164
|
+
"namespace": "stores"
|
|
174
165
|
},
|
|
175
166
|
"startTime": 1714521600000,
|
|
176
167
|
"active": true
|
|
@@ -230,11 +221,11 @@ Instead of targeting a scope, you can require a minimum cart subtotal. This is a
|
|
|
230
221
|
|
|
231
222
|
## Scope values for Wix Stores
|
|
232
223
|
|
|
233
|
-
| Scope target | `namespace` | `group
|
|
234
|
-
|
|
235
|
-
| All store products | `"stores"` | `
|
|
236
|
-
| Specific product | `"stores"` | `"product"
|
|
237
|
-
| Specific collection | `"stores"` | `"collection"
|
|
224
|
+
| Scope target | `namespace` | `group` |
|
|
225
|
+
|---|---|---|
|
|
226
|
+
| All store products | `"stores"` | Omit the `group` field entirely — a present-but-empty `group: {}` is rejected the same as a populated one missing `entityId` |
|
|
227
|
+
| Specific product | `"stores"` | `{ "name": "product", "entityId": "<product UUID>" }` |
|
|
228
|
+
| Specific collection | `"stores"` | `{ "name": "collection", "entityId": "<collection UUID>" }` |
|
|
238
229
|
|
|
239
230
|
---
|
|
240
231
|
|
|
@@ -246,7 +237,7 @@ When the recommendation output has `mechanism: "COUPON"`, use this mapping to co
|
|
|
246
237
|
|
|
247
238
|
| Recommendation `scope` | Coupon `scope` |
|
|
248
239
|
|---|---|
|
|
249
|
-
| `SITE` | `{ "namespace": "stores"
|
|
240
|
+
| `SITE` | `{ "namespace": "stores" }` (all products — omit `group` entirely) |
|
|
250
241
|
| `CATEGORY` | `{ "namespace": "stores", "group": { "name": "collection", "entityId": "<first categoryId>" } }` |
|
|
251
242
|
| `ITEMS` | `{ "namespace": "stores", "group": { "name": "product", "entityId": "<first productId>" } }` |
|
|
252
243
|
|
|
@@ -340,7 +331,7 @@ When the recommendation output has `mechanism: "COUPON"`, use this mapping to co
|
|
|
340
331
|
|
|
341
332
|
| Error | Cause | Fix |
|
|
342
333
|
|---|---|---|
|
|
343
|
-
| `"When scope or minimumSubtotal is not used - only FreeShipping coupon is allowed"` | Coupon sent without `scope` or `minimumSubtotal` | Add `scope: { "namespace": "stores"
|
|
334
|
+
| `"When scope or minimumSubtotal is not used - only FreeShipping coupon is allowed"` | Coupon sent without `scope` or `minimumSubtotal` | Add `scope: { "namespace": "stores" }` for site-wide (omit `group` — do not send `group: {}`, which is rejected identically), or set `minimumSubtotal` |
|
|
344
335
|
| Duplicate code | Another coupon uses the same code | Generate a different code |
|
|
345
336
|
| Invalid startTime | Value too low (must be epoch ms, not seconds) | Multiply by 1000 if in seconds |
|
|
346
337
|
| Both scope and minimumSubtotal set | These are oneOf — cannot use both | Choose scope OR minimumSubtotal |
|
|
@@ -200,7 +200,11 @@ The response is the definition under `ticketDefinition`:
|
|
|
200
200
|
Keep `revision` — `PATCH /events/v3/ticket-definitions/{id}` requires the current value, and it
|
|
201
201
|
increments on every update.
|
|
202
202
|
|
|
203
|
-
- **`feeType` is required
|
|
203
|
+
- **`feeType` is required.** A free ticket (`fixedPrice` of `"0"`) takes `NO_FEE`. A paid ticket
|
|
204
|
+
takes `FEE_ADDED_AT_CHECKOUT` (the guest pays the service fee) unless the user asked to absorb it,
|
|
205
|
+
then `FEE_INCLUDED`. `NO_FEE` on a paid ticket is accepted only on sites that do not collect ticket
|
|
206
|
+
fees. If a paid ticket fails with `400 INVALID_FEE_TYPE`, the site is one that does not collect
|
|
207
|
+
ticket fees: retry that ticket once with `NO_FEE`.
|
|
204
208
|
- **`value` is a string.** `"value": 10` fails `400 Unexpected value for field value`.
|
|
205
209
|
- **No writable `free` flag** — `pricingMethod.free` is read-only; a free ticket is a `fixedPrice`
|
|
206
210
|
of `"0"`.
|
|
@@ -235,24 +235,6 @@ export default function ProductDetail() {
|
|
|
235
235
|
- Render the PDP gallery from `images` — every image reachable (thumbnails, arrows, or a swipeable rail), not just the first; a single-image product gets no empty strip.
|
|
236
236
|
- `submit()` resets `adding` after completion and preserves the cart result: `undefined` on success, `null` on failure. Add failures live in `useCart().error`, not the PDP load `error`; the shipped drawer opens to display them.
|
|
237
237
|
|
|
238
|
-
For an optional **add and check out** shortcut, check the add result before checkout:
|
|
239
|
-
```jsx
|
|
240
|
-
// Inside ProductDetail, with the other hooks above any conditional returns:
|
|
241
|
-
const { checkout, loading: cartLoading } = useCart(); // named import from @/context/CartContext
|
|
242
|
-
async function addAndCheckout() {
|
|
243
|
-
if (!d.product || !d.canAdd || d.adding || cartLoading) return;
|
|
244
|
-
const result = await d.submit();
|
|
245
|
-
if (result === null) return; // add failed; preserve its error and do not check out the old cart
|
|
246
|
-
await checkout();
|
|
247
|
-
}
|
|
248
|
-
// <button disabled={!d.canAdd || d.adding || cartLoading} onClick={addAndCheckout}>Add and check out</button>
|
|
249
|
-
```
|
|
250
|
-
This adds to the shopper's **current cart** and checks that cart out — so label it that way. A true
|
|
251
|
-
**Buy Now** (skip the cart, buy just this item) needs a standalone cart handed to the redirect session;
|
|
252
|
-
it is not shipped, and routing "Buy now" through the current cart is wrong (it would carry whatever the
|
|
253
|
-
cart already holds). For direct `addToCart(...)` calls, use the same `result === null` check before checkout.
|
|
254
|
-
A resolved promise alone does not mean success; a truthiness check also rejects successful `undefined`.
|
|
255
|
-
|
|
256
238
|
### Variant and modifier controls
|
|
257
239
|
The product-detail example above provides both groups:
|
|
258
240
|
```js
|
|
@@ -475,28 +457,6 @@ Path params are `$name` in both the filename and the route path; `useParams()` f
|
|
|
475
457
|
reads them unchanged. Full pattern, including `ssr: false` for per-user routes:
|
|
476
458
|
[`../_shared/routing.md`](../_shared/routing.md).
|
|
477
459
|
|
|
478
|
-
## What a complete storefront shows
|
|
479
|
-
|
|
480
|
-
The wiring above is necessary, not sufficient. These are defaults for when the brief doesn't say
|
|
481
|
-
otherwise — whatever the user asked for in their prompt wins over any line here. Look at the
|
|
482
|
-
catalog before designing — categories, assortment size, media, options, sales — and design for this
|
|
483
|
-
store, not for a stereotype of its category. Then:
|
|
484
|
-
|
|
485
|
-
- **Home:** what the store sells and one shopping action in the first screen; real products from
|
|
486
|
-
the catalog under truthful headings; not a repeat of the shop page.
|
|
487
|
-
- **Shop:** a real product card — image, name, price, link — in the first screen; sort and the
|
|
488
|
-
filters the catalog supports; loading, empty, no-results, and error states that look different.
|
|
489
|
-
- **Product page:** image, name, price, the first choice, and the buy button with `blockedReason`
|
|
490
|
-
in the first screen; every ribbon; every image reachable in the gallery.
|
|
491
|
-
- **Cart:** the shipped drawer — it opens after every add, and checkout is a button in it.
|
|
492
|
-
- **Overlays you build** (quick-add, mobile nav, filters): mount at the document root, lock
|
|
493
|
-
background scroll, close on Escape, return focus on close — as the shipped `CartDrawer` does.
|
|
494
|
-
- **Copy:** nothing the merchant didn't supply — no invented reviews, scarcity, or delivery
|
|
495
|
-
promises; no Wix IDs or technical words in visible text.
|
|
496
|
-
|
|
497
|
-
Pre-order ships (`isPreorder`). Subscriptions, product groups, promotions, and notify-me are built
|
|
498
|
-
only when the catalog has them — never fabricated; consult the documentation skill for their endpoints.
|
|
499
|
-
|
|
500
460
|
## Missing capabilities
|
|
501
461
|
For anything these interfaces do not cover, consult the official Wix API documentation using
|
|
502
462
|
the documentation skill available in your environment.
|
|
@@ -41,6 +41,9 @@ const result = await seed.setupStore(ctx, {
|
|
|
41
41
|
// currency: { requested, actual, status, warnings } }
|
|
42
42
|
```
|
|
43
43
|
|
|
44
|
+
A product `name` is at most **80 characters** — Wix rejects the whole batch over that, so keep
|
|
45
|
+
names short and put the detail in `description`.
|
|
46
|
+
|
|
44
47
|
The optional `currency` sets the site's payment currency before product creation. Pass it only when
|
|
45
48
|
the user explicitly asked for a currency, or when it's obvious for the store — otherwise omit it. Do
|
|
46
49
|
not infer a currency from the builder's country/region or the brief's language; when in doubt, leave
|
|
@@ -174,6 +174,12 @@ function buildOptions(options = []) {
|
|
|
174
174
|
function validateProducts(products) {
|
|
175
175
|
const seen = new Map();
|
|
176
176
|
for (const product of products) {
|
|
177
|
+
// Wix caps a product name at 80 characters and the bulk create rejects the WHOLE batch over it
|
|
178
|
+
// ("name has size 175, expected 80 or less"), so the 81st character costs the entire seed.
|
|
179
|
+
if (typeof product.name === "string" && product.name.length > 80) {
|
|
180
|
+
throw new Error(`Product "${product.name.slice(0, 48)}…": the name is ${product.name.length} characters and Wix allows 80. ` +
|
|
181
|
+
`Shorten it and move the detail into description. No products were created by this call.`);
|
|
182
|
+
}
|
|
177
183
|
if (product.inStock !== undefined && typeof product.inStock !== "boolean") {
|
|
178
184
|
throw new Error(`Product "${product.name}": inStock must be a boolean. No products were created by this call.`);
|
|
179
185
|
}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
// Fast path for a managed CREATE run — one deterministic call from "plan exists" to
|
|
2
|
-
// "brand layer can start". Composes the pieces that also remain individually runnable
|
|
3
|
-
// (deploy.mjs, the vertical's seed module); use those directly to recover a failed step,
|
|
4
|
-
// or for connect/iterate runs (which must NOT scaffold and don't use this script).
|
|
5
|
-
//
|
|
6
|
-
// node <SKILL_ROOT>/install/fast-path.mjs --business-name "<Brand>" --plan plan.json \
|
|
7
|
-
// --vertical <storefront|bookings|…> [--stack astro] [--folder-name <npm-safe-name>] [--flatten]
|
|
8
|
-
//
|
|
9
|
-
// --flatten (opt-in): leave the project directly in the current directory instead of a subfolder,
|
|
10
|
-
// moving it before the background install starts. Default off — the normal output is a subfolder.
|
|
11
|
-
//
|
|
12
|
-
// It emits ONE JSON event per line and exits in ~35s with BOTH long steps — the dependency
|
|
13
|
-
// install AND the seed — running detached in the background (logs + completion markers
|
|
14
|
-
// reported in the final event), so the caller can build the brand layer while they finish.
|
|
15
|
-
// Steps: scaffold (Wix CLI; requires a logged-in session) → deploy shipped code + deps +
|
|
16
|
-
// lockfile → pin AGENTS.md → start `npm ci || npm install` detached → start the seed detached.
|
|
17
|
-
import { spawn, spawnSync } from "node:child_process";
|
|
18
|
-
import { existsSync, openSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
19
|
-
import { dirname, join, resolve } from "node:path";
|
|
20
|
-
import { fileURLToPath } from "node:url";
|
|
21
|
-
|
|
22
|
-
const SKILL_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
23
|
-
|
|
24
|
-
const emit = (event, extra = {}) => console.log(JSON.stringify({ event, ...extra }));
|
|
25
|
-
const fail = (step, detail) => {
|
|
26
|
-
emit("error", { step, detail: String(detail).slice(0, 600) });
|
|
27
|
-
process.exit(1);
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
// ---- args ---------------------------------------------------------------------------------------
|
|
31
|
-
const argv = process.argv.slice(2);
|
|
32
|
-
const flag = (name) => {
|
|
33
|
-
const i = argv.indexOf(`--${name}`);
|
|
34
|
-
return i !== -1 && argv[i + 1] && !argv[i + 1].startsWith("--") ? argv[i + 1] : null;
|
|
35
|
-
};
|
|
36
|
-
const businessName = flag("business-name");
|
|
37
|
-
const planPath = flag("plan");
|
|
38
|
-
const vertical = flag("vertical");
|
|
39
|
-
const stack = flag("stack") ?? "astro";
|
|
40
|
-
// Opt-in: leave the project in the current directory (flat) instead of a subfolder.
|
|
41
|
-
const flatten = argv.includes("--flatten");
|
|
42
|
-
// --vertical is REQUIRED: a defaulted vertical deploys the wrong code and runs the wrong
|
|
43
|
-
// seed against the plan — fail loudly with the discovered choices instead.
|
|
44
|
-
const knownVerticals = readdirSync(join(SKILL_ROOT, "references"), { withFileTypes: true })
|
|
45
|
-
.filter((d) => d.isDirectory() && d.name !== "shared" && existsSync(join(SKILL_ROOT, "references", d.name, "app")))
|
|
46
|
-
.map((d) => d.name);
|
|
47
|
-
if (!businessName || !planPath || !vertical) {
|
|
48
|
-
fail("args", `usage: fast-path.mjs --business-name "<Brand>" --plan plan.json --vertical <${knownVerticals.join("|")}> [--stack astro]`);
|
|
49
|
-
}
|
|
50
|
-
if (!knownVerticals.includes(vertical)) {
|
|
51
|
-
fail("args", `unknown vertical "${vertical}" — shipped verticals: ${knownVerticals.join(", ")}`);
|
|
52
|
-
}
|
|
53
|
-
if (!existsSync(planPath)) fail("args", `plan file not found: ${planPath}`);
|
|
54
|
-
const plan = JSON.parse(readFileSync(planPath, "utf8"));
|
|
55
|
-
|
|
56
|
-
const folderName =
|
|
57
|
-
flag("folder-name") ??
|
|
58
|
-
businessName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 40);
|
|
59
|
-
// `let`: after deploy we flatten the scaffold up into the repo root and repoint
|
|
60
|
-
// projectDir there, so the detached install/seed and every reported path use the
|
|
61
|
-
// final location.
|
|
62
|
-
let projectDir = resolve(process.cwd(), folderName);
|
|
63
|
-
|
|
64
|
-
// ---- 1 · scaffold -------------------------------------------------------------------------------
|
|
65
|
-
if (existsSync(join(process.cwd(), "wix.config.json"))) {
|
|
66
|
-
fail("scaffold", "the current directory is already a Wix project — this script is for CREATE runs only; use deploy.mjs + the seed module directly");
|
|
67
|
-
}
|
|
68
|
-
if (existsSync(join(projectDir, "wix.config.json"))) {
|
|
69
|
-
emit("scaffold_skipped", { reason: "project already exists", folder: folderName });
|
|
70
|
-
} else {
|
|
71
|
-
emit("scaffolding", { folder: folderName });
|
|
72
|
-
const scaffold = spawnSync(
|
|
73
|
-
"npm",
|
|
74
|
-
// --skip-git: this wrapper composes its own steps and leaves version control to
|
|
75
|
-
// the caller / the enclosing repo; the scaffold's own `git init` + "Initial
|
|
76
|
-
// commit" is noise here, and becomes a nested-repo (submodule gitlink) hazard
|
|
77
|
-
// if the project is later placed inside an existing repo. --skip-install for the
|
|
78
|
-
// same reason: deps install in a detached step below.
|
|
79
|
-
["create", "@wix/new@latest", "--", "headless",
|
|
80
|
-
"--folder-name", folderName, "--business-name", businessName,
|
|
81
|
-
"--site-template", "--skip-install", "--skip-git", "--no-publish"],
|
|
82
|
-
{ env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 300_000 },
|
|
83
|
-
);
|
|
84
|
-
if (scaffold.status !== 0 || !existsSync(join(projectDir, "wix.config.json"))) {
|
|
85
|
-
fail("scaffold", (scaffold.stderr || scaffold.stdout || "scaffold produced no wix.config.json — is the Wix CLI logged in? (npx @wix/cli@latest whoami)").slice(-600));
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
const wixConfig = JSON.parse(readFileSync(join(projectDir, "wix.config.json"), "utf8"));
|
|
89
|
-
const siteId = wixConfig.siteId ?? wixConfig.projectId;
|
|
90
|
-
emit("scaffolded", { folder: folderName, siteId });
|
|
91
|
-
|
|
92
|
-
// ---- 2 · deploy shipped code + deps + lockfile ---------------------------------------------------
|
|
93
|
-
const deploy = spawnSync(
|
|
94
|
-
"node",
|
|
95
|
-
[
|
|
96
|
-
join(SKILL_ROOT, "install", "deploy.mjs"),
|
|
97
|
-
vertical,
|
|
98
|
-
"--stack",
|
|
99
|
-
stack,
|
|
100
|
-
"--plan",
|
|
101
|
-
resolve(planPath),
|
|
102
|
-
],
|
|
103
|
-
{ cwd: projectDir, encoding: "utf8", timeout: 60_000 },
|
|
104
|
-
);
|
|
105
|
-
if (deploy.status !== 0) fail("deploy", deploy.stderr || deploy.stdout);
|
|
106
|
-
let deployResult = {};
|
|
107
|
-
try { deployResult = JSON.parse(deploy.stdout); } catch { /* keep going with raw output below */ }
|
|
108
|
-
if (deployResult.error) fail("deploy", deployResult.error);
|
|
109
|
-
emit("deployed", deployResult);
|
|
110
|
-
|
|
111
|
-
// ---- 2b · record what this project is, for later sessions ---------------------------------------
|
|
112
|
-
const pin = spawnSync(
|
|
113
|
-
"node",
|
|
114
|
-
[join(SKILL_ROOT, "install", "pin-agents-md.mjs"), "--vertical", vertical, "--stack", stack],
|
|
115
|
-
{ cwd: projectDir, encoding: "utf8", timeout: 10_000 },
|
|
116
|
-
);
|
|
117
|
-
try { emit("agents_md", JSON.parse(pin.stdout)); } catch { /* never block the build on the note */ }
|
|
118
|
-
|
|
119
|
-
// ---- 2c · optional --flatten: move the scaffold into the current directory ----------------------
|
|
120
|
-
// OFF by default — the normal output is a self-contained subfolder, unchanged. Opt in when the
|
|
121
|
-
// caller wants the project directly in the current directory (e.g. bootstrapping into an existing
|
|
122
|
-
// repo that must stay one flat tree). Done HERE, before the detached install below, on purpose:
|
|
123
|
-
// no node_modules exists yet, so the move is instant and cannot collide with a running install —
|
|
124
|
-
// the failure mode when a project is flattened by hand after the background install has started.
|
|
125
|
-
// A pure move: the scaffold was created with --skip-git (above), so there is no nested repo to
|
|
126
|
-
// reconcile — git is whatever the destination already is.
|
|
127
|
-
const targetDir = process.cwd();
|
|
128
|
-
if (flatten && projectDir !== targetDir) {
|
|
129
|
-
try {
|
|
130
|
-
for (const entry of readdirSync(projectDir)) {
|
|
131
|
-
renameSync(join(projectDir, entry), join(targetDir, entry));
|
|
132
|
-
}
|
|
133
|
-
rmSync(projectDir, { recursive: true, force: true });
|
|
134
|
-
projectDir = targetDir;
|
|
135
|
-
emit("flattened", { into: targetDir });
|
|
136
|
-
} catch (e) {
|
|
137
|
-
fail("flatten", e?.stack || e);
|
|
138
|
-
}
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// ---- 3 · start the dependency install, detached --------------------------------------------------
|
|
142
|
-
const installLog = join(projectDir, "npm-install.log");
|
|
143
|
-
const logFd = openSync(installLog, "a");
|
|
144
|
-
const install = spawn("sh", ["-c", "npm ci --ignore-scripts || npm install --ignore-scripts"], {
|
|
145
|
-
cwd: projectDir,
|
|
146
|
-
detached: true,
|
|
147
|
-
stdio: ["ignore", logFd, logFd],
|
|
148
|
-
});
|
|
149
|
-
install.unref();
|
|
150
|
-
emit("install_started", { log: installLog, doneMarker: "node_modules/.package-lock.json" });
|
|
151
|
-
|
|
152
|
-
// ---- 4 · start the seed, detached ----------------------------------------------------------------
|
|
153
|
-
// The seed includes a Wix-side provisioning wait of unpredictable length (10-80s); running it
|
|
154
|
-
// in the caller's foreground would idle the agent for exactly that long. Detach it like the
|
|
155
|
-
// install: result JSON + exit-code marker land as files the caller syncs on before release.
|
|
156
|
-
const seedDir = join(SKILL_ROOT, "references", vertical, "seed");
|
|
157
|
-
const seedName = existsSync(seedDir)
|
|
158
|
-
? readdirSync(seedDir).find((f) => f.startsWith("seed-") && f.endsWith(".mjs"))
|
|
159
|
-
: undefined;
|
|
160
|
-
const seedFile = seedName ? join(seedDir, seedName) : null;
|
|
161
|
-
if (!seedFile) fail("seed", `no seed module found under ${seedDir}`);
|
|
162
|
-
const seedResultFile = join(projectDir, "seed-result.json");
|
|
163
|
-
const seedLog = join(projectDir, "seed.log");
|
|
164
|
-
const seedDoneMarker = join(projectDir, ".seed-exit");
|
|
165
|
-
const planAbs = resolve(planPath);
|
|
166
|
-
const seedChild = spawn(
|
|
167
|
-
"sh",
|
|
168
|
-
// --fresh-site: this script created the site moments ago, so the only products in it are the
|
|
169
|
-
// Stores install's samples — the seed clears them first (a seed run by hand never deletes).
|
|
170
|
-
["-c", `node "${seedFile}" "${planAbs}" --fresh-site > seed-result.json 2> seed.log; echo $? > .seed-exit`],
|
|
171
|
-
{ cwd: projectDir, detached: true, stdio: "ignore" },
|
|
172
|
-
);
|
|
173
|
-
seedChild.unref();
|
|
174
|
-
emit("seeding_started", { vertical, resultFile: seedResultFile, log: seedLog, doneMarker: seedDoneMarker });
|
|
175
|
-
|
|
176
|
-
// ---- done ----------------------------------------------------------------------------------------
|
|
177
|
-
emit("ready_for_brand_layer", {
|
|
178
|
-
projectDir,
|
|
179
|
-
siteId,
|
|
180
|
-
dashboardUrl: deployResult.dashboardUrl,
|
|
181
|
-
productsUrl: deployResult.productsUrl,
|
|
182
|
-
categoriesUrl: deployResult.categoriesUrl,
|
|
183
|
-
install: { log: installLog, doneMarker: "node_modules/.package-lock.json" },
|
|
184
|
-
seed: { resultFile: "seed-result.json", log: "seed.log", doneMarker: ".seed-exit", success: "file contains 0" },
|
|
185
|
-
next: "theme SiteLayout + write the home page; then wait for BOTH done markers, verify .seed-exit is 0 (else read seed.log and re-run the seed module), build, release",
|
|
186
|
-
});
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
// Record what this project is in its agent instruction files, so a later session — which has
|
|
2
|
-
// none of the build conversation and may never open SKILL.md — still knows the Wix site is the
|
|
3
|
-
// source of truth and which skill answers which kind of question.
|
|
4
|
-
//
|
|
5
|
-
// node <SKILL_ROOT>/install/pin-agents-md.mjs --vertical <name> [--stack astro]
|
|
6
|
-
//
|
|
7
|
-
// Run from the project root. Kept out of deploy.mjs on purpose: "copy a vertical's files" and
|
|
8
|
-
// "record the project's ground rules" are separate concerns with separate reasons to change.
|
|
9
|
-
// APPENDS to each file — a template's or the user's own content is never overwritten — and is
|
|
10
|
-
// idempotent via the heading sentinel, so re-running fast-path.mjs writes at most once.
|
|
11
|
-
// Project facts that live in the repo (site id, app id) are NOT copied here; they would go
|
|
12
|
-
// stale against wix.config.json, which is the one place that owns them.
|
|
13
|
-
import { appendFileSync, existsSync, readFileSync } from "node:fs";
|
|
14
|
-
import { join } from "node:path";
|
|
15
|
-
|
|
16
|
-
const argv = process.argv.slice(2);
|
|
17
|
-
const flag = (n, d) => { const i = argv.indexOf(`--${n}`); return i >= 0 ? argv[i + 1] : d; };
|
|
18
|
-
|
|
19
|
-
const vertical = flag("vertical");
|
|
20
|
-
const stack = flag("stack", "astro");
|
|
21
|
-
// "astro" means the Wix-managed flavour (ambient auth), which is worth naming. Any other stack
|
|
22
|
-
// is echoed as given rather than mapped, so adding one can't silently mislabel the project.
|
|
23
|
-
const frontend = stack === "astro" ? "Wix-managed Astro" : stack.charAt(0).toUpperCase() + stack.slice(1);
|
|
24
|
-
|
|
25
|
-
const SENTINEL = "## This app — a Wix Headless frontend";
|
|
26
|
-
const note = `
|
|
27
|
-
${SENTINEL}
|
|
28
|
-
|
|
29
|
-
A ${frontend} frontend for a live Wix site; \`wix.config.json\` identifies the site. Built on
|
|
30
|
-
the **${vertical}** Wix business solution — more can be added to this same app. **The Wix site
|
|
31
|
-
owns the content and commerce** — read and write it through the Wix APIs, never hardcode what
|
|
32
|
-
it holds.
|
|
33
|
-
|
|
34
|
-
The Wix skills are installed at \`.agents/skills/\` — read them from that exact path:
|
|
35
|
-
|
|
36
|
-
- **\`wix-headless-fast\`** — the code already deployed in \`src/\` and how to extend it. Every
|
|
37
|
-
business solution it ships sits under \`references/<solution>/\`: \`INSTRUCTIONS.md\` is that
|
|
38
|
-
solution's playbook, \`seed/\` creates its content. To add one to this app, run from the
|
|
39
|
-
project root:
|
|
40
|
-
\`node .agents/skills/wix-headless-fast/install/deploy.mjs <solution> --stack ${stack}\`
|
|
41
|
-
- **\`wix-docs\`** — the Wix API reference. Confirm any endpoint, request shape, field, or enum
|
|
42
|
-
here before writing code against it, frontend or backend. Never infer a contract from
|
|
43
|
-
generated SDK types or \`node_modules\`.
|
|
44
|
-
- **\`wix-manage\`** — recipes for managing the live site itself, as opposed to changing this
|
|
45
|
-
codebase. No recipe for the task? Fall back to \`wix-docs\`.
|
|
46
|
-
`;
|
|
47
|
-
|
|
48
|
-
// Both files, independently — a project may have either, both, or neither, and agents differ
|
|
49
|
-
// in which one they read.
|
|
50
|
-
const pinned = ["AGENTS.md", "CLAUDE.md"].map((name) => {
|
|
51
|
-
const file = join(process.cwd(), name);
|
|
52
|
-
const existing = existsSync(file) ? readFileSync(file, "utf8") : "";
|
|
53
|
-
const wrote = !existing.includes(SENTINEL);
|
|
54
|
-
if (wrote) appendFileSync(file, (existing && !existing.endsWith("\n") ? "\n" : "") + note);
|
|
55
|
-
return { file: name, wrote, created: wrote && !existing };
|
|
56
|
-
});
|
|
57
|
-
console.log(JSON.stringify({ event: "agents_md", pinned }));
|