@wix/agent-skills 1.24.0 → 1.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +1 -0
  2. package/package.json +2 -1
  3. package/skills/wix-app/SKILL.md +16 -14
  4. package/skills/wix-app/references/BUSINESS_MANAGER_THEME.md +107 -0
  5. package/skills/wix-app/references/BUSINESS_MANAGER_TOKENS.md +81 -0
  6. package/skills/wix-app/references/DASHBOARD_MODAL.md +41 -0
  7. package/skills/wix-app/references/DASHBOARD_PAGE.md +13 -11
  8. package/skills/wix-app/references/DASHBOARD_PLUGIN.md +19 -4
  9. package/skills/wix-app/references/WIX_PATTERNS_DOCS.md +3 -2
  10. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE.md +66 -68
  11. package/skills/wix-app/references/dashboard-page/DYNAMIC_PARAMETERS.md +21 -38
  12. package/skills/wix-app/references/data-collection/LIFECYCLE.md +6 -4
  13. package/skills/wix-headless/SKILL.md +0 -1
  14. package/skills/wix-headless/references/CAPABILITIES.md +3 -3
  15. package/skills/wix-headless/references/DISCOVERY.md +1 -1
  16. package/skills/wix-headless/references/SDK_HANDOFF.md +1 -1
  17. package/skills/wix-headless/references/SEED.md +1 -1
  18. package/skills/wix-headless/references/SETUP.md +1 -1
  19. package/skills/wix-headless/references/inline-recipes/how-to-code-restaurant-reservations.md +1 -1
  20. package/skills/wix-headless/references/inline-recipes/setup-restaurant-experiences.md +2 -2
  21. package/skills/wix-headless/references/managed/AUTHENTICATION.md +3 -14
  22. package/skills/wix-headless/scripts/bootstrap.mjs +156 -0
  23. package/skills/wix-headless-kit/SKILL.md +4 -4
  24. package/skills/wix-headless-kit/install/AGENTS.md +2 -2
  25. package/skills/wix-headless-kit/install/bootstrap.mjs +156 -0
  26. package/skills/wix-headless-kit/install/check.mjs +1 -1
  27. package/skills/wix-manage/SKILL.md +2 -8
  28. package/skills/wix-manage/references/cms/cms-data-items-crud.md +250 -18
  29. package/skills/wix-manage/references/cms/cms-schema-management.md +52 -1
  30. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_COLLECTION.md +0 -184
  31. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_ROUTER.md +0 -203
  32. package/skills/wix-app/references/dashboard-page/DRAFT_TEMPLATE_SETTINGS.md +0 -44
  33. package/skills/wix-manage/references/cms/cms-data-operations-extended.md +0 -263
  34. package/skills/wix-manage/references/cms/cms-references-and-relationships.md +0 -162
@@ -1,97 +1,95 @@
1
1
  # Draft Template — the starting point for every dashboard page
2
2
 
3
- **Start here for any Dashboard Page request, before writing a shell, provider, or router from scratch.** Pick the case below, copy its files, rename, and adapt fields/API calls/data source. Only leave this file when the request needs something no case shows, and then go to [WIX_PATTERNS_DOCS.md](../WIX_PATTERNS_DOCS.md) first — before any lookup inside `node_modules/@wix/patterns`, `index.json` included. It routes you to the package's own `Collection Toolkit.md` guide, a component doc or an example; [TABLE_STATE.md](TABLE_STATE.md) and MCP lookups come after it.
3
+ **Start here for any Dashboard Page request, before writing a shell, provider, or router from scratch.** The templates themselves ship inside the installed `@wix/patterns`: whole, working pages that the package type-checks against its own release, so they never drift from the version your code compiles against. This file says how to find one, where its files go in a Wix CLI app, and what this skill adds on top. Which template fits, and the wiring each one must keep, is the package's answer — read it there.
4
4
 
