@wix/agent-skills 1.21.0 → 1.23.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 +71 -7
- package/skills/wix-app/references/APP_VALIDATION.md +16 -10
- package/skills/wix-app/references/DASHBOARD_PAGE.md +11 -25
- package/skills/wix-app/references/DATA_COLLECTION.md +16 -219
- package/skills/wix-app/references/SDK_MODULE_MAP.md +2 -0
- package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +182 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +97 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_COLLECTION.md +184 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +203 -0
- package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_SETTINGS.md +44 -0
- package/skills/wix-app/references/dashboard-page/QUERY_AND_PAGING.md +182 -0
- package/skills/wix-app/references/dashboard-page/TABLE_STATE.md +133 -0
- package/skills/wix-app/references/dashboard-page/UX_SUCCESS_MODEL.md +10 -5
- package/skills/wix-app/references/data-collection/LIFECYCLE.md +68 -0
- package/skills/wix-app/references/data-collection/PERMISSIONS.md +47 -0
- package/skills/wix-app/references/data-collection/SCHEMA.md +181 -0
- package/skills/wix-app/references/data-collection/WIX_DATA.md +38 -66
- package/skills/wix-headless-fast/SKILL.md +95 -230
- package/skills/wix-headless-fast/cold-start/bootstrap.mjs +156 -0
- package/skills/wix-headless-fast/{entry/skill.md → cold-start/cold-start.md} +17 -14
- package/skills/wix-headless-fast/guides/existing-site.md +78 -0
- package/skills/wix-headless-fast/guides/migration.md +43 -0
- package/skills/wix-headless-fast/guides/reference-mode.md +100 -0
- package/skills/wix-headless-fast/install/AGENTS.md +9 -4
- package/skills/wix-headless-fast/install/agents-md.mjs +5 -1
- package/skills/wix-headless-fast/install/attach.mjs +25 -47
- package/skills/wix-headless-fast/install/check.mjs +87 -0
- package/skills/wix-headless-fast/install/context.mjs +126 -0
- package/skills/wix-headless-fast/install/deploy.mjs +106 -80
- package/skills/wix-headless-fast/install/lock.mjs +55 -0
- package/skills/wix-headless-fast/install/setup.mjs +83 -23
- package/skills/wix-headless-fast/install/templates.mjs +168 -0
- package/skills/wix-manage/SKILL.md +3 -3
- package/skills/wix-manage/references/app-installation/install-wix-apps.md +68 -2
- package/skills/wix-manage/references/contacts/bulk-label-and-unlabel-contacts.md +37 -4
- package/skills/wix-manage/references/ecommerce/ecom-pricing.md +29 -3
- package/skills/wix-manage/references/google-ads/google-ads-dashboard-navigation.md +27 -0
- package/skills/wix-manage/references/google-ads/manage-campaign-success-guide.md +83 -37
- package/skills/wix-manage/references/sites/site-import.md +101 -37
- package/skills/wix-headless-fast/references/blog/INSTRUCTIONS.md +0 -303
- package/skills/wix-headless-fast/references/blog/app/components/blog/BlogFeedView.tsx +0 -136
- package/skills/wix-headless-fast/references/blog/app/components/blog/PostView.tsx +0 -112
- package/skills/wix-headless-fast/references/blog/app/components/blog/RichContent.tsx +0 -42
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/useBlogFeed.ts +0 -31
- package/skills/wix-headless-fast/references/blog/app/hooks/blog/usePost.ts +0 -22
- package/skills/wix-headless-fast/references/blog/app/wix/blog/blog-feed-store.ts +0 -137
- package/skills/wix-headless-fast/references/blog/app/wix/blog/post-store.ts +0 -78
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts-core.ts +0 -117
- package/skills/wix-headless-fast/references/blog/app/wix/blog/posts.ts +0 -52
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy-core.ts +0 -47
- package/skills/wix-headless-fast/references/blog/app/wix/blog/taxonomy.ts +0 -58
- package/skills/wix-headless-fast/references/blog/app/wix/blog/types.ts +0 -62
- package/skills/wix-headless-fast/references/blog/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog/[...slug].astro +0 -97
- package/skills/wix-headless-fast/references/blog/app-astro/pages/blog.astro +0 -24
- package/skills/wix-headless-fast/references/blog/lock/astro/package-lock.json +0 -16156
- package/skills/wix-headless-fast/references/blog/rest/posts.ts +0 -42
- package/skills/wix-headless-fast/references/blog/rest/taxonomy.ts +0 -62
- package/skills/wix-headless-fast/references/blog/seed/SEED.md +0 -81
- package/skills/wix-headless-fast/references/blog/seed/read-site.mjs +0 -22
- package/skills/wix-headless-fast/references/blog/seed/seed-blog.mjs +0 -287
- package/skills/wix-headless-fast/references/bookings/INSTRUCTIONS.md +0 -277
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServiceBookingView.tsx +0 -144
- package/skills/wix-headless-fast/references/bookings/app/components/bookings/ServicesView.tsx +0 -119
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useBookingFlow.ts +0 -32
- package/skills/wix-headless-fast/references/bookings/app/hooks/bookings/useServices.ts +0 -23
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-core.ts +0 -218
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking-flow-store.ts +0 -169
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/booking.ts +0 -121
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-core.ts +0 -92
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services-store.ts +0 -90
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/services.ts +0 -57
- package/skills/wix-headless-fast/references/bookings/app/wix/bookings/types.ts +0 -72
- package/skills/wix-headless-fast/references/bookings/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services/[slug].astro +0 -76
- package/skills/wix-headless-fast/references/bookings/app-astro/pages/services.astro +0 -22
- package/skills/wix-headless-fast/references/bookings/lock/astro/package-lock.json +0 -11962
- package/skills/wix-headless-fast/references/bookings/rest/booking.ts +0 -113
- package/skills/wix-headless-fast/references/bookings/rest/services.ts +0 -55
- package/skills/wix-headless-fast/references/bookings/seed/SEED.md +0 -71
- package/skills/wix-headless-fast/references/bookings/seed/read-site.mjs +0 -28
- package/skills/wix-headless-fast/references/bookings/seed/seed-bookings.mjs +0 -302
- package/skills/wix-headless-fast/references/cms/INSTRUCTIONS.md +0 -355
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useCollection.ts +0 -50
- package/skills/wix-headless-fast/references/cms/app/hooks/cms/useItem.ts +0 -37
- package/skills/wix-headless-fast/references/cms/app/wix/cms/collection-store.ts +0 -158
- package/skills/wix-headless-fast/references/cms/app/wix/cms/item-store.ts +0 -93
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items-core.ts +0 -177
- package/skills/wix-headless-fast/references/cms/app/wix/cms/items.ts +0 -123
- package/skills/wix-headless-fast/references/cms/app/wix/cms/types.ts +0 -75
- package/skills/wix-headless-fast/references/cms/app-astro/layouts/SiteLayout.astro +0 -41
- package/skills/wix-headless-fast/references/cms/lock/astro/package-lock.json +0 -11262
- package/skills/wix-headless-fast/references/cms/rest/items.ts +0 -125
- package/skills/wix-headless-fast/references/cms/seed/SEED.md +0 -112
- package/skills/wix-headless-fast/references/cms/seed/read-site.mjs +0 -20
- package/skills/wix-headless-fast/references/cms/seed/seed-cms.mjs +0 -324
- package/skills/wix-headless-fast/references/events/INSTRUCTIONS.md +0 -307
- package/skills/wix-headless-fast/references/events/app/components/events/EventConfirmationView.tsx +0 -66
- package/skills/wix-headless-fast/references/events/app/components/events/EventRegistrationView.tsx +0 -178
- package/skills/wix-headless-fast/references/events/app/components/events/EventsView.tsx +0 -118
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEventRegistration.ts +0 -35
- package/skills/wix-headless-fast/references/events/app/hooks/events/useEvents.ts +0 -24
- package/skills/wix-headless-fast/references/events/app/wix/events/events-core.ts +0 -130
- package/skills/wix-headless-fast/references/events/app/wix/events/events-store.ts +0 -97
- package/skills/wix-headless-fast/references/events/app/wix/events/events.ts +0 -55
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-core.ts +0 -113
- package/skills/wix-headless-fast/references/events/app/wix/events/registration-store.ts +0 -156
- package/skills/wix-headless-fast/references/events/app/wix/events/registration.ts +0 -97
- package/skills/wix-headless-fast/references/events/app/wix/events/types.ts +0 -61
- package/skills/wix-headless-fast/references/events/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/events/app-astro/pages/event-confirmation.astro +0 -12
- package/skills/wix-headless-fast/references/events/app-astro/pages/events/[slug].astro +0 -80
- package/skills/wix-headless-fast/references/events/app-astro/pages/events.astro +0 -21
- package/skills/wix-headless-fast/references/events/lock/astro/package-lock.json +0 -11884
- package/skills/wix-headless-fast/references/events/rest/events.ts +0 -57
- package/skills/wix-headless-fast/references/events/rest/registration.ts +0 -83
- package/skills/wix-headless-fast/references/events/seed/SEED.md +0 -95
- package/skills/wix-headless-fast/references/events/seed/read-site.mjs +0 -26
- package/skills/wix-headless-fast/references/events/seed/seed-events.mjs +0 -276
- package/skills/wix-headless-fast/references/forms/INSTRUCTIONS.md +0 -340
- package/skills/wix-headless-fast/references/forms/app/hooks/forms/useWixForm.ts +0 -85
- package/skills/wix-headless-fast/references/forms/app/wix/forms/form-store.ts +0 -332
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms-core.ts +0 -238
- package/skills/wix-headless-fast/references/forms/app/wix/forms/forms.ts +0 -54
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions-core.ts +0 -216
- package/skills/wix-headless-fast/references/forms/app/wix/forms/submissions.ts +0 -59
- package/skills/wix-headless-fast/references/forms/app/wix/forms/types.ts +0 -135
- package/skills/wix-headless-fast/references/forms/lock/astro/package-lock.json +0 -10593
- package/skills/wix-headless-fast/references/forms/rest/forms.ts +0 -34
- package/skills/wix-headless-fast/references/forms/rest/submissions.ts +0 -56
- package/skills/wix-headless-fast/references/forms/seed/SEED.md +0 -116
- package/skills/wix-headless-fast/references/forms/seed/read-site.mjs +0 -20
- package/skills/wix-headless-fast/references/forms/seed/seed-forms.mjs +0 -327
- package/skills/wix-headless-fast/references/members/INSTRUCTIONS.md +0 -316
- package/skills/wix-headless-fast/references/members/app/components/members/AccountView.tsx +0 -60
- package/skills/wix-headless-fast/references/members/app/components/members/LoginForm.tsx +0 -155
- package/skills/wix-headless-fast/references/members/app/components/members/MemberMenu.tsx +0 -78
- package/skills/wix-headless-fast/references/members/app/components/members/RequireAuth.tsx +0 -47
- package/skills/wix-headless-fast/references/members/app/hooks/members/useMember.ts +0 -66
- package/skills/wix-headless-fast/references/members/app/wix/members/auth-core.ts +0 -105
- package/skills/wix-headless-fast/references/members/app/wix/members/auth.ts +0 -59
- package/skills/wix-headless-fast/references/members/app/wix/members/client.ts +0 -53
- package/skills/wix-headless-fast/references/members/app/wix/members/member-store.ts +0 -122
- package/skills/wix-headless-fast/references/members/app/wix/members/members-core.ts +0 -43
- package/skills/wix-headless-fast/references/members/app/wix/members/members.ts +0 -29
- package/skills/wix-headless-fast/references/members/app/wix/members/types.ts +0 -21
- package/skills/wix-headless-fast/references/members/app-astro/layouts/SiteLayout.astro +0 -42
- package/skills/wix-headless-fast/references/members/app-astro/pages/account.astro +0 -10
- package/skills/wix-headless-fast/references/members/app-astro/pages/login.astro +0 -11
- package/skills/wix-headless-fast/references/members/lock/astro/package-lock.json +0 -10329
- package/skills/wix-headless-fast/references/members/rest/auth.ts +0 -215
- package/skills/wix-headless-fast/references/members/rest/members.ts +0 -26
- package/skills/wix-headless-fast/references/members/seed/SEED.md +0 -39
- package/skills/wix-headless-fast/references/members/seed/read-site.mjs +0 -15
- package/skills/wix-headless-fast/references/members/seed/seed-members.mjs +0 -94
- package/skills/wix-headless-fast/references/portfolio/INSTRUCTIONS.md +0 -315
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionProjectsView.tsx +0 -110
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/CollectionsView.tsx +0 -90
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/GalleryMedia.tsx +0 -35
- package/skills/wix-headless-fast/references/portfolio/app/components/portfolio/ProjectDetailView.tsx +0 -85
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollectionProjects.ts +0 -32
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useCollections.ts +0 -21
- package/skills/wix-headless-fast/references/portfolio/app/hooks/portfolio/useProjectDetail.ts +0 -31
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collection-projects-store.ts +0 -82
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/collections-store.ts +0 -58
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio-core.ts +0 -177
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/portfolio.ts +0 -78
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/project-detail-store.ts +0 -81
- package/skills/wix-headless-fast/references/portfolio/app/wix/portfolio/types.ts +0 -55
- package/skills/wix-headless-fast/references/portfolio/app-astro/layouts/SiteLayout.astro +0 -40
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio/[slug].astro +0 -30
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/portfolio.astro +0 -21
- package/skills/wix-headless-fast/references/portfolio/app-astro/pages/projects/[slug].astro +0 -29
- package/skills/wix-headless-fast/references/portfolio/lock/astro/package-lock.json +0 -10197
- package/skills/wix-headless-fast/references/portfolio/rest/portfolio.ts +0 -75
- package/skills/wix-headless-fast/references/portfolio/seed/SEED.md +0 -86
- package/skills/wix-headless-fast/references/portfolio/seed/read-site.mjs +0 -20
- package/skills/wix-headless-fast/references/portfolio/seed/seed-portfolio.mjs +0 -256
- package/skills/wix-headless-fast/references/pricing-plans/INSTRUCTIONS.md +0 -296
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlanDetailView.tsx +0 -47
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/PlansView.tsx +0 -101
- package/skills/wix-headless-fast/references/pricing-plans/app/components/pricing-plans/SubscribeButton.tsx +0 -41
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlanPurchase.ts +0 -16
- package/skills/wix-headless-fast/references/pricing-plans/app/hooks/pricing-plans/usePlans.ts +0 -22
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plan-purchase-store.ts +0 -54
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-core.ts +0 -106
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans-store.ts +0 -64
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/plans.ts +0 -27
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase-core.ts +0 -43
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/purchase.ts +0 -22
- package/skills/wix-headless-fast/references/pricing-plans/app/wix/pricing-plans/types.ts +0 -30
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/layouts/SiteLayout.astro +0 -41
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans/[slug].astro +0 -40
- package/skills/wix-headless-fast/references/pricing-plans/app-astro/pages/plans.astro +0 -21
- package/skills/wix-headless-fast/references/pricing-plans/lock/astro/package-lock.json +0 -11226
- package/skills/wix-headless-fast/references/pricing-plans/rest/plans.ts +0 -31
- package/skills/wix-headless-fast/references/pricing-plans/rest/purchase.ts +0 -20
- package/skills/wix-headless-fast/references/pricing-plans/seed/SEED.md +0 -79
- package/skills/wix-headless-fast/references/pricing-plans/seed/read-site.mjs +0 -21
- package/skills/wix-headless-fast/references/pricing-plans/seed/seed-pricing-plans.mjs +0 -279
- package/skills/wix-headless-fast/references/restaurants/INSTRUCTIONS.md +0 -360
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/MenuView.tsx +0 -142
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartButton.tsx +0 -26
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/OrderCartDrawer.tsx +0 -121
- package/skills/wix-headless-fast/references/restaurants/app/components/restaurants/ReservationView.tsx +0 -155
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useMenus.ts +0 -34
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useOrderCart.ts +0 -43
- package/skills/wix-headless-fast/references/restaurants/app/hooks/restaurants/useReservation.ts +0 -34
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-core.ts +0 -155
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu-store.ts +0 -89
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/menu.ts +0 -71
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/order-store.ts +0 -103
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering-core.ts +0 -124
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/ordering.ts +0 -185
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservation-store.ts +0 -193
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations-core.ts +0 -94
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/reservations.ts +0 -74
- package/skills/wix-headless-fast/references/restaurants/app/wix/restaurants/types.ts +0 -168
- package/skills/wix-headless-fast/references/restaurants/app-astro/layouts/SiteLayout.astro +0 -47
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/menu.astro +0 -22
- package/skills/wix-headless-fast/references/restaurants/app-astro/pages/reservations.astro +0 -12
- package/skills/wix-headless-fast/references/restaurants/lock/astro/package-lock.json +0 -12560
- package/skills/wix-headless-fast/references/restaurants/rest/menu.ts +0 -60
- package/skills/wix-headless-fast/references/restaurants/rest/ordering.ts +0 -169
- package/skills/wix-headless-fast/references/restaurants/rest/reservations.ts +0 -68
- package/skills/wix-headless-fast/references/restaurants/seed/SEED.md +0 -89
- package/skills/wix-headless-fast/references/restaurants/seed/read-site.mjs +0 -25
- package/skills/wix-headless-fast/references/restaurants/seed/seed-restaurants.mjs +0 -499
- package/skills/wix-headless-fast/references/shared/CONTENT.md +0 -22
- package/skills/wix-headless-fast/references/shared/CUSTOM_OPERATIONS.md +0 -73
- package/skills/wix-headless-fast/references/shared/DESIGN.md +0 -26
- package/skills/wix-headless-fast/references/shared/SUPPLIED-CONTENT.md +0 -23
- package/skills/wix-headless-fast/references/shared/app/styles/global.css +0 -52
- package/skills/wix-headless-fast/references/shared/app/wix/config.ts +0 -15
- package/skills/wix-headless-fast/references/shared/app/wix/media.ts +0 -81
- package/skills/wix-headless-fast/references/shared/app/wix/money.ts +0 -24
- package/skills/wix-headless-fast/references/shared/app/wix/sdk.ts +0 -89
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app/wix/media-upload/client.ts +0 -46
- package/skills/wix-headless-fast/references/shared/capabilities/media-upload/app-astro/pages/api/wix/media-upload.ts +0 -46
- package/skills/wix-headless-fast/references/shared/rest/client.ts +0 -155
- package/skills/wix-headless-fast/references/shared/rest/config.ts +0 -3
- package/skills/wix-headless-fast/references/shared/rest/media.ts +0 -65
- package/skills/wix-headless-fast/references/shared/seed/images.mjs +0 -145
- package/skills/wix-headless-fast/references/shared/seed/read-site.mjs +0 -99
- package/skills/wix-headless-fast/references/storefront/INSTRUCTIONS.md +0 -612
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartButton.tsx +0 -26
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/CartDrawer.tsx +0 -155
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/FilterPanel.tsx +0 -266
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/OptionPicker.tsx +0 -95
- package/skills/wix-headless-fast/references/storefront/app/components/storefront/QuickAdd.tsx +0 -136
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useCart.ts +0 -48
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useProductDetail.ts +0 -39
- package/skills/wix-headless-fast/references/storefront/app/hooks/storefront/useShop.ts +0 -34
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-core.ts +0 -121
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart-store.ts +0 -107
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/cart.ts +0 -138
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog-core.ts +0 -314
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/catalog.ts +0 -133
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/product-detail-store.ts +0 -147
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/shop-store.ts +0 -289
- package/skills/wix-headless-fast/references/storefront/app/wix/storefront/types.ts +0 -169
- package/skills/wix-headless-fast/references/storefront/app-astro/layouts/SiteLayout.astro +0 -65
- package/skills/wix-headless-fast/references/storefront/lock/astro/package-lock.json +0 -12247
- package/skills/wix-headless-fast/references/storefront/rest/cart.ts +0 -132
- package/skills/wix-headless-fast/references/storefront/rest/catalog.ts +0 -123
- package/skills/wix-headless-fast/references/storefront/seed/SEED.md +0 -130
- package/skills/wix-headless-fast/references/storefront/seed/read-site.mjs +0 -38
- package/skills/wix-headless-fast/references/storefront/seed/seed-store.mjs +0 -561
|
@@ -7,10 +7,16 @@
|
|
|
7
7
|
//
|
|
8
8
|
// - `wix.config.json` present → refuses. The folder is already a Wix project; deploy.mjs adds
|
|
9
9
|
// this skill's code or a solution to it, then one install, then the seed if there is content.
|
|
10
|
+
// One exception: a config and NO project whose `.env.local` (`wix env pull`, run here) declares a
|
|
11
|
+
// MIGRATION PREVIEW — the config names a site created only to host the deployment, the env names
|
|
12
|
+
// the site being migrated. Then → MIGRATE: the composed template is copied in around the config,
|
|
13
|
+
// the code deploys with the parent's app as its client, the install starts, and NO seed runs: the
|
|
14
|
+
// parent owns its content. Release still goes where the config says (install/context.mjs).
|
|
10
15
|
// - a project but no `wix.config.json` (a package.json, or an index.html at the root) → CONNECT:
|
|
11
16
|
// `npm create @wix/new@latest init` in place creates the site and the config, then deploy.
|
|
12
|
-
// - otherwise (empty, or loose files such as a CSV or a brief) → CREATE:
|
|
13
|
-
//
|
|
17
|
+
// - otherwise (empty, or loose files such as a CSV or a brief) → CREATE: `wix create` with the
|
|
18
|
+
// vertical's composed template (templates/<vertical>/project: the CLI's blank scaffold with the
|
|
19
|
+
// vertical deployed and a lockfile), placed in the current directory (`--business-name` required).
|
|
14
20
|
//
|
|
15
21
|
// The script reads file markers only (wix.config.json, a package.json or index.html); everything
|
|
16
22
|
// else is the agent's call: when connecting, `--stack` is required and comes from SKILL.md step 1,
|
|
@@ -22,10 +28,12 @@
|
|
|
22
28
|
// steps — the dependency install and the seed — running detached in the background (logs and
|
|
23
29
|
// completion markers in the final event), so the caller can build the brand layer while they finish.
|
|
24
30
|
import { spawn, spawnSync } from "node:child_process";
|
|
25
|
-
import { existsSync, openSync, readFileSync, readdirSync, renameSync, rmSync } from "node:fs";
|
|
31
|
+
import { cpSync, existsSync, openSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
26
32
|
import { basename, dirname, join, resolve } from "node:path";
|
|
27
33
|
import { fileURLToPath } from "node:url";
|
|
28
34
|
import { AGENT_CONFIG_FILES, writeAgentsMd } from "./agents-md.mjs";
|
|
35
|
+
import { listVerticals, templatesDir } from "./templates.mjs";
|
|
36
|
+
import { siteContext } from "./context.mjs";
|
|
29
37
|
|
|
30
38
|
const SKILL_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
31
39
|
|
|
@@ -47,9 +55,10 @@ const vertical = flag("vertical");
|
|
|
47
55
|
const stackFlag = flag("stack");
|
|
48
56
|
// Opt-in: keep a CREATE's project in a subfolder instead of the current directory.
|
|
49
57
|
const subfolder = argv.includes("--subfolder");
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
// The shipped code: the repository's templates/ (a checkout, the cache, or fetched now, in a second).
|
|
59
|
+
let TEMPLATES;
|
|
60
|
+
try { TEMPLATES = templatesDir(); } catch (e) { fail("templates", e.message); }
|
|
61
|
+
const knownVerticals = listVerticals(TEMPLATES);
|
|
53
62
|
const usage = `usage: setup.mjs --vertical <${knownVerticals.join("|")}> [--plan plan.json] [--business-name "<Brand>"] [--stack astro|react|lib|static] [--subfolder]`;
|
|
54
63
|
// --vertical is REQUIRED: a defaulted vertical deploys the wrong code and runs the wrong seed.
|
|
55
64
|
if (!vertical) fail("args", usage);
|
|
@@ -64,17 +73,23 @@ if (planPath && !existsSync(planPath)) fail("args", `plan file not found: ${plan
|
|
|
64
73
|
// ---- 0 · read the folder --------------------------------------------------------------------------
|
|
65
74
|
const cwd = process.cwd();
|
|
66
75
|
const has = (p) => existsSync(join(cwd, p));
|
|
67
|
-
if (has("wix.config.json")) {
|
|
68
|
-
fail("place", "this folder is already a Wix project (wix.config.json): nothing to set up. deploy.mjs <vertical> adds this skill's code or a solution to it; then ONE npm install; then the vertical's seed module if there is content to create");
|
|
69
|
-
}
|
|
70
76
|
const pkg = has("package.json") ? JSON.parse(readFileSync(join(cwd, "package.json"), "utf8")) : null;
|
|
71
77
|
const hasProject = !!pkg || has("index.html");
|
|
72
|
-
|
|
78
|
+
// A config here: refuse, unless it is a migration preview with no project yet (the site context
|
|
79
|
+
// pulls .env.local when missing — that is where the migration is declared).
|
|
80
|
+
let ctx = null;
|
|
81
|
+
if (has("wix.config.json")) {
|
|
82
|
+
ctx = siteContext({ cwd });
|
|
83
|
+
if (hasProject || !ctx.migration.active) {
|
|
84
|
+
fail("place", "this folder is already a Wix project (wix.config.json): nothing to set up. deploy.mjs <vertical> adds this skill's code or a solution to it; then ONE npm install; then the vertical's seed module if there is content to create. A config with no project is attach.mjs's case (SKILL.md step 3)" + (ctx.pullError ? ` — note: env pull failed here (${ctx.pullError.slice(0, 120)})` : ""));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const mode = ctx ? "migrate" : hasProject ? "connect" : "create";
|
|
73
88
|
if (mode === "connect" && !stackFlag) {
|
|
74
89
|
fail("args", "connecting a project on disk needs --stack astro|react|lib|static — the stack you resolved in SKILL.md step 1 for this project");
|
|
75
90
|
}
|
|
76
91
|
const stack = stackFlag ?? "astro";
|
|
77
|
-
emit("folder", { mode, stack, project: hasProject ? (pkg?.name ?? basename(cwd)) : null });
|
|
92
|
+
emit("folder", { mode, stack, project: hasProject ? (pkg?.name ?? basename(cwd)) : null, ...(ctx ? { migration: ctx.migration, deploySiteId: ctx.deploy.siteId, warnings: ctx.warnings } : {}) });
|
|
78
93
|
|
|
79
94
|
let projectDir = cwd;
|
|
80
95
|
let folderName = null;
|
|
@@ -92,7 +107,13 @@ if (mode === "create") {
|
|
|
92
107
|
if (existsSync(join(projectDir, "wix.config.json"))) {
|
|
93
108
|
emit("scaffold_skipped", { reason: "project already exists", folder: folderName });
|
|
94
109
|
} else {
|
|
95
|
-
|
|
110
|
+
// The composed template: the CLI copies it in place of its blank one, creates the site and
|
|
111
|
+
// writes wix.config.json; the vertical's code and its lockfile arrive with it. Any other
|
|
112
|
+
// stack takes the CLI's blank Astro scaffold, as before, and deploy below adds the code.
|
|
113
|
+
// The composed project is the one part of the templates a project's repository does not keep;
|
|
114
|
+
// a committed copy fetches it here, at the commit the rest came from.
|
|
115
|
+
const template = stack === "astro" ? join(templatesDir({ need: `${vertical}/project` }), vertical, "project") : null;
|
|
116
|
+
emit("scaffolding", { folder: folderName, template });
|
|
96
117
|
const scaffold = spawnSync(
|
|
97
118
|
"npm",
|
|
98
119
|
// --skip-git: this wrapper composes its own steps and leaves version control to
|
|
@@ -102,13 +123,33 @@ if (mode === "create") {
|
|
|
102
123
|
// same reason: deps install in a detached step below.
|
|
103
124
|
["create", "@wix/new@latest", "--", "headless",
|
|
104
125
|
"--folder-name", folderName, "--business-name", businessName,
|
|
105
|
-
"--site-template", "
|
|
126
|
+
"--site-template", "blank", ...(template ? ["--template-path", template] : []),
|
|
127
|
+
"--skip-install", "--skip-git", "--no-publish"],
|
|
106
128
|
{ env: { ...process.env, CI: "1" }, encoding: "utf8", timeout: 300_000 },
|
|
107
129
|
);
|
|
108
130
|
if (scaffold.status !== 0 || !existsSync(join(projectDir, "wix.config.json"))) {
|
|
109
131
|
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));
|
|
110
132
|
}
|
|
111
133
|
}
|
|
134
|
+
} else if (mode === "migrate") {
|
|
135
|
+
// ---- 1 · a migration preview: the config and credentials came with the folder -------------------
|
|
136
|
+
// No site is created and nothing is provisioned: `wix.config.json` already names the deploy site
|
|
137
|
+
// and its app, `.env.local` the parent's. Managed Astro gets the vertical's composed template copied
|
|
138
|
+
// in around them (what `wix create` would have copied; the config and env are never overwritten);
|
|
139
|
+
// any other stack is code-only and deploy below adds it.
|
|
140
|
+
if (planPath) emit("seed_skipped", { reason: `migration preview of site ${ctx.migration.parentSiteId}: the site owns its content and nothing is seeded — the plan is ignored` });
|
|
141
|
+
if (stack === "astro") {
|
|
142
|
+
const template = join(templatesDir({ need: `${vertical}/project` }), vertical, "project");
|
|
143
|
+
emit("scaffolding", { folder: cwd, template, from: "migration preview" });
|
|
144
|
+
cpSync(template, cwd, { recursive: true, force: false, errorOnExist: false });
|
|
145
|
+
const pkgPath = join(cwd, "package.json");
|
|
146
|
+
const scaffolded = JSON.parse(readFileSync(pkgPath, "utf8"));
|
|
147
|
+
scaffolded.name = basename(cwd).toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/^-+|-+$/g, "") || "site";
|
|
148
|
+
writeFileSync(pkgPath, JSON.stringify(scaffolded, null, 2) + "\n");
|
|
149
|
+
}
|
|
150
|
+
const gi = join(cwd, ".gitignore");
|
|
151
|
+
const cur = existsSync(gi) ? readFileSync(gi, "utf8") : "";
|
|
152
|
+
if (!/\.env/.test(cur)) writeFileSync(gi, cur + "\n# local env (pulled from Wix)\n.env.local\n.env\n");
|
|
112
153
|
} else {
|
|
113
154
|
// ---- 1 · init in place --------------------------------------------------------------------------
|
|
114
155
|
// The CLI's `init` links the folder to a NEW site: creates the site and its OAuth app, writes
|
|
@@ -123,12 +164,25 @@ if (mode === "create") {
|
|
|
123
164
|
}
|
|
124
165
|
const wixConfig = JSON.parse(readFileSync(join(projectDir, "wix.config.json"), "utf8"));
|
|
125
166
|
const siteId = wixConfig.siteId ?? wixConfig.projectId;
|
|
126
|
-
|
|
167
|
+
// Static: `wix release` uploads the output folder whole, so the project root (config, plan, seed
|
|
168
|
+
// output, the skills) must not be it. The site lives in site/: the config points there and the REST
|
|
169
|
+
// layer deploys into it; the pages, styles and assets are the agent's to move (the `next` says so).
|
|
170
|
+
const STATIC_OUT = "site";
|
|
171
|
+
if (stack === "static") {
|
|
172
|
+
wixConfig.site = { ...(wixConfig.site ?? {}), outputDirectory: `./${STATIC_OUT}` };
|
|
173
|
+
writeFileSync(join(projectDir, "wix.config.json"), JSON.stringify(wixConfig, null, 2) + "\n");
|
|
174
|
+
}
|
|
175
|
+
emit(mode === "create" ? "scaffolded" : mode === "migrate" ? "migration_preview" : "connected", {
|
|
176
|
+
folder: folderName ?? cwd, stack,
|
|
177
|
+
...(ctx ? { deploySiteId: ctx.deploy.siteId, contentSiteId: ctx.content.siteId } : { siteId }),
|
|
178
|
+
});
|
|
127
179
|
|
|
128
|
-
// ---- 2 · deploy shipped code + deps
|
|
180
|
+
// ---- 2 · deploy shipped code + deps ---------------------------------------------------------------
|
|
181
|
+
// A CREATE from the composed template already holds the code and the lock: deploy adds nothing and
|
|
182
|
+
// reports the project; a CONNECT gets the code and its dependencies here.
|
|
129
183
|
const deploy = spawnSync(
|
|
130
184
|
"node",
|
|
131
|
-
[join(SKILL_ROOT, "install", "deploy.mjs"), vertical, "--stack", stack, ...(planPath ? ["--plan", resolve(planPath)] : [])],
|
|
185
|
+
[join(SKILL_ROOT, "install", "deploy.mjs"), vertical, "--stack", stack, ...(stack === "static" ? ["--out", STATIC_OUT] : []), ...(planPath ? ["--plan", resolve(planPath)] : [])],
|
|
132
186
|
{ cwd: projectDir, encoding: "utf8", timeout: 60_000 },
|
|
133
187
|
);
|
|
134
188
|
if (deploy.status !== 0) fail("deploy", deploy.stderr || deploy.stdout);
|
|
@@ -169,7 +223,7 @@ if (mode === "create" && !subfolder && projectDir !== cwd) {
|
|
|
169
223
|
// ---- 2d · the agent config files `wix create` would have written --------------------------------
|
|
170
224
|
// Skipped by the CLI because of --skip-install. Fill-only: a project that has its own AGENTS.md
|
|
171
225
|
// keeps it (the event says `kept`).
|
|
172
|
-
emit("agent_configs", writeAgentsMd(projectDir, { skill: basename(SKILL_ROOT), stack }));
|
|
226
|
+
emit("agent_configs", writeAgentsMd(projectDir, { skill: basename(SKILL_ROOT), stack, ...(ctx ? { migration: { parentSiteId: ctx.migration.parentSiteId, deploySiteId: ctx.deploy.siteId } } : {}) }));
|
|
173
227
|
|
|
174
228
|
// ---- 3 · start the dependency install, detached --------------------------------------------------
|
|
175
229
|
// ONE install, here, for any project with a package.json (deploy patched it). The static stack has
|
|
@@ -195,8 +249,8 @@ if (stack !== "static" && existsSync(join(projectDir, "package.json"))) {
|
|
|
195
249
|
// result JSON + exit-code marker land as files the caller syncs on before release. Seeding is
|
|
196
250
|
// additive: it never deletes or overwrites what the site holds.
|
|
197
251
|
let seed = null;
|
|
198
|
-
if (planPath) {
|
|
199
|
-
const seedDir = join(
|
|
252
|
+
if (planPath && mode !== "migrate") {
|
|
253
|
+
const seedDir = join(TEMPLATES, vertical, "seed");
|
|
200
254
|
const seedName = existsSync(seedDir)
|
|
201
255
|
? readdirSync(seedDir).find((f) => f.startsWith("seed-") && f.endsWith(".mjs"))
|
|
202
256
|
: undefined;
|
|
@@ -218,22 +272,28 @@ const release = {
|
|
|
218
272
|
astro: "npx @wix/cli@latest build, then npx @wix/cli@latest release",
|
|
219
273
|
react: "the project's own build, then npx @wix/cli@latest release of the build folder named in wix.config.json (what Wix hosting serves and how routes must be shaped: SKILL.md step 1)",
|
|
220
274
|
lib: "the project's own build, then npx @wix/cli@latest release of the build folder named in wix.config.json (SKILL.md step 1)",
|
|
221
|
-
static:
|
|
275
|
+
static: `npx @wix/cli@latest release — no build; it uploads ${STATIC_OUT}/ whole (wix.config.json site.outputDirectory), so the pages, styles and assets move into ${STATIC_OUT}/ first and import the modules from ./js/wix/ there; the root keeps the config, the plan, the seed output and the skills`,
|
|
222
276
|
}[stack];
|
|
277
|
+
// On a migration preview the links and `siteId` are the PARENT's — the site whose content the pages
|
|
278
|
+
// show and whose dashboard manages it; `deploySiteId` is where `wix release` goes.
|
|
223
279
|
emit("ready_for_brand_layer", {
|
|
224
280
|
mode,
|
|
225
281
|
stack,
|
|
226
282
|
projectDir,
|
|
227
|
-
siteId,
|
|
283
|
+
siteId: ctx ? ctx.content.siteId : siteId,
|
|
284
|
+
...(ctx ? { deploySiteId: ctx.deploy.siteId, migration: { parentSiteId: ctx.migration.parentSiteId } } : {}),
|
|
228
285
|
dashboardUrl: deployResult.dashboardUrl,
|
|
229
286
|
productsUrl: deployResult.productsUrl,
|
|
230
287
|
categoriesUrl: deployResult.categoriesUrl,
|
|
231
288
|
install,
|
|
232
289
|
seed,
|
|
233
290
|
next:
|
|
234
|
-
(
|
|
291
|
+
(mode === "migrate"
|
|
292
|
+
? "a migration preview: the site being migrated owns its content (read it with the vertical's read-site.mjs when the brief allows probing; never seed it); theme + write the home page; "
|
|
293
|
+
: planPath
|
|
235
294
|
? "theme + write the home page; "
|
|
236
295
|
: "the site is new and empty — seed it (a plan per step 2, the vertical's seed module) or say so; theme + write the home page; ") +
|
|
237
296
|
(install || seed ? "then wait for the done markers" + (seed ? ", verify .seed-exit is 0 (else read seed.log and re-run the seed module)" : "") + "; " : "") +
|
|
238
|
-
`then ${release}
|
|
297
|
+
`then ${release}` +
|
|
298
|
+
(mode === "migrate" ? "; the release is the PREVIEW on the deploy site — close with its URL and the parent's dashboard, say the original site is unchanged and that completing the migration is the user's next step in the Wix CLI once they approve" : ""),
|
|
239
299
|
});
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
// Where the shipped code is. The verticals live in the skill's repository under `templates/`
|
|
2
|
+
// (each with its code layers, its tools and a composed project with a lockfile), not in the skill
|
|
3
|
+
// folder: the skill stays small and the code is fetched once, when first needed.
|
|
4
|
+
//
|
|
5
|
+
// node <SKILL_ROOT>/install/templates.mjs [--refresh] # prints {templates, source, verticals}
|
|
6
|
+
//
|
|
7
|
+
// Resolution, in order:
|
|
8
|
+
// 1. a checkout of the repository itself (the skill folder sits in it): `<repo>/templates/`.
|
|
9
|
+
// 2. the cache `<SKILL_ROOT>/templates/`, filled by an earlier call (`--refresh` refetches).
|
|
10
|
+
// 3. a fetch: a sparse, shallow clone of `templates/` from the repository the skill was installed
|
|
11
|
+
// from (skills-lock.json's `source`, default wix/skills), at the branch or tag the install
|
|
12
|
+
// named (its `ref`, falling back to the default branch when that ref no longer exists) or the
|
|
13
|
+
// repository's default branch. The lock records no commit, so there is nothing more exact to
|
|
14
|
+
// pin to; `WIX_HEADLESS_FAST_TEMPLATES_REF=<branch|tag|sha>` overrides the ref.
|
|
15
|
+
// The cache stays with the project: its `.gitignore` leaves out only the composed `project/`
|
|
16
|
+
// folders (the scaffolds with their lockfiles, read once, at create or attach) and the repository
|
|
17
|
+
// tooling, so the code layers, playbooks, seeds and readers are committed at the commit the project
|
|
18
|
+
// was built from, recorded in `.source`. A caller that needs a `project/` folder passes it as
|
|
19
|
+
// `need`; when a committed copy lacks it, that part is fetched at the same commit.
|
|
20
|
+
import { cpSync, existsSync, mkdtempSync, mkdirSync, readdirSync, readFileSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { tmpdir } from "node:os";
|
|
22
|
+
import { dirname, join, resolve } from "node:path";
|
|
23
|
+
import { spawnSync } from "node:child_process";
|
|
24
|
+
import { fileURLToPath } from "node:url";
|
|
25
|
+
|
|
26
|
+
export const SKILL_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), "..");
|
|
27
|
+
export const DEFAULT_REPO = "https://github.com/wix/skills.git";
|
|
28
|
+
|
|
29
|
+
const git = (args, opts = {}) => spawnSync("git", args, { encoding: "utf8", timeout: 180_000, ...opts });
|
|
30
|
+
|
|
31
|
+
// The repository and ref the skill was installed from: the nearest skills-lock.json above the
|
|
32
|
+
// skill folder, its entry for this skill. "wix/skills" → the default branch of that repository;
|
|
33
|
+
// "https://github.com/owner/repo/tree/<ref>" → that ref.
|
|
34
|
+
export function installSource() {
|
|
35
|
+
const skill = SKILL_ROOT.split("/").pop();
|
|
36
|
+
let dir = SKILL_ROOT;
|
|
37
|
+
for (let i = 0; i < 6; i++) {
|
|
38
|
+
const p = join(dir, "skills-lock.json");
|
|
39
|
+
if (existsSync(p)) {
|
|
40
|
+
try {
|
|
41
|
+
const entry = JSON.parse(readFileSync(p, "utf8")).skills?.[skill];
|
|
42
|
+
if (typeof entry?.source === "string" && entry.source) {
|
|
43
|
+
const parsed = parseSource(entry.source);
|
|
44
|
+
// `ref` is the branch or tag the install named (skills-lock.json v1 keeps it beside `source`)
|
|
45
|
+
return { ...parsed, ref: parsed.ref ?? (typeof entry.ref === "string" && entry.ref ? entry.ref : null) };
|
|
46
|
+
}
|
|
47
|
+
} catch { /* fall through to the default */ }
|
|
48
|
+
break;
|
|
49
|
+
}
|
|
50
|
+
const up = dirname(dir);
|
|
51
|
+
if (up === dir) break;
|
|
52
|
+
dir = up;
|
|
53
|
+
}
|
|
54
|
+
return { repo: DEFAULT_REPO, ref: null };
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parseSource(src) {
|
|
58
|
+
const m = src.match(/^(?:https?:\/\/github\.com\/)?([^/\s]+)\/([^/\s#@]+?)(?:\.git)?(?:\/tree\/([^\s]+))?$/);
|
|
59
|
+
if (!m) return { repo: DEFAULT_REPO, ref: null };
|
|
60
|
+
return { repo: `https://github.com/${m[1]}/${m[2]}.git`, ref: m[3] ?? null };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function listVerticals(dir) {
|
|
64
|
+
return readdirSync(dir, { withFileTypes: true })
|
|
65
|
+
.filter((d) => d.isDirectory() && d.name !== "shared" && d.name !== "blank" && existsSync(join(dir, d.name, "app")))
|
|
66
|
+
.map((d) => d.name)
|
|
67
|
+
.sort();
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function templatesDir({ refresh = false, need = null } = {}) {
|
|
71
|
+
const checkout = resolve(SKILL_ROOT, "..", "..", "templates");
|
|
72
|
+
if (existsSync(join(checkout, "shared", "app"))) return checkout;
|
|
73
|
+
const cache = join(SKILL_ROOT, "templates");
|
|
74
|
+
if (!refresh && existsSync(join(cache, "shared", "app"))) {
|
|
75
|
+
if (need && !existsSync(join(cache, need))) fetchIgnoredPart(cache, need);
|
|
76
|
+
return cache;
|
|
77
|
+
}
|
|
78
|
+
fetchTemplates(cache);
|
|
79
|
+
return cache;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// What a project's repository does not carry: the composed scaffolds (heavy, read once) and the
|
|
83
|
+
// repository's own tooling. Everything else in the cache is committed with the project.
|
|
84
|
+
const IGNORED = ["*/project/", "blank/", "compose.mjs"];
|
|
85
|
+
|
|
86
|
+
// A committed copy lacks the ignored parts. Fetch them at the commit the copy came from, so the
|
|
87
|
+
// scaffold a create copies matches the code committed beside it; the committed files are untouched.
|
|
88
|
+
function fetchIgnoredPart(cache, need) {
|
|
89
|
+
const src = templatesSource(cache);
|
|
90
|
+
const { repo } = installSource();
|
|
91
|
+
const r = cloneSparse(src.repo ?? repo, src.commit ?? process.env.WIX_HEADLESS_FAST_TEMPLATES_REF ?? null);
|
|
92
|
+
const tmp = r.tmp;
|
|
93
|
+
if (r.status !== 0 || !existsSync(join(tmp, "templates", need))) {
|
|
94
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
95
|
+
throw new Error(`could not fetch templates/${need} from ${src.repo ?? repo}${src.commit ? ` @ ${src.commit.slice(0, 7)}` : ""}: ${(r.stderr || r.stdout || "not in the clone").trim().slice(-300)}`);
|
|
96
|
+
}
|
|
97
|
+
for (const part of ["blank", ...readdirSync(join(tmp, "templates"), { withFileTypes: true }).filter((d) => d.isDirectory() && existsSync(join(tmp, "templates", d.name, "project"))).map((d) => `${d.name}/project`)]) {
|
|
98
|
+
const from = join(tmp, "templates", part);
|
|
99
|
+
if (existsSync(from) && !existsSync(join(cache, part))) cpSync(from, join(cache, part), { recursive: true });
|
|
100
|
+
}
|
|
101
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function templatesSource(dir) {
|
|
105
|
+
const p = join(dir, ".source");
|
|
106
|
+
if (existsSync(p)) { try { return JSON.parse(readFileSync(p, "utf8")); } catch { /* below */ } }
|
|
107
|
+
return { checkout: dir };
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function fetchTemplates(cache) {
|
|
111
|
+
const { repo, ref: lockRef } = installSource();
|
|
112
|
+
const envRef = process.env.WIX_HEADLESS_FAST_TEMPLATES_REF || null;
|
|
113
|
+
let ref = envRef || lockRef || null;
|
|
114
|
+
let r = cloneSparse(repo, ref);
|
|
115
|
+
// The branch the skill was installed from can be gone by the time the code is first needed
|
|
116
|
+
// (merged and deleted). The lock's ref then falls back to the default branch; an explicit env
|
|
117
|
+
// ref does not.
|
|
118
|
+
let fellBack = false;
|
|
119
|
+
if (r.status !== 0 && ref && !envRef && /not found|couldn't find remote ref|unknown revision/i.test(r.stderr || "")) {
|
|
120
|
+
rmSync(r.tmp, { recursive: true, force: true });
|
|
121
|
+
fellBack = true; ref = null;
|
|
122
|
+
r = cloneSparse(repo, null);
|
|
123
|
+
}
|
|
124
|
+
const tmp = r.tmp;
|
|
125
|
+
if (r.error?.code === "ENOENT") {
|
|
126
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
127
|
+
throw new Error("git is not installed or not on PATH; the shipped code is fetched with a git clone of the skill's repository");
|
|
128
|
+
}
|
|
129
|
+
if (r.status !== 0 || !existsSync(join(tmp, "templates", "shared", "app"))) {
|
|
130
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
131
|
+
throw new Error(`could not fetch templates/ from ${repo}${ref ? ` @ ${ref}` : ""}: ${(r.stderr || r.stdout || "no templates/shared/app in the clone").trim().slice(-400)}`);
|
|
132
|
+
}
|
|
133
|
+
const commit = git(["-C", tmp, "rev-parse", "HEAD"]).stdout.trim();
|
|
134
|
+
rmSync(cache, { recursive: true, force: true });
|
|
135
|
+
mkdirSync(dirname(cache), { recursive: true });
|
|
136
|
+
try { renameSync(join(tmp, "templates"), cache); }
|
|
137
|
+
catch { cpSync(join(tmp, "templates"), cache, { recursive: true }); }
|
|
138
|
+
rmSync(tmp, { recursive: true, force: true });
|
|
139
|
+
writeFileSync(join(cache, ".gitignore"), IGNORED.join("\n") + "\n");
|
|
140
|
+
writeFileSync(join(cache, ".source"), JSON.stringify({ repo, ref: ref ?? (fellBack ? `default branch (${lockRef} not found)` : "default branch"), commit, fetchedAt: new Date().toISOString() }, null, 2) + "\n");
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// A sparse, shallow clone of templates/ at a ref (branch, tag, or commit id) into a temp dir.
|
|
144
|
+
function cloneSparse(repo, ref) {
|
|
145
|
+
const tmp = mkdtempSync(join(tmpdir(), "wix-headless-fast-templates-"));
|
|
146
|
+
let r;
|
|
147
|
+
if (ref && /^[0-9a-f]{40}$/i.test(ref)) {
|
|
148
|
+
// a commit: shallow-fetch just it (GitHub serves any reachable commit by id)
|
|
149
|
+
for (const args of [["init", "-q", tmp], ["-C", tmp, "remote", "add", "origin", repo], ["-C", tmp, "fetch", "-q", "--depth", "1", "--filter=blob:none", "origin", ref], ["-C", tmp, "sparse-checkout", "set", "templates"], ["-C", tmp, "checkout", "-q", "FETCH_HEAD"]]) {
|
|
150
|
+
r = git(args);
|
|
151
|
+
if (r.status !== 0) break;
|
|
152
|
+
}
|
|
153
|
+
} else {
|
|
154
|
+
r = git(["clone", "--quiet", "--depth", "1", "--filter=blob:none", "--sparse", ...(ref ? ["-b", ref] : []), repo, tmp]);
|
|
155
|
+
if (r.status === 0) r = git(["-C", tmp, "sparse-checkout", "set", "templates"]);
|
|
156
|
+
}
|
|
157
|
+
return { ...r, tmp };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
|
|
161
|
+
try {
|
|
162
|
+
const dir = templatesDir({ refresh: process.argv.includes("--refresh") });
|
|
163
|
+
console.log(JSON.stringify({ templates: dir, source: templatesSource(dir), verticals: listVerticals(dir) }));
|
|
164
|
+
} catch (e) {
|
|
165
|
+
console.log(JSON.stringify({ error: e.message }));
|
|
166
|
+
process.exit(1);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
@@ -163,7 +163,7 @@ Create and modify CMS collection structures. Covers listing collections, creatin
|
|
|
163
163
|
Deletes multiple contacts using filter-based bulk delete. Covers safe deletion patterns, GDPR compliance, soft delete alternatives, and batch processing strategies.
|
|
164
164
|
|
|
165
165
|
### [Bulk Label and Unlabel Contacts](references/contacts/bulk-label-and-unlabel-contacts.md)
|
|
166
|
-
|
|
166
|
+
Creates contact label definitions or adds/removes labels from matching contacts. Use Find or Create Label for label creation alone; use bulk labeling only when the user requests contact assignments.
|
|
167
167
|
|
|
168
168
|
### [Create a Contact](references/contacts/create-a-contact.md)
|
|
169
169
|
Creates a contact with the Contacts API. Covers the minimum identifying fields, the single-object shape of `email` and `phone`, and adding a physical address with the ISO 3166-2 subdivision format required for state, region, and province codes.
|
|
@@ -211,7 +211,7 @@ Buy a domain through Wix or connect one the user already owns — intent, availa
|
|
|
211
211
|
**Entry point for all eCommerce recommendation requests.** Unified skill that analyzes site data across ALL domains (discounts + shipping + gift cards), generates up to 5 cross-domain recommendations, and persists them to the tracking database. Covers discount strategies (seasonal, upsell, stock mover, bundling), shipping optimization (coverage gaps, free shipping, rate strategy, carrier backup), AND selling gift cards (denominations sized from the site's own AOV and catalog prices). Use this for business improvement requests about earning more from existing visitors. **Traffic acquisition (SEO, ads, social, content) is out of scope** — route "grow my traffic" to marketing.
|
|
212
212
|
|
|
213
213
|
### [Pricing & Promotions](references/ecommerce/ecom-pricing.md)
|
|
214
|
-
|
|
214
|
+
Routes discounts, coupons, sales and bundles to promotion recipes, and visual product ribbons to the Catalog Ribbons API. Load this dispatcher for mixed pricing/refund/payment/product-price/shipping requests to choose the appropriate APIs.
|
|
215
215
|
|
|
216
216
|
### [Shipping](references/ecommerce/ecom-shipping.md)
|
|
217
217
|
**Dispatcher** — routes shipping-setup requests (rates, regions, pickup, free shipping, fix coverage, optimize rates) to the right leaf recipe. The Shipping Options + Delivery Profiles APIs have no public docs page; `ecom-shipping-api.md` is the authoritative inline reference.
|
|
@@ -422,7 +422,7 @@ List, count, and find the sites in a Wix account. Covers the namespace filter fo
|
|
|
422
422
|
Probe a Wix site or account for full context in one call — installed apps by display name, locale, currency, timezone, and status. Account token + siteId targets one site; account token alone returns up to 10; site-scoped token alone returns the site it is scoped to.
|
|
423
423
|
|
|
424
424
|
### [Site Import](references/sites/site-import.md)
|
|
425
|
-
Drive the Wix Site Import agent to migrate an existing store or site from another platform (Shopify, WooCommerce, Magento, or any URL) into Wix
|
|
425
|
+
Drive the Wix Site Import agent to migrate an existing store or site from another platform (Shopify, WooCommerce, Magento, or any URL) into Wix — as a brand-new site or into the user's existing one — or to import from CSV/TSV export files with no source site. Use this skill whenever the user wants to import, migrate, or clone a store/site into Wix, mentions moving off Shopify/WooCommerce/Magento, or gives a source store URL and asks to bring it into Wix. Covers starting the import, polling progress, answering the agent's mid-import questions, handling deploy/failure/auth-expiry states, and sending post-deploy follow-up changes.
|
|
426
426
|
|
|
427
427
|
### [Sites Dashboard Navigation](references/sites/sites-dashboard-navigation.md)
|
|
428
428
|
"Builds direct links to the account-level sites pages on manage.wix.com — the My Sites list (all sites in the account) and each site's own dashboard. Pairs the site list with the Query Sites read API. Use when the user asks where something is in the Wix dashboard, wants a direct link to a dashboard page, or you need a dashboard URL to include with the result of an API operation."
|
|
@@ -107,15 +107,81 @@ Some common apps:
|
|
|
107
107
|
- NEVER guess the `appDefId`. For Wix-built apps, use the table above. For any other app, resolve the ID using Step 0 (Search Market Listings).
|
|
108
108
|
- The `tenantType` MUST be `SITE`
|
|
109
109
|
- The `id` in tenant is the site's metaSiteId
|
|
110
|
+
- The endpoint, request body, and appDefId table above are complete and canonical for a listed Wix-built app — don't re-verify them with a separate REST/API doc search first; that's a redundant round-trip.
|
|
111
|
+
- Don't spend a call checking whether the app is already installed before installing it. If the task or the error you're handling already tells you it isn't installed (e.g. a fresh site, or a `*_NOT_INSTALLED` error), just call install directly.
|
|
110
112
|
|
|
111
113
|
---
|
|
112
114
|
|
|
113
115
|
## Error Handling
|
|
114
116
|
|
|
115
117
|
### App Not Installed Error
|
|
116
|
-
If you receive an error indicating a required app is not installed,
|
|
118
|
+
If you receive an error indicating a required app is not installed (e.g. a `428` or a `*_NOT_INSTALLED` error code), install the app it names using its appDefId, then retry the call that failed. If the user's own request already implies this app is needed — i.e. doing what they asked requires a feature only that app provides — just install it as part of doing that task; don't stop to ask permission or name the app first. Confirm first only when the app isn't implied by anything the user asked for.
|
|
119
|
+
|
|
120
|
+
**Example** (install the missing app, then retry the call that surfaced the error — all in one script):
|
|
121
|
+
```javascript
|
|
122
|
+
async function() {
|
|
123
|
+
const siteId = "<SITE_ID>";
|
|
124
|
+
|
|
125
|
+
await wix.request({
|
|
126
|
+
method: "POST",
|
|
127
|
+
url: "https://www.wixapis.com/apps-installer-service/v1/app-instance/install",
|
|
128
|
+
body: {
|
|
129
|
+
tenant: { tenantType: "SITE", id: siteId },
|
|
130
|
+
appInstance: { appDefId: "<APP_DEF_ID>" }
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
return await wix.request({
|
|
135
|
+
method: "<METHOD>",
|
|
136
|
+
url: "<ORIGINAL_URL_THAT_FAILED>",
|
|
137
|
+
body: { /* original request body */ }
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### App-Dependent Call Fails Right After Install (Propagation Delay)
|
|
143
|
+
Installing an app and then immediately calling one of its own APIs — e.g. calling Set Multilingual Mode right after installing Wix Multilingual — can race the platform's install propagation, surfacing as a not-found error on the dependent call even though the install itself already succeeded.
|
|
144
|
+
|
|
145
|
+
**Example** (retry with backoff past the propagation delay instead of surfacing the error, all in one script):
|
|
146
|
+
```javascript
|
|
147
|
+
async function() {
|
|
148
|
+
const siteId = "<SITE_ID>";
|
|
149
|
+
|
|
150
|
+
async function requestWithRetry(requestOptions, delaysMs = [1000, 2000, 4000]) {
|
|
151
|
+
let lastError;
|
|
152
|
+
try {
|
|
153
|
+
return await wix.request(requestOptions);
|
|
154
|
+
} catch (err) {
|
|
155
|
+
lastError = err;
|
|
156
|
+
}
|
|
157
|
+
for (const delayMs of delaysMs) {
|
|
158
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
159
|
+
try {
|
|
160
|
+
return await wix.request(requestOptions);
|
|
161
|
+
} catch (err) {
|
|
162
|
+
lastError = err;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
throw lastError;
|
|
166
|
+
}
|
|
117
167
|
|
|
118
|
-
|
|
168
|
+
await wix.request({
|
|
169
|
+
method: "POST",
|
|
170
|
+
url: "https://www.wixapis.com/apps-installer-service/v1/app-instance/install",
|
|
171
|
+
body: {
|
|
172
|
+
tenant: { tenantType: "SITE", id: siteId },
|
|
173
|
+
appInstance: { appDefId: "14d84998-ae09-1abf-c6fc-3f3cace5bf19" }
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
return await requestWithRetry({
|
|
178
|
+
method: "POST",
|
|
179
|
+
url: "https://www.wixapis.com/locale-settings/v2/settings/mode",
|
|
180
|
+
body: { multilingualModeEnabled: true }
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
Chain any further calls inside this same function through the same `requestWithRetry` helper, after the first one succeeds.
|
|
119
185
|
|
|
120
186
|
---
|
|
121
187
|
|
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Bulk Label and Unlabel Contacts"
|
|
3
|
-
description:
|
|
3
|
+
description: Creates contact label definitions or adds/removes labels from matching contacts. Use Find or Create Label for label creation alone; use bulk labeling only when the user requests contact assignments.
|
|
4
4
|
---
|
|
5
5
|
# Bulk Label And Unlabel Contacts
|
|
6
6
|
|
|
7
7
|
## Description
|
|
8
8
|
Adds and removes labels from multiple contacts using the Wix Contacts REST API.
|
|
9
9
|
|
|
10
|
+
## Create labels or assign labels?
|
|
11
|
+
|
|
12
|
+
If the user asks to **create a label** (for example, "Create VIP, Wholesale, and Newsletter labels"), use [Find or Create Label](https://dev.wix.com/docs/api-reference/crm/members-contacts/contacts/labels/find-or-create-label) directly. Creating a label definition does not require finding contacts or starting a bulk labeling job.
|
|
13
|
+
|
|
14
|
+
```http
|
|
15
|
+
POST https://www.wixapis.com/contacts/v4/labels
|
|
16
|
+
Content-Type: application/json
|
|
17
|
+
|
|
18
|
+
{"displayName":"VIP"}
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
Use the returned `label` and its `key`; `newLabel` distinguishes a newly created label from an existing one. For several names, make one call per name and check each result. Stop here when the request is only to create labels: do not assign them to contacts.
|
|
22
|
+
|
|
23
|
+
If the user asks to **label or unlabel contacts**, resolve the requested label keys first, then follow the bulk assignment flow below. Create a missing label only when that is part of the authorized request. An explicit request to create labels or apply them to a specified set of contacts authorizes that operation; otherwise confirm the target and intended change before mutating.
|
|
24
|
+
|
|
25
|
+
## Bulk assignment flow
|
|
26
|
+
|
|
10
27
|
Labels are added to and removed from all contacts that meet the specified `filter` and `search` criteria.
|
|
11
28
|
The request should specify a `filter` value, a `search` value, or both.
|
|
12
29
|
To perform a dry run, call [Query Contacts](https://dev.wix.com/docs/api-reference/crm/members-contacts/contacts/contacts/contact-v4/query-contacts) with the intended filter options.
|
|
@@ -17,6 +34,22 @@ The job's status can be retrieved with [Get Bulk Job](https://dev.wix.com/docs/a
|
|
|
17
34
|
|
|
18
35
|
**IMPORTANT NOTE:** When specific contacts are to be labeled, they should be filtered by id.
|
|
19
36
|
|
|
37
|
+
### Steps
|
|
38
|
+
|
|
39
|
+
1. **Resolve label keys.** When adding, Find or Create Label (above) returns the `key`. To remove a label, or to
|
|
40
|
+
use only a label that already exists, look it up by name instead of creating it:
|
|
41
|
+
`POST https://www.wixapis.com/contacts/v4/labels/query` with
|
|
42
|
+
`{"query":{"filter":{"displayName":{"$eq":"Newsletter"}}}}`, and take each returned label's `key`.
|
|
43
|
+
2. **Resolve named contacts to IDs** with Search Contacts, as in
|
|
44
|
+
[Update a Contact](update-a-contact.md) — Query Contacts cannot filter on a name:
|
|
45
|
+
`POST https://www.wixapis.com/contacts/v5/contacts/search` with `{"search":{"search":{"expression":"Leo Marsh"}}}`.
|
|
46
|
+
Each result in `contacts` has an `id` and `name.first` / `name.last`; keep only exact name matches. If none or
|
|
47
|
+
more than one contact matches, stop and ask the user.
|
|
48
|
+
3. **Start the job** with the endpoint below, filtering by the resolved IDs:
|
|
49
|
+
`{"filter":{"id":{"$in":["<CONTACT_ID>"]}},"labelKeysToAdd":["<LABEL_KEY>"]}` (or `labelKeysToRemove`).
|
|
50
|
+
4. **Confirm the job finished** with `GET https://www.wixapis.com/contacts/v4/bulk/jobs/{jobId}`; repeat until
|
|
51
|
+
`job.status` is `COMPLETED`, then report `job.successTotal` and `job.failedTotal`.
|
|
52
|
+
|
|
20
53
|
## API Endpoint
|
|
21
54
|
`POST https://www.wixapis.com/contacts/v4/bulk/contacts/add-remove-labels`
|
|
22
55
|
|
|
@@ -29,10 +62,10 @@ curl -X POST \
|
|
|
29
62
|
-H 'Content-Type: application/json' \
|
|
30
63
|
-d '{
|
|
31
64
|
"filter": {
|
|
32
|
-
"
|
|
65
|
+
"id": { "$in": ["<CONTACT_ID_1>", "<CONTACT_ID_2>"] }
|
|
33
66
|
},
|
|
34
|
-
"labelKeysToAdd": ["custom.
|
|
35
|
-
"labelKeysToRemove": ["custom.
|
|
67
|
+
"labelKeysToAdd": ["custom.newsletter"],
|
|
68
|
+
"labelKeysToRemove": ["custom.prospect"]
|
|
36
69
|
}'
|
|
37
70
|
```
|
|
38
71
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: "Pricing & Promotions"
|
|
3
|
-
description:
|
|
3
|
+
description: Routes discounts, coupons, sales and bundles to promotion recipes, and visual product ribbons to the Catalog Ribbons API. Load this dispatcher for mixed pricing/refund/payment/product-price/shipping requests to choose the appropriate APIs.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Pricing & Promotions
|
|
@@ -14,7 +14,9 @@ Discount rules, coupon codes, sales, ribbons, bundles, tiered pricing, and the s
|
|
|
14
14
|
- A standing $0 shipping option/region rate → see **Shipping & fulfillment**.
|
|
15
15
|
- Refunding a previous discounted order → route to verified Get Paid/payment docs or Dashboard guidance.
|
|
16
16
|
|
|
17
|
-
> **
|
|
17
|
+
> **Ribbon-only requests** — go directly to the ribbon path below; skip MerchantContext and promotion-strategy discovery.
|
|
18
|
+
>
|
|
19
|
+
> **Before dispatching promotion requests** — confirm MerchantContext is loaded. If `siteData.country` is not in your conversation context, load it via [Load Merchant Context](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/e-commerce-load-context). Skip if already loaded.
|
|
18
20
|
>
|
|
19
21
|
> **Promotion dispatch.** Score each entry below by (a) the merchant's query → `intent:*` tags, (b) MerchantContext → context tags. Load the **highest-scoring** entry. Ties → highest `priority`. No match → follow the base recipe at the bottom.
|
|
20
22
|
>
|
|
@@ -24,9 +26,33 @@ Discount rules, coupon codes, sales, ribbons, bundles, tiered pricing, and the s
|
|
|
24
26
|
|
|
25
27
|
> - [Create coupon](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/pricing-create-coupon) — tags: `[intent:create-coupon]` · priority 0
|
|
26
28
|
> - [Create discount rule (auto-apply)](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/pricing-create-discount-rule) — tags: `[intent:create-discount-rule]` · priority 0
|
|
27
|
-
> - [
|
|
29
|
+
> - [Create or reuse a Catalog V3 ribbon](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/ribbons-v3/get-or-create-ribbon) — tags: `[intent:add-ribbon]` · priority 0 · *a ribbon is a visual product label, not a discount rule; follow the ribbon path below*
|
|
28
30
|
> - [Schedule a future sale](https://dev.wix.com/docs/api-reference/business-solutions/e-commerce/skills/pricing-create-discount-rule) — tags: `[intent:schedule-sale]` · priority 0 · *uses Discount Rules with `startTime` in the future*
|
|
29
31
|
|
|
32
|
+
### Ribbon path
|
|
33
|
+
|
|
34
|
+
For a Catalog V3 request such as "Create a Bestseller ribbon", use the idempotent name-based [Get or Create Ribbon](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/ribbons-v3/get-or-create-ribbon) method:
|
|
35
|
+
|
|
36
|
+
```http
|
|
37
|
+
POST https://www.wixapis.com/stores/v3/ribbons/get-or-create
|
|
38
|
+
Content-Type: application/json
|
|
39
|
+
|
|
40
|
+
{"ribbonName":"Bestseller"}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Read the returned `ribbon.id`. If the user requested only the ribbon definition, report that result and stop. If they also requested assignment to a product, read the product's current `revision` with `GET https://www.wixapis.com/stores/v3/products/{productId}`, as in [Update Product with Options](../stores/update-product-with-options.md). Then send only the ribbon with that `revision` through [Update Product](https://dev.wix.com/docs/api-reference/business-solutions/stores/catalog-v3/products-v3/update-product); fields you omit are left unchanged:
|
|
44
|
+
|
|
45
|
+
```http
|
|
46
|
+
PATCH https://www.wixapis.com/stores/v3/products/{productId}
|
|
47
|
+
Content-Type: application/json
|
|
48
|
+
|
|
49
|
+
{"product":{"id":"<PRODUCT_ID>","revision":"<CURRENT_REVISION>","ribbon":{"id":"<RIBBON_ID>"}}}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
This path covers Catalog V3.
|
|
53
|
+
|
|
54
|
+
Do not create a coupon, discount rule, or price change just because a ribbon says "Sale". Those require a separate discount request. An explicit ribbon creation or assignment request authorizes that operation; otherwise confirm the intended change first. Merchant context needed for promotion recommendations is not a prerequisite to creating a named visual ribbon.
|
|
55
|
+
|
|
30
56
|
### Business flows — the orchestrator
|
|
31
57
|
|
|
32
58
|
The single business-flow orchestrator (`recommend-ecommerce-strategy`) handles all strategic discount intents. It classifies internally (SEASONAL / UPSELL_BOOST / STOCK_MOVER / BUNDLE_AND_SAVE / ABANDONED_CART) and loads its `goal-*` / `flow-*` support files from the kept ecommerce-root siblings.
|