5
- Every snippet below was copied from the installed `dist/docs/*.md` and `dist/dts-bundle/*.d.ts`, not from memory — confirm props against your own installed version before deviating.
5
+ Go through [WIX_PATTERNS_DOCS.md § Prerequisites](../WIX_PATTERNS_DOCS.md#prerequisites) first for `<pkgRoot>`; every path below is relative to it.
6
6
 
7
- ## Which case matches the request?
7
+ ## 1. Find the templates
8
8
 
9
- | The request needs… | Case | Router? |
10
- | --- | --- | --- |
11
- | A list/report whose rows are read-only — no create/edit form | **A — Collection + read-only detail** | Yes |
12
- | A list **and** create/edit for each record (no separate app-settings area) | **B — Collection + Entity** | Yes |
13
- | Only app-wide settings/config — no list at all | **C — Settings only** | No |
14
- | A list, create/edit, **and** an app-settings area, all in one extension | **D — Collection + Entity + Settings** | Yes |
9
+ Templates are docs-index entries with `category: "Templates"`, and each lists every file of its page in `templateFiles` (relative to `dist/templates/`). One probe lists them all with the guide that chooses between them:
15
10
 
16
- Don't default to D because it's the most complete — [Step 4c's checklist](../../SKILL.md#step-4c-ux-completeness-self-audit) doesn't ask for a settings or entity page unless the request needs one. If unsure between B and D, re-read the prompt for "settings," "configure," "preferences" — their absence means B.
17
-
18
- ## Then: what `fetchData` calls
19
-
20
- Case tells you how many pages. This tells you only what `fetchData` calls — the page itself is
21
- [DRAFT_TEMPLATE_COLLECTION.md](DRAFT_TEMPLATE_COLLECTION.md) either way:
11
+ ```bash
12
+ python3 -c "
13
+ import json; i = json.load(open('<pkgRoot>/dist/docs/index.json'))
14
+ for k, e in i.items():
15
+ if e.get('category') == 'Templates' or e.get('relatedTemplates'):
16
+ print(k, '|', e.get('templateFiles') or e.get('relatedTemplates'), '|', e.get('summary', '')[:200])
17
+ "
18
+ ```
22
19
 
23
- | The rows come from | `fetchData` calls |
24
- | --- | --- |
25
- | A vertical SDK (`@wix/bookings`, `@wix/ecom`, …) or any API you call yourself | that SDK's own query method — [DATA_SOURCES.md](DATA_SOURCES.md) |
26
- | A **CMS collection** — one your Data Collection extension ships, or an existing site collection | `@wix/data` `items.query()` — [WIX_DATA.md](../data-collection/WIX_DATA.md) |
20
+ **No `Templates` entries means the install predates them — upgrade `@wix/patterns`.** Do not rebuild a page from memory or from an older copy of this skill instead: the templates exist so the shell, provider nesting and router wiring come from the package.
27
21
 
28
- A CMS collection is not a different template, just a different call inside the same one: you write
29
- `fetchData`, the filters and the columns for both.
22
+ ## 2. Choose, then read the chosen template's page
30
23
 
31
- **Cases B and D both need a router** — their entry file, app shell, and entity page are in [DRAFT_TEMPLATE_ROUTER.md](DRAFT_TEMPLATE_ROUTER.md). The collection and settings files are shared by every case that uses them, B and D included; the router file links back rather than repeating them.
24
+ 1. `Read <pkgRoot>/dist/docs/Page Templates.md` — which pages the request needs (read-only list, list with create/edit, settings only, or list with create/edit and settings) and where the rows come from (your own fetch, or a CMS collection).
25
+ 2. `Read` the chosen template's page (`dist/docs/<entry file>`) — its files, and the wiring to keep. Most of that wiring passes `tsc` and `wix build` when wrong and fails only in the browser, which is why [Step 5's Preview](../../SKILL.md#validation) is not optional for a routed page.
32
26
 
33
- ## File layout
27
+ Take the smallest template that covers what the request names. A settings area or an edit form nobody asked for is not "more complete" — [Step 4c's checklist](../../SKILL.md#step-4c-ux-completeness-self-audit) does not ask for one.
34
28
 
35
- ```
36
- src/extensions/dashboard/pages/{feature}/
37
- {feature}.extension.ts # single wix generate scaffold — always exactly one route registered here
38
- {feature}.tsx # entry — Case C: Section 1 below. Cases A/B/D: DRAFT_TEMPLATE_ROUTER.md
39
- {Feature}App.tsx # Case A/B/D only — see DRAFT_TEMPLATE_ROUTER.md
40
- {Feature}CollectionPage.tsx # Case A, B, D — see DRAFT_TEMPLATE_COLLECTION.md
41
- {Feature}DetailPage.tsx # Case A only — read-only detail route, DRAFT_TEMPLATE_ROUTER.md §4
42
- {Feature}EntityPage.tsx # Case B, D only — see DRAFT_TEMPLATE_ROUTER.md
43
- {Feature}SettingsPage.tsx # Case C, D only — see DRAFT_TEMPLATE_SETTINGS.md
44
- {feature}-api.ts # fetch/save calls — keep these out of the components
45
- ```
29
+ ## 3. Copy it into the extension
46
30
 
47
- Scaffold with a single call regardless of case — this is always one extension:
31
+ Scaffold once — every template is one extension, however many routes it has:
48
32
 
49
33
  ```bash
50
34
  wix generate --params '{"extensionType":"DASHBOARD_PAGE","title":"<title>","route":"<route>"}'
51
35
  ```
52
36
 
53
- ## 1. Entry — Case C only (router-free, no location plumbing)
37
+ `route` takes no leading slash — `support-tickets`, not `/support-tickets`. See [Dashboard Page → Scaffold](../DASHBOARD_PAGE.md#scaffold).
38
+
39
+ Then copy **every** file in `templateFiles` into the generated folder (`src/extensions/dashboard/pages/<feature>/`). They import one another by relative path, so keep their names relative to each other; one file on its own does not compile.
40
+
41
+ - The template's `page.tsx` is the entry file, and the CLI generated none by that name: put its contents into the component file the builder's `component` path points at (e.g. `employee-shifts.tsx`), and add no separate `page.tsx`, which nothing loads. Leave the builder file and the `src/extensions.ts` registration as the CLI wrote them.
42
+ - Rename `Items` / `Item` to your feature and entity, in file names and identifiers alike.
43
+ - Route paths stay page-relative (`/`, `/:id`, `/new`) — never prefix them with `<route>` ([DASHBOARD_PAGE.md](../DASHBOARD_PAGE.md)).
44
+
45
+ - **Theme the entry file.** In it, replace the template's `WixDesignSystemProvider` and its `@wix/design-system/styles.global.css` import with the app's `BusinessManagerTheme` — written once per app ([BUSINESS_MANAGER_THEME.md § 2](../BUSINESS_MANAGER_THEME.md#2-the-wrapper--write-this-file-once-per-app)), kept in the entry file so it sits above `WixPatternsProvider`. **Business Manager passes none of the redesign through the extension's iframe**, so the bare provider renders the pre-redesign look while `tsc`, `wix build` and `wix preview` all pass.
46
+
47
+ ## 4. Replace the data file
54
48
 
55
- Case C has one page and no rows to open, so it needs no router and no manual `location` wiring. **Cases A, B and D all route** — a row opens a page of its own in every one of them — so their entry file is [DRAFT_TEMPLATE_ROUTER.md](DRAFT_TEMPLATE_ROUTER.md) §1:
49
+ Each template's data file (`items-api.ts`, `settings-api.ts`) is an in-memory stand-in with the signatures the pages call. Replace the bodies, keep the signatures:
56
50
 
57
- ```tsx
58
- // {feature}.tsx — Case C
59
- import type { FC } from 'react';
60
- import { withDashboard } from '@wix/patterns';
61
- import { WixPatternsProvider } from '@wix/patterns/provider';
62
- import { WixDesignSystemProvider } from '@wix/design-system';
63
- import '@wix/design-system/styles.global.css';
64
- import { {Feature}SettingsPage } from './{Feature}SettingsPage'; // DRAFT_TEMPLATE_SETTINGS.md
51
+ - **Whose data it is decides the call.** Data an existing Wix app owns comes from that app's SDK, never a CMS collection ([SDK-First Rule](../../SKILL.md#sdk-first-rule-existing-wix-app-data-is-never-cms)). The method and field names: [DATA_SOURCES.md](DATA_SOURCES.md). Filter paths, operators and cursor paging: [QUERY_AND_PAGING.md](QUERY_AND_PAGING.md).
52
+ - **A CMS collection uses the CMS template, which has no data file** — its `collection.ts` takes the full scoped id, `<app-namespace>/<idSuffix>` for a collection your extension ships ([DATA_COLLECTION.md](../DATA_COLLECTION.md)). The collection does not exist until the app is released ([LIFECYCLE.md](../data-collection/LIFECYCLE.md#the-extension-does-not-create-the-collection)).
53
+ - **On a CMS collection, the prompt's fields are the collection's fields** — every one a column, none hand-written. For a collection your extension ships, create exactly those fields; an existing site collection shows all its fields, and the user hides the rest in the column picker. A stated column list decides the columns only on the hand-wired path.
65
54
 
66
- const Page: FC = () => (
67
- <WixDesignSystemProvider>
68
- <WixPatternsProvider>
69
- <{Feature}SettingsPage />
70
- </WixPatternsProvider>
71
- </WixDesignSystemProvider>
72
- );
55
+ ### Installing the CMS pair
73
56
 
74
- export default withDashboard(Page);
57
+ `@wix/patterns-cms` pins one exact `@wix/patterns` version, and the template's page says why a second copy breaks the page silently.
58
+
59
+ **Already in `package.json`** (some app templates ship both): don't install it again — go straight to the check. **Not there:** install it, then align `@wix/patterns` to its pin:
60
+
61
+ ```bash
62
+ npm install @wix/patterns-cms
63
+ npm install @wix/patterns@$(node -p "require('@wix/patterns-cms/package.json').dependencies['@wix/patterns']")
64
+ ```
65
+
66
+ **Either way, confirm a single copy:**
67
+
68
+ ```bash
69
+ npm dedupe
70
+ find node_modules -path '*@wix/patterns/package.json' -not -path '*/dist/*'
75
71
  ```
76
72
 
77
- Cases A, B and D differ — they need `location` supplied manually for `PatternsReactRouter`. See [DRAFT_TEMPLATE_ROUTER.md](DRAFT_TEMPLATE_ROUTER.md) rather than adding that plumbing here; it's dead code without a router underneath it.
73
+ More than one line is a bug that `tsc` and `wix build` both pass. Declared ranges produce it when `@wix/patterns` ships ahead of `@wix/patterns-cms`, or a lockfile holds an older one. Run the second `npm install` line above, then the check again.
74
+
75
+ ### Turning `query.search` into a query
78
76
 
79
- ## 2. Collection page — Case A, B, D
77
+ On the hand-wired path the data file turns one search term into a filter over *several* fields — an OR. **This is where search ships broken**: it renders, it reaches the query, and still returns every row. (The CMS template's source does this itself.)
80
78
 
81
- In [DRAFT_TEMPLATE_COLLECTION.md](DRAFT_TEMPLATE_COLLECTION.md) — `useTableCollection`, a working
82
- filter, the four placeholder states, and a row that opens. Shared by Cases A, B and D. No
83
- `SummaryBar` unless the request asked for one, and the drill-in is always a route — never a side
84
- panel.
79
+ There is no shared free-text operator; the shape differs per endpoint. Read its *Supported Filters* page ([QUERY_AND_PAGING.md](QUERY_AND_PAGING.md#the-filterable-fields-are-a-closed-list-published-per-endpoint)), then `$or` one clause per identity field it lists. Never route the term to a single field by its shape — a measured run shipped this, and one branch is always dead:
80
+
81
+ ```ts
82
+ query = term.includes('@')
83
+ ? query.startsWith('loginEmail', term)
84
+ : query.startsWith('contact.firstName', term); // a surname matches nothing, ever
85
+ ```
85
86
 
86
- ## 3. Settings page — Case C, D
87
+ `startsWith` is prefix-only too — "Smith" never finds "John Smith" — so prefer the containment operator when the endpoint declares one, and say which you used in `noResultsState`.
87
88
 
88
- In [DRAFT_TEMPLATE_SETTINGS.md](DRAFT_TEMPLATE_SETTINGS.md) — `useSettingsPage` + `useForm`,
89
- with the same field-controller patterns as the entity page.
89
+ **Whatever the shape, prove it narrows.** Run one term you expect to hit a known row and one you expect to hit nothing, and check the row count changes for both.
90
90
 
91
- ## What to change vs. keep, per case
91
+ ## 5. What this skill adds to every template
92
92
 
93
- | Change per request | Keep as shown |
94
- | --- | --- |
95
- | Feature/entity names, fields, columns, API calls in `{feature}-api.ts` | Which case (A/B/C/D) — don't over-build D for a request that only named a list |
96
- | Detail fields, form field types, columns | The drill-in being a route, and the absence of a `SummaryBar` the request never asked for |
97
- | Real data source (SDK-first per [SDK-First Rule](../../SKILL.md#sdk-first-rule-existing-wix-app-data-is-never-cms)) vs CMS | B/D wiring (provider/router nesting, `parentPath`, `location`): [DRAFT_TEMPLATE_ROUTER.md](DRAFT_TEMPLATE_ROUTER.md) |
93
+ - **No `SummaryBar` unless the request asked for one** — a named total, count or "how many / how much" figure. The templates ship without one on purpose ([SKILL.md § Step 2](../../SKILL.md)).
94
+ - **The state is MobX.** Deriving anything from the collection state in your own component — a header figure, a badge — goes through `useSelector` ([TABLE_STATE.md](TABLE_STATE.md#reading-state-outside-the-table-it-is-mobx)).
95
+ - **Anything the template does not show** — another filter type, a column renderer, a component the request needs — goes through [The Discovery Chain](../WIX_PATTERNS_DOCS.md#the-discovery-chain). A template replaces composing the shell, never the lookup for a symbol it does not contain.
@@ -136,49 +136,35 @@ This dashboard page manages dynamic parameters for an embedded script. The param
136
136
  - Display the preview alongside the settings form using Layout and Cell components
137
137
  - The preview should react to parameter changes in real-time
138
138
 
139
- ## Example Implementation
139
+ ## Files to Generate
140
140
 
141
- See the generated site-popup example for a complete reference implementation:
142
- - src/extensions/dashboard/withProviders.tsx - Provider wrapper with WDS
143
- - src/extensions/dashboard/pages/page.tsx - Dashboard page with parameter management (wrapped with withProviders)
144
- - src/extensions/dashboard/components/site-popup-settings.tsx - Settings form component
145
- - src/extensions/dashboard/types.ts - Type definitions
141
+ When dynamic parameters are present, generate all of these — the two wrapper files are not optional:
146
142
 
147
- Key implementation patterns from the example:
148
- 1. withProviders.tsx wraps the component with WixDesignSystemProvider
149
- 2. page.tsx exports the component wrapped: export default withProviders(MyComponent)
150
- 3. Parameters are saved as individual string fields, not as JSON
151
- 4. Parameters are loaded with proper type conversion (string to boolean, string to number, etc.)
152
- 5. Use embeddedScripts directly from '@wix/app-management'
143
+ | File | Role |
144
+ | --- | --- |
145
+ | `dashboard/BusinessManagerTheme.tsx` | Theme wrapper — [BUSINESS_MANAGER_THEME.md § 2](../BUSINESS_MANAGER_THEME.md#2-the-wrapper--write-this-file-once-per-app) |
146
+ | `dashboard/withProviders.tsx` | Provider wrapper (below) |
147
+ | `dashboard/pages/page.tsx` | The page, exported wrapped in `withProviders` |
148
+ | `dashboard/types.ts` | Parameter type definitions |
149
+ | Component files | Settings forms, previews |
153
150
 
154
- ## File Generation Requirements
155
-
156
- When dynamic parameters are present, you MUST generate these files:
157
- 1. src/extensions/dashboard/withProviders.tsx - Provider wrapper (REQUIRED for WDS)
158
- 2. src/extensions/dashboard/pages/page.tsx - The main dashboard page component
159
- 3. src/extensions/dashboard/types.ts - Type definitions for the parameters (if needed)
160
- 4. Any additional component files (settings forms, previews, etc.)
161
-
162
- The withProviders.tsx is NOT optional - it must always be generated when there are dynamic parameters.
151
+ Parameters are saved as individual string fields, never as one JSON string, and converted back to their real types on load. Use `embeddedScripts` directly from `@wix/app-management`.
163
152
 
164
153
  ## Provider Wrapper Implementation
165
154
 
166
- You MUST generate the following file: src/extensions/dashboard/withProviders.tsx
167
-
168
- This file is REQUIRED to wrap dashboard components with the Wix Design System provider.
155
+ Generate `src/extensions/dashboard/withProviders.tsx`:
169
156
 
170
157
  ```typescript
171
158
  import React from 'react';
172
- import { WixDesignSystemProvider } from '@wix/design-system';
173
159
  import { i18n } from '@wix/essentials';
160
+ import { BusinessManagerTheme } from './BusinessManagerTheme';
174
161
 
175
162
  export default function withProviders<P extends {} = {}>(Component: React.FC<P>) {
176
163
  return function DashboardProviders(props: P) {
177
- const locale = i18n.getLocale();
178
164
  return (
179
- <WixDesignSystemProvider locale={locale} features={{ newColorsBranding: true }}>
165
+ <BusinessManagerTheme locale={i18n.getLocale()}>
180
166
  <Component {...props} />
181
- </WixDesignSystemProvider>
167
+ </BusinessManagerTheme>
182
168
  );
183
169
  };
184
170
  }
@@ -187,16 +173,16 @@ export default function withProviders<P extends {} = {}>(Component: React.FC<P>)
187
173
  export { withProviders };
188
174
  ```
189
175
 
190
- This file must be included in your generated files output.
176
+ Business Manager passes none of the redesign through the extension's iframe, so a wrapper holding `WixDesignSystemProvider` alone renders the pre-redesign look while compiling and previewing cleanly. `features={{ newColorsBranding: true }}` predates the theme and does not substitute for it.
191
177
 
192
178
  ## Using Provider Wrapper
193
179
 
194
180
  In your dashboard page component (page.tsx):
195
- 1. Import the withProviders wrapper: `import withProviders from '../../withProviders';`
181
+ 1. `import withProviders from '../../withProviders';`
196
182
  2. Import embeddedScripts from '@wix/app-management'
197
- 3. DO NOT wrap your component with WixDesignSystemProvider - the provider wrapper does this
198
- 4. Export the component wrapped with withProviders: `export default withProviders(MyComponent);`
199
- 5. Your component should only contain the Page component and its content, not providers
183
+ 3. Add no design-system providers in the page — `withProviders` owns them
184
+ 4. Export wrapped: `export default withProviders(MyComponent);`
185
+ 5. The component holds the Page and its content, not providers
200
186
 
201
187
  Example structure:
202
188
  ```typescript
@@ -204,8 +190,7 @@ import { useEffect, useState, type FC } from 'react';
204
190
  import { dashboard } from '@wix/dashboard';
205
191
  import { embeddedScripts } from '@wix/app-management';
206
192
  import { Page, Card, Button, ... } from '@wix/design-system';
207
- import '@wix/design-system/styles.global.css';
208
- import withProviders from '../../withProviders';
193
+ import withProviders from '../../withProviders'; // owns the stylesheets and providers
209
194
 
210
195
  const MyDashboardPage: FC = () => {
211
196
  const [options, setOptions] = useState<MyScriptOptions>(defaultOptions);
@@ -253,9 +238,7 @@ export default withProviders(MyDashboardPage);
253
238
  ## Critical Notes
254
239
 
255
240
  - Only implement UI for parameters that are relevant to your specific use case - ignore parameters that don't apply
256
- - ALWAYS generate withProviders.tsx when there are dynamic parameters
257
- - ALWAYS wrap the dashboard page export with withProviders()
258
- - DO NOT use WixDesignSystemProvider directly in the dashboard page component - use withProviders instead
241
+ - ALWAYS generate `BusinessManagerTheme.tsx` and `withProviders.tsx`, and wrap the page export with `withProviders()` — no design-system providers in the page itself
259
242
  - ALWAYS use embeddedScripts directly from '@wix/app-management'
260
243
  - ALWAYS convert parameter values to strings when saving (embeddedScripts.embedScript must receive all string values in the parameters object)
261
244
  - ALWAYS convert string parameters back to proper types when loading (e.g., 'true' -> true for booleans, string to number for numbers)
@@ -17,10 +17,12 @@ about tooling, not evidence about the collection.
17
17
 
18
18
  ## Reading the collection from a dashboard page
19
19
 
20
- A page over a CMS collection is an ordinary collection page: the template is
21
- [DRAFT_TEMPLATE_COLLECTION.md](../dashboard-page/DRAFT_TEMPLATE_COLLECTION.md), and its `fetchData`
22
- calls `@wix/data` `items.query()` — see [WIX_DATA.md](WIX_DATA.md) for the query, result and paging
23
- shapes. You write the filters and the columns yourself, exactly as for a vertical SDK.
20
+ A page over a CMS collection uses the package's **CMS Collection and Entity Template**, not the
21
+ hand-wired one — the CMS already owns the field list, so `useCmsSchemaSource` supplies fetch,
22
+ filters, columns and the entity form from it. See
23
+ [DRAFT_TEMPLATE.md](../dashboard-page/DRAFT_TEMPLATE.md). Wiring
24
+ `items.query()` and hand-written columns by hand still compiles, and quietly gives up everything the
25
+ schema would have provided.
24
26
 
25
27
  ## The site must have CMS, or nothing is created at all
26
28
 
@@ -9,7 +9,6 @@ allowed-tools:
9
9
  - Bash(npm install *)
10
10
  - Bash(npm run *)
11
11
  - Bash(node *)
12
- - Bash(bash *)
13
12
  - Bash(cd *)
14
13
  - Bash(ls *)
15
14
  - Bash(mkdir *)
@@ -67,7 +67,7 @@ The verticals the skill operates end-to-end today: **stores · blog · cms · fo
67
67
 
68
68
  ### bookings — appointments and classes
69
69
  - **Intent:** book / appointment / schedule / class / session / reserve a slot — a **service or appointment with a provider** (a stylist, trainer, tutor, clinic, studio).
70
- - **Not bookings — route to `restaurants` instead:** a **table at a restaurant** is a Table Reservations concern, not a bookable service; and a **special dining occasion guests reserve** — a wine tasting, cheese pairing, chef's table, or tasting-menu evening at a restaurant — is a **restaurants → Table Reservations *experience*** (see the restaurants entry), **not** a bookings service. Reach for `bookings` only when what's booked is a standalone service/class with a provider, never a seat at a restaurant.
70
+ - **Not bookings — route to `restaurants` instead:** a **table at a restaurant** is a Table Reservations concern, not a bookable service; and a **special dining occasion guests reserve** — a chef's table, cheese pairing, or tasting-menu evening at a restaurant — is a **restaurants → Table Reservations *experience*** (see the restaurants entry), **not** a bookings service. Reach for `bookings` only when what's booked is a standalone service/class with a provider, never a seat at a restaurant.
71
71
  - **Not bookings — route to `rentals` instead:** when the **customer chooses how long** they want something, and what's booked is a **thing rather than a person** — a room by the hour, a desk for three days, a van for the weekend. The tell is a duration *range* the customer picks within. A 60-minute haircut is `bookings`; a meeting room booked for 1–8 hours is `rentals`.
72
72
  - **Required site features:** a list of services; a page per service; a way to pick a time and book it.
73
73
  - **Implementation checklist:** show each service's name, duration, and price; show the staff or provider; show the available time slots; a book action; a confirmation after booking.
@@ -85,8 +85,8 @@ The verticals the skill operates end-to-end today: **stores · blog · cms · fo
85
85
  - **Implementation checklist:** show each plan's name, price, and billing cycle; list what each plan includes (the perks); a clear "choose plan" action; highlight a recommended tier where it makes sense.
86
86
 
87
87
  ### restaurants — menus and food
88
- - **Intent:** menu / restaurant / cafe / food / dish list / order food / dine-in / **reserve a table** / a **special dining occasion guests reserve** (wine tasting, cheese pairing, chef's table, tasting-menu evening).
89
- - **Required site features:** a menu organized into sections; each item with a name, description, and price. **Add-ons, each on demand (separate apps/features; see `SETUP.md`):** online ordering, table reservations, and — **when the brief names special dining occasions** — **experiences** (a reservation that *is* a curated occasion at the restaurant, e.g. a wine-and-cheese pairing), which ride on the Table Reservations app.
88
+ - **Intent:** menu / restaurant / cafe / food / dish list / order food / dine-in / **reserve a table** / a **special dining occasion guests reserve** (chef's table, cheese pairing, tasting-menu evening).
89
+ - **Required site features:** a menu organized into sections; each item with a name, description, and price. **Add-ons, each on demand (separate apps/features; see `SETUP.md`):** online ordering, table reservations, and — **when the brief names special dining occasions** — **experiences** (a reservation that *is* a curated occasion at the restaurant, e.g. a chef's table), which ride on the Table Reservations app.
90
90
  - **Experiences vs bookings — decide here:** an **experience** is a **reservation that is itself a special dining occasion at the restaurant** — it books seats at the restaurant's own location and carries its own name, per-guest price, schedule, and booking form. Use it for pairing sessions, tastings, and chef's tables. Use the separate **`bookings`** vertical only for a **service/appointment/class with a provider** that isn't a seat at the restaurant. For anything a restaurant hosts and seats, prefer an experience.
91
91
  - **Implementation checklist:** show the menu grouped into sections in order; each item's name, description, price, and labels (vegan, spicy…) and modifiers / variants where they exist; an order action if online ordering is wired; a reserve-a-table action if reservations are wired; **if experiences are wired, list each experience with its name, description, per-guest price, and schedule, and a reserve-seats action.**
92
92
 
@@ -52,7 +52,7 @@ For each capability, build its `intent.<cap>` block — the inputs the seed step
52
52
  | bookings | `{ serviceCount, servicesNamed: [] }` | `serviceCount: 2`, brand-derived service names |
53
53
  | rentals | `{ unitType, serviceCount, resourceCount, servicesNamed: [] }` | `unitType: "HOUR"` (`"DAY"` if the brief rents by the day), `serviceCount: 2`, `resourceCount: 2`, brand-derived item names |
54
54
  | pricing-plans | `{ planCount, tiersNamed: [] }` | `planCount: 2` (e.g. Basic / Pro), monthly billing |
55
- | restaurants | `{ menuName, sections: [{ name, itemCount }], ordering?, reservations?, experiences?: [{ name }] }` | one menu, 2 sections, `itemCount: 3` each; add-ons only when the brief names them. **`experiences[]`** = special dining occasions guests reserve (wine/cheese pairing, chef's table) — a restaurant *reservation that is an experience*, **not** the `bookings` vertical (see `CAPABILITIES.md`). |
55
+ | restaurants | `{ menuName, sections: [{ name, itemCount }], ordering?, reservations?, experiences?: [{ name }] }` | one menu, 2 sections, `itemCount: 3` each; add-ons only when the brief names them. **`experiences[]`** = special dining occasions guests reserve (chef's table, tasting menu) — a restaurant *reservation that is an experience*, **not** the `bookings` vertical (see `CAPABILITIES.md`). |
56
56
  | portfolio | `{ collections: [{ name }], projectCount }` | one brand-derived collection, `projectCount: 3` |
57
57
 
58
58
  Counts are deliberately small (the seed shows the shape, not a full catalog).
@@ -48,7 +48,7 @@ Before implementing a capability, Read the local recipe linked in its row.
48
48
  | restaurants — online ordering (add-on; only when the Orders app is in the run) | add `@wix/ecom` + `@wix/redirects` to the row above | `how-to-code-restaurant-orders.md` — ordering cart/checkout on top of the menu display. |
49
49
  | portfolio | `@wix/portfolio` (+ `media` from `@wix/sdk` to resolve image strings; **no** `@wix/ecom`/`@wix/redirects` — display-only) | `how-to-code-portfolio.md` — Portfolio v1 display-only read. |
50
50
  | restaurants — table reservations (add-on; only when the Table Reservations app is in the run) | `@wix/table-reservations` (a **distinct** package from `@wix/restaurants`; **no ecom/redirects** — a reservation is a hold, not a purchase) | `how-to-code-restaurant-reservations.md` — book-a-table hold→reserve. |
51
- | restaurants — experiences (add-on; special dining occasions guests reserve — wine/cheese pairing, chef's table) | `@wix/table-reservations` (same package; the **`experiences`** namespace + `timeSlots`/`reservations`) | `how-to-code-restaurant-reservations.md` — "Booking an experience" (query experiences → scheduled slots → reserve with `experienceId`). |
51
+ | restaurants — experiences (add-on; special dining occasions guests reserve — chef's table, tasting menu) | `@wix/table-reservations` (same package; the **`experiences`** namespace + `timeSlots`/`reservations`) | `how-to-code-restaurant-reservations.md` — "Booking an experience" (query experiences → scheduled slots → reserve with `experienceId`). |
52
52
 
53
53
  > **Cross-cutting packages** (`CAPABILITIES.md` § "Cross-cutting capabilities"): these aren't per-vertical rows but get added to the package list when the run used them. If **coupons** were seeded, add `@wix/marketing`. The eCommerce checkout packages (`@wix/ecom`, `@wix/redirects`) are already folded into the stores row above — include them once when any purchase-flow vertical is present.
54
54
 
@@ -39,7 +39,7 @@ Each entry states only the entities, where their count/content comes from, the p
39
39
  - **bookings** — *Bookable services.* Recipe: **`setup-bookings.md`**. `intent.bookings.serviceCount` services (name + short description fitting `brand`, a simple duration and price); keep the schedule minimal. **Keep:** `serviceIds[]` as **seed-time state** (pricing-plans' coverage step keys on them, below); nothing for the handoff — services are discovered **live** (`queryServices`).
40
40
  - **rentals** — *Rentable resources.* Recipe: **`setup-rentals.md`**. A resource **type** per kind of thing rented (rooms, vans, cameras), then `intent.rentals.resourceCount` **resources** inside it (Room A / Room B — parallel capacity comes from more resources, not higher capacity), then `intent.rentals.serviceCount` **rental services**, each carrying the Wix Rentals `appId`, `serviceResources` naming its resource type, a `primaryResourceType`, and a `durationRange` (`HOUR` or `DAY`) with the min/max the request implies — defaulting to **60/480 minutes** hourly and **1/5 days** daily when the brief doesn't say — and a **per-unit** price. **No category** — unlike bookings, rental services don't use one; don't create or assign one. **Order is load-bearing: resource type → resources → services** — a service whose resource type holds no resources has permanently empty availability. Seed resources **24/7** (no working-hours schedule) unless the request names opening hours; that keeps a multi-day rental to a single booking. **Keep:** `resourceTypeIds[]` as seed-time state (services key on them); nothing for the handoff — rentals are discovered **live** (`queryServicesByFilters` filtered by the rentals `appId`).
41
41
  - **pricing-plans** — *Membership tiers.* Recipe: **`setup-pricing-plans.md`**. `intent.pricing-plans.planCount` recurring plans (name, price, a monthly billing cycle) fitting `brand`. **When bookings is also in this run and the intent is a membership that covers services** (a studio/gym/class pass), also **attach the covered bookings services to the plan** — seed the bookings services first, since coverage keys on their service ids (see the recipe). **Keep:** the bookings coverage only transiently, to wire it during seeding; nothing for the handoff — plans are discovered **live** (`queryPlans`) and plan→service coverage is read **live** at checkout, never from a frozen map (see `how-to-code-pricing-plans.md`).
42
- - **restaurants** — *A menu.* Recipe: **`setup-restaurants.md`** (add-ons have their own recipes, below). Create the `itemCount` items per section first (name, description, price fitting `brand`), then the `intent.restaurants.sections` referencing those item ids, then the menu referencing the section ids — **items → sections → menu** (build bottom-up: a section is created with its `itemIds`, a menu with its `sectionIds`, so each child must exist before its parent — see `setup-restaurants.md` STEP 1–3). Text-only. **Keep (seed-time only):** `menuId`, `sectionIds[]`, `itemIds[]` to build the tree; nothing goes to the handoff — the frontend reads menus **live** (`listMenus`/`listSections`/`listItems`). **Online ordering (add-on, on demand)** — only when the request calls for ordering (not just a displayed menu): install the Orders app (per `SETUP.md` §2) and follow **`setup-restaurant-orders.md`**; **seed the menu FIRST, then ordering, in the same unit** (ordering auto-provisions per menu either way — see the concurrency note above — but keeping them one unit gives the ordering verify a menu to confirm against). **Configure the business-location address (`setup-restaurant-orders.md` STEP 0) — required for ordering to work end-to-end; without it Wix limits ordering to "testing only" and checkout breaks.** If the brief gives no address, set a placeholder and flag the owner in the handoff. Completing a *paid* order additionally needs premium + a payment method (dashboard/premium — surface, don't fail). **Keep:** nothing for the handoff — read **live** (`listOperations`/`listFulfillmentMethods`); track `operationId`/`fulfillmentMethodIds[]` transiently. **Table reservations (add-on, on demand, INDEPENDENT of ordering/menu)** — only when the request calls for reservations: install the Table Reservations app (per `SETUP.md` §2) and follow **`setup-restaurant-reservations.md`**; **no menu dependency** (reservations bind to a location, not a menu — do NOT apply the menu-first rule) and **nothing to bulk-seed** (visitors create reservations at runtime); **configure the business-location address (`setup-restaurant-reservations.md` STEP 0)** — the reservation location is the site's business location, so set a real address (placeholder + owner flag if the brief names none; shared with ordering if both are present); enabling online reservations is **premium-gated** (record the precondition, don't fail). **Keep:** nothing for the handoff — read **live** (`listReservationLocations`); track `reservationLocationId` transiently. **Experiences (add-on within Table Reservations, on demand)** — only when the request names **special dining occasions guests reserve** (wine/cheese pairing, chef's table, tasting-menu evening — an *experience*, not a `bookings` service; see `CAPABILITIES.md`): they need the **Table Reservations app** (no extra install) and a configured **business-location address** (`setup-restaurant-reservations.md` STEP 0, same as reservations) and, unlike plain reservations, they **are** seeded — create one Experience per named occasion on the default reservation location, following **`setup-restaurant-experiences.md`**. Booking an experience is **premium-gated** like online reservations (record the precondition, don't fail). **Keep:** nothing for the handoff — experiences are read **live** (`queryExperiences`); track `experienceId`(s) transiently.
42
+ - **restaurants** — *A menu.* Recipe: **`setup-restaurants.md`** (add-ons have their own recipes, below). Create the `itemCount` items per section first (name, description, price fitting `brand`), then the `intent.restaurants.sections` referencing those item ids, then the menu referencing the section ids — **items → sections → menu** (build bottom-up: a section is created with its `itemIds`, a menu with its `sectionIds`, so each child must exist before its parent — see `setup-restaurants.md` STEP 1–3). Text-only. **Keep (seed-time only):** `menuId`, `sectionIds[]`, `itemIds[]` to build the tree; nothing goes to the handoff — the frontend reads menus **live** (`listMenus`/`listSections`/`listItems`). **Online ordering (add-on, on demand)** — only when the request calls for ordering (not just a displayed menu): install the Orders app (per `SETUP.md` §2) and follow **`setup-restaurant-orders.md`**; **seed the menu FIRST, then ordering, in the same unit** (ordering auto-provisions per menu either way — see the concurrency note above — but keeping them one unit gives the ordering verify a menu to confirm against). **Configure the business-location address (`setup-restaurant-orders.md` STEP 0) — required for ordering to work end-to-end; without it Wix limits ordering to "testing only" and checkout breaks.** If the brief gives no address, set a placeholder and flag the owner in the handoff. Completing a *paid* order additionally needs premium + a payment method (dashboard/premium — surface, don't fail). **Keep:** nothing for the handoff — read **live** (`listOperations`/`listFulfillmentMethods`); track `operationId`/`fulfillmentMethodIds[]` transiently. **Table reservations (add-on, on demand, INDEPENDENT of ordering/menu)** — only when the request calls for reservations: install the Table Reservations app (per `SETUP.md` §2) and follow **`setup-restaurant-reservations.md`**; **no menu dependency** (reservations bind to a location, not a menu — do NOT apply the menu-first rule) and **nothing to bulk-seed** (visitors create reservations at runtime); **configure the business-location address (`setup-restaurant-reservations.md` STEP 0)** — the reservation location is the site's business location, so set a real address (placeholder + owner flag if the brief names none; shared with ordering if both are present); enabling online reservations is **premium-gated** (record the precondition, don't fail). **Keep:** nothing for the handoff — read **live** (`listReservationLocations`); track `reservationLocationId` transiently. **Experiences (add-on within Table Reservations, on demand)** — only when the request names **special dining occasions guests reserve** (chef's table, tasting menu, tasting-menu evening — an *experience*, not a `bookings` service; see `CAPABILITIES.md`): they need the **Table Reservations app** (no extra install) and a configured **business-location address** (`setup-restaurant-reservations.md` STEP 0, same as reservations) and, unlike plain reservations, they **are** seeded — create one Experience per named occasion on the default reservation location, following **`setup-restaurant-experiences.md`**. Booking an experience is **premium-gated** like online reservations (record the precondition, don't fail). **Keep:** nothing for the handoff — experiences are read **live** (`queryExperiences`); track `experienceId`(s) transiently.
43
43
  - **portfolio** — *A project showcase.* Recipe: **`setup-portfolio.md`**. Create the `intent.portfolio.collections` (title + short description fitting `brand`), then `intent.portfolio.projectCount` projects assigned to them — **collections before projects** (a project's `collectionIds` must hold real collection ids; see the recipe). Text-only by default (omit `coverImage`; images only when `imagery` is on — §1, §4). **Keep (seed-time only):** `collectionIds{<name>}` to assign projects into their collections; nothing goes to the handoff — the frontend lists collections and projects **live** (`collections.queryCollections` / `projects.queryProjects`), so owner-added content appears with no code change.
44
44
 
45
45
  > **Cross-cutting (on demand).** `coupons` is not a standalone capability (`CAPABILITIES.md` § "Cross-cutting capabilities") — there's no app to install and it's not in `verticals[]`. If intent calls for discounts **and** a parent vertical (stores / bookings / events / pricing-plans) is in this run, create coupons scoped to that parent and add the coupon to the `seeded` map (`seeded.coupons = { couponIds[] }`). **Coupons has no inline recipe** — read the create shape from the docs (`DOC_DISCOVERY.md` is the fallback): every coupon needs a scope with a namespace and its parent vertical's app installed (<https://dev.wix.com/docs/api-reference/business-solutions/coupons/about-wix-coupons.md>), and the create-coupon spec needs name/code/start + a scope namespace + exactly one coupon-type field (<https://dev.wix.com/docs/api-reference/business-solutions/coupons/coupons/create-a-coupon.md>). eCommerce needs no seeding — it's the runtime checkout layer that rides along.
@@ -25,7 +25,7 @@ For each capability in `verticals[]`, install its app by `appDefId` (these are t
25
25
  - **bookings** → `13d21c63-b5ec-5912-8397-c3a5ddb27a97`
26
26
  - **rentals** → `ff5d6eb1-65e4-4f9a-8b14-64d34c12cc2e` (Wix Rentals). Rentals provisions the Bookings infrastructure it runs on, so a Rentals-only install is enough — its availability comes back without adding Wix Bookings separately. Rentals has **no APIs of its own**: it is Services V2 with rentals-specific field values (`setup-rentals.md`), and its frontend package is `@wix/bookings` — there is no `@wix/rentals`. Rentals uses no categories; to also offer plain fixed-duration services, install Wix Bookings (`13d21c63-b5ec-5912-8397-c3a5ddb27a97`) as well, seed those with `setup-bookings.md`, and filter every catalog read by `appId` so the two sets stay separate.
27
27
  - **pricing-plans** → `1522827f-c56c-a5c9-2ac9-00f9e6ae12d3`
28
- - **restaurants** → `b278a256-2757-4f19-9313-c05c783bec92` (Wix Restaurants **Menus** — the seedable core). Online ordering and table reservations are **separate optional apps**, install only if intent calls for them: Orders (New) → `9a5d83fd-8570-482e-81ab-cfa88942ee60`, Table Reservations → `f9c07de2-5341-40c6-b096-8eb39de391fb`. **Experiences** (special dining occasions guests reserve — wine tastings, chef's tables; see `CAPABILITIES.md`) are a **feature of the Table Reservations app, not a separate install** — installing Table Reservations is all that's needed to create and book them (`setup-restaurant-experiences.md`).
28
+ - **restaurants** → `b278a256-2757-4f19-9313-c05c783bec92` (Wix Restaurants **Menus** — the seedable core). Online ordering and table reservations are **separate optional apps**, install only if intent calls for them: Orders (New) → `9a5d83fd-8570-482e-81ab-cfa88942ee60`, Table Reservations → `f9c07de2-5341-40c6-b096-8eb39de391fb`. **Experiences** (special dining occasions guests reserve — chef's tables, tasting menus; see `CAPABILITIES.md`) are a **feature of the Table Reservations app, not a separate install** — installing Table Reservations is all that's needed to create and book them (`setup-restaurant-experiences.md`).
29
29
  - **portfolio** → `d90652a2-f5a1-4c7c-84c4-d4cdcc41f130` (Wix Portfolio; the install ships a default sample collection + projects — `setup-portfolio.md` STEP 0 cleans them)
30
30
  - **cms** → **no install** (Wix Data is core) — skip
31
31
 
@@ -166,7 +166,7 @@ Doc: <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/rese
166
166
 
167
167
  ### Booking an experience (only when experiences are in the run)
168
168
 
169
- An **experience** is a reservation that *is* a curated dining occasion (wine/cheese pairing, chef's table). It uses the **same** booking mechanism as a table, with two differences: you **list experiences** to show and choose from, and you offer bookable times **projected from the experience's own schedule** (there is *no* experience-scoped slot API — see Gotcha C) rather than from `getScheduledTimeSlots`, then stamp the chosen `experienceId` onto the reservation. Read the shapes from the docs (`.md?apiView=SDK`); the earned drift gotchas below are what the docs won't tell you.
169
+ An **experience** is a reservation that *is* a curated dining occasion (chef's table, tasting menu). It uses the **same** booking mechanism as a table, with two differences: you **list experiences** to show and choose from, and you offer bookable times **projected from the experience's own schedule** (there is *no* experience-scoped slot API — see Gotcha C) rather than from `getScheduledTimeSlots`, then stamp the chosen `experienceId` onto the reservation. Read the shapes from the docs (`.md?apiView=SDK`); the earned drift gotchas below are what the docs won't tell you.
170
170
 
171
171
  Flow (mirrors the docs' sample flow — <https://dev.wix.com/docs/api-reference/business-solutions/restaurants/reservations/experiences/sample-flows.md>):
172
172
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: "Setup Restaurant Experiences"
3
- description: Seeds Wix Table Reservations **Experiences** — a reservation that IS a special dining occasion at the restaurant (wine tasting, cheese pairing, chef's table, tasting-menu evening), as opposed to a plain table booking or a Bookings service. Points to the live docs for the full create payload and carries only the earned gotchas the docs don't state (field-shape drift, payment policy, premium gating, when-to-use). The occasions/prices/schedule come from the request.
3
+ description: Seeds Wix Table Reservations **Experiences** — a reservation that IS a special dining occasion at the restaurant (chef's table, cheese pairing, tasting-menu evening), as opposed to a plain table booking or a Bookings service. Points to the live docs for the full create payload and carries only the earned gotchas the docs don't state (field-shape drift, payment policy, premium gating, when-to-use). The occasions/prices/schedule come from the request.
4
4
  ---
5
5
  **RECIPE**: Business Recipe – Experiences Setup for Wix Table Reservations (Experiences API)
6
6
 
@@ -8,7 +8,7 @@ description: Seeds Wix Table Reservations **Experiences** — a reservation that
8
8
 
9
9
  ## When to use experiences (and when NOT to)
10
10
 
11
- An **experience** is a **reservation that is itself a curated dining occasion at the restaurant** — it books seats at the restaurant's own reservation location but overrides the location's defaults with its own name, description, per-guest payment policy, party-size, schedule, and booking form. Reach for it when the brief names a **wine tasting, cheese/wine pairing, chef's table, or tasting-menu evening**.
11
+ An **experience** is a **reservation that is itself a curated dining occasion at the restaurant** — it books seats at the restaurant's own reservation location but overrides the location's defaults with its own name, description, per-guest payment policy, party-size, schedule, and booking form. Reach for it when the brief names a **chef's table, cheese pairing, or tasting-menu evening**.
12
12
 
13
13
  - **Experience vs plain reservation:** a plain reservation just books a table; an experience books a *named, scheduled, often paid occasion*. Same booking mechanism, different intent.
14
14
  - **Experience vs `bookings`:** the separate `bookings` vertical is for a **service/appointment/class with a provider** (spa, tutor, studio). If a restaurant *hosts and seats* the occasion, it's an experience — not a booking. (See `CAPABILITIES.md` — "Experiences vs bookings".)
@@ -16,7 +16,7 @@ npx @wix/cli@latest whoami # exits 0 when logged in; non-zero when logged out
16
16
  If it's non-zero, **log in yourself** — don't punt to the user and stop. A foreground command that exits in seconds, one JSON event per line:
17
17
 
18
18
  ```bash
19
- node ../../entry/bootstrap.mjs # path relative to this file
19
+ node ../../scripts/bootstrap.mjs # path relative to this file
20
20
  ```
21
21
 
22
22
  | Event | Do |
@@ -25,20 +25,9 @@ node ../../entry/bootstrap.mjs # path relative to this file
25
25
  | `awaiting_user` (`verificationUri`, `userCode`, `message`) | Send `message` verbatim, then stop. Re-run the script when the user says they're done → `logged_in`. Re-running early returns the **same** code, not a new one. |
26
26
  | `cli_unreachable` / `login_failed` (`detail`) | Show `detail` and stop. |
27
27
 
28
- ### If you'd rather not execute it
28
+ ### What the script does
29
29
 
30
- Read `../../entry/bootstrap.mjs` and do the same thing yourself. What matters:
31
-
32
- ```bash
33
- # AI_AGENT must be set, or the CLI renders an interactive Ink TUI and emits no JSON.
34
- # Detach, redirecting to a FILE — a pipe dies with your process and the CLI gets EPIPE.
35
- AI_AGENT=my-agent nohup npx -y @wix/cli@latest login > /tmp/wix-login.log 2>&1 &
36
-
37
- grep -m1 awaiting_user /tmp/wix-login.log
38
- # {"event":"awaiting_user","expiresInSeconds":600,"userCode":"…","verificationUri":"…"}
39
- ```
40
-
41
- Send the user that URL + code, then stop — nothing moves until they act. The detached login lands the session on its own; resume with `whoami` (exit 0). Codes last ~10 min; after that, start a new login and surface the new code.
30
+ Read `../../scripts/bootstrap.mjs` if you want to see it: it sets `AI_AGENT` so the CLI emits JSON events instead of an interactive TUI, starts `wix login` detached with its output in a file under the OS temp dir (a pipe would die with the parent), and prints the device code. It is the one login path; there is no manual variant. Codes last ~10 min; after that, run it again and surface the new code.
42
31
 
43
32
  ## 2 · Mint the token
44
33