@wix/agent-skills 1.31.2 → 1.32.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 (40) hide show
  1. package/README.md +4 -12
  2. package/package.json +1 -1
  3. package/skills/wix-app/SKILL.md +39 -15
  4. package/skills/wix-app/references/BACKEND_API.md +1 -1
  5. package/skills/wix-app/references/EDITOR_REACT_COMPONENT.md +11 -8
  6. package/skills/wix-app/references/SDK_MODULE_MAP.md +1 -1
  7. package/skills/wix-app/references/STORES_VERSIONING.md +24 -2
  8. package/skills/wix-app/references/dashboard-page/DATA_SOURCES.md +9 -13
  9. package/skills/wix-app/references/dashboard-page/IMAGE_RENDERING.md +61 -0
  10. package/skills/wix-app/references/editor-react-component/ACCESSIBILITY.md +54 -64
  11. package/skills/wix-app/references/editor-react-component/ANIMATED-COMPONENTS.md +14 -21
  12. package/skills/wix-app/references/editor-react-component/COMPONENT-CONTRACT.md +37 -30
  13. package/skills/wix-app/references/editor-react-component/CSS-GUIDELINES.md +43 -29
  14. package/skills/wix-app/references/editor-react-component/DESIGN-STATES.md +40 -39
  15. package/skills/wix-app/references/editor-react-component/PARTS.md +8 -6
  16. package/skills/wix-app/references/editor-react-component/PROPS-VS-CSS.md +22 -2
  17. package/skills/wix-app/scripts/scan-a11y-eslint.cjs +39 -16
  18. package/skills/wix-app/scripts/sdk-curated.json +45 -0
  19. package/skills/wix-app/scripts/sdk-lookup.cjs +1180 -0
  20. package/skills/wix-base44-connector/SKILL.md +4 -2
  21. package/skills/wix-base44-connector/scripts/utils.cjs +5 -2
  22. package/skills/wix-headless-kit/SKILL.md +4 -1
  23. package/skills/wix-headless-kit/guides/api-run.md +14 -20
  24. package/skills/wix-headless-kit/install/pins.json +1 -1
  25. package/skills/wix-manage/SKILL.md +30 -8
  26. package/skills/wix-manage/references/automations/actions.md +134 -0
  27. package/skills/wix-manage/references/automations/activation-status.md +64 -48
  28. package/skills/wix-manage/references/automations/api-catalog.md +213 -77
  29. package/skills/wix-manage/references/automations/automation-model.md +199 -0
  30. package/skills/wix-manage/references/automations/bracket-expressions.md +132 -0
  31. package/skills/wix-manage/references/automations/build-and-manage-automations.md +305 -26
  32. package/skills/wix-manage/references/automations/triggers.md +190 -0
  33. package/skills/wix-manage/references/collaborators/manage-site-collaborators.md +128 -0
  34. package/skills/wix-manage/references/forms/forms-dashboard-navigation.md +3 -1
  35. package/skills/wix-manage/references/forms/read-forms-and-submissions.md +277 -0
  36. package/skills/wix-manage/references/forms/update-form.md +5 -0
  37. package/skills/wix-manage/references/seo/manage-url-redirects.md +33 -4
  38. package/skills/wix-manage/references/sites/create-headless-site.md +14 -5
  39. package/skills/wix-manage/references/sites/upload-static-site.md +373 -302
  40. package/skills/wix-vibe-headless/references/shared/app/components/WixManageBanner.jsx +3 -3
@@ -3,7 +3,7 @@
3
3
  Use this reference when a named part changes appearance through interaction or
4
4
  a selectable value. A design state styles an element differently for an
5
5
  eligible interaction (`hover`, `focus`, `disabled`, `invalid`) or per a
6
- selectable value in its data (`selected`, `active`, `open`, …). Author states
6
+ selectable value in its data (`selected`, `active`, `checked`, …). Author states
7
7
  in the component's `.tsx` and `.module.css`.
8
8
 
9
9
  ## Contents
@@ -24,9 +24,9 @@ in the component's `.tsx` and `.module.css`.
24
24
  | Input field — `select`/`textarea`; `input` except `hidden`/`button`/`submit`/`reset`/`image`; or an input-widget `role` (`checkbox`/`radio`/`switch`/`slider`/`spinbutton`/`textbox`/`searchbox`/`combobox`/`listbox`) | `focus` |
25
25
  | Other interactive element whose editable focus appearance the user explicitly requests | `focus` |
26
26
  | Disableable — `button`/`input`/`select`/`textarea`/`fieldset` or a disableable role | `disabled` (+ `invalid` for `input`/`select`/`textarea`) |
27
- | Has a selectable/variant value in its data — `selected`/`active`/`current`/`open`/`expanded`/`checked`/`featured` | that custom state |
28
- | Whole component opens/collapses (expandable panel, disclosure, dropdown, drawer), even when a click toggles it | root `isOpen` prop state — see §6 |
29
- | None of the above | no states — resting style only |
27
+ | Has a selectable/variant value in its data — `selected`/`active`/`current`/`checked`/`featured` | that custom state |
28
+ | Whole component opens/collapses or reveals content (expandable panel, disclosure, dropdown, drawer, popup, tooltip) on click, hover or focus | root `isOpen` state, no part `open` state (§6) |
29
+ | None of the above, incl. a non-interactive root or part | no states, and no `:hover`/`:active`/`:focus` rules, even decorative; resting style only |
30
30
 
31
31
  An explicit `role` overrides the tag's implicit semantics: `<input role="button">`
32
32
  is a non-input control, while `<div role="checkbox">` is an input field.
@@ -37,8 +37,7 @@ unless the user explicitly requests editable focus styling. A control that
37
37
  restyles its own outline, background, or border must still carry a standalone
38
38
  `:focus-visible` rule as its keyboard affordance.
39
39
 
40
- A custom state may also be driven by a root-level boolean prop (e.g.
41
- `isFeatured`) instead of by markup or item data — see §5. For an active-item
40
+ A root-level boolean prop can also drive a custom state (§5). For an active-item
42
41
  component, drive each item's `--active` class by comparing its index with the
43
42
  active-index prop rather than storing a per-item boolean.
44
43
 
@@ -47,7 +46,7 @@ active-index prop rather than storing a per-item boolean.
47
46
  Flat: the element's own global class + `--<state>`. Because inner-part
48
47
  global classes are prefixed with the component name, the state class is prefixed too —
49
48
  e.g. `pricing-card-cta--hover`, `pricing-card-plan-row--selected`. Never bare
50
- (`cta--hover` would collide with other components on the page) and never
49
+ (`cta--hover` collides with other components) and never
51
50
  nested (`card__row--selected`).
52
51
 
53
52
  ## 3. Author CSS
@@ -56,7 +55,7 @@ Put the resting value in the bare class; put only the state override in the
56
55
  state selector. When both set the same property, write the state selector
57
56
  **before** the bare class. Manifest generation can otherwise record the state
58
57
  value as the part's resting `defaultValue`, even though runtime CSS renders
59
- correctly. The state selector still wins at runtime through its specificity.
58
+ correctly.
60
59
 
61
60
  - **Native design state** — pair the pseudo-class with the `:global` modifier.
62
61
  Every exposed native state needs both selectors in the same rule; do not
@@ -101,34 +100,34 @@ The local static accessibility scan cannot measure rendered color contrast.
101
100
  - **Native** — render the correct interactive element (`<button>`, an
102
101
  interactive `role`, or a handler). No custom state class is needed, but the
103
102
  element must still satisfy the accessibility contract for its semantics.
104
- - **Custom** — toggle the global state class from the element's data. Every
105
- state class toggled in TSX needs a matching `:global()` rule in §3; without
106
- one the manifest generator silently drops the state.
103
+ - **Custom** — toggle the global state class from the element's data, except
104
+ for open/collapsed content (§6). Every state class toggled in TSX needs a
105
+ matching `:global()` rule in §3; without one the manifest generator silently
106
+ drops the state.
107
107
  - **Inner elements** — every named inner element gets an `elementProps` entry;
108
108
  spread it so editor-driven states reach it. On a raw HTML element also merge
109
109
  `elementProps?.<key>.className` inline; on a skill-built sub-component the
110
- spread alone suffices (it merges `className` itself). The `elementProps` key
111
- stays the **short** part name (`cta`) even though the element's global class
112
- is prefixed (`pricing-card-cta`) — don't rename the key to match the class.
110
+ spread alone suffices (it merges `className` itself). The key is the global
111
+ class in camelCase (`COMPONENT-CONTRACT.md`).
113
112
 
114
113
  Wire native and custom states as follows:
115
114
 
116
115
  ```tsx
117
116
  <button
118
117
  type="button"
119
- {...elementProps?.cta}
120
- className={classNames('pricing-card-cta', styles.cta, elementProps?.cta?.className)}
118
+ {...elementProps?.pricingCardCta}
119
+ className={classNames('pricing-card-cta', styles.cta, elementProps?.pricingCardCta?.className)}
121
120
  >
122
121
  {label}
123
122
  </button>
124
123
 
125
124
  <li
126
- {...elementProps?.planRow}
125
+ {...elementProps?.pricingCardPlanRow}
127
126
  className={classNames(
128
127
  'pricing-card-plan-row',
129
128
  styles.planRow,
130
129
  row.selected && 'pricing-card-plan-row--selected',
131
- elementProps?.planRow?.className,
130
+ elementProps?.pricingCardPlanRow?.className,
132
131
  )}
133
132
  >
134
133
  {row.label}
@@ -143,10 +142,9 @@ A **prop-triggered** state is a custom state switched by a single boolean
143
142
  whole component's appearance — e.g. `isLoading`, `isFeatured`.
144
143
 
145
144
  Mark the prop with the `ElementState<boolean>` type from
146
- `@wix/react-component-utils`. It is an identity alias — the prop still behaves
147
- as a plain `boolean` at runtime and stays a normal boolean in the component's
148
- data — but the manifest generator detects the marker and emits a custom state
149
- on the **root element**.
145
+ `@wix/react-component-utils`. It is an identity alias (a plain `boolean` at
146
+ runtime and in data); the manifest generator detects the marker and emits a
147
+ custom state on the **root element**.
150
148
 
151
149
  ```tsx
152
150
  import type { ElementState } from '@wix/react-component-utils';
@@ -178,39 +176,41 @@ Rules:
178
176
  ```
179
177
 
180
178
  Prefer a native state (interactive markup) or a class trigger (per-item data
181
- such as `row.selected`) whenever one fits — those are the common cases and work
182
- at any depth. Reach for a prop trigger only for a root-level boolean switch
183
- or an open/collapsed toggle (§6).
179
+ such as `row.selected`) whenever one fits; both work at any depth. Reach for a
180
+ prop trigger only for a root-level boolean switch.
184
181
 
185
182
  ## 6. Expose Open/Collapsed Toggles
186
183
 
187
- For a component that opens or collapses, expose its state to the editor even
188
- when a click toggles it. `useState` alone gives the manifest no state picker.
189
- For one-of-many item bodies, use the active-item contract instead.
184
+ Expose the open state of a component that opens, collapses or reveals content
185
+ on click, hover or focus. `useState` alone, or a CSS `:hover`/`:focus-within`
186
+ reveal, gives the editor no state picker. For one-of-many item bodies, use the
187
+ active-item contract instead.
190
188
 
191
189
  - Add `isOpen?: ElementState<boolean>` and set `isOpen: false` in
192
- `defaultProps`.
193
- - Seed and resync internal state from the prop; clicks update internal state.
190
+ `defaultProps`. Keep it props-only (§5).
191
+ - Seed and resync internal state from the prop; clicks, pointer enter/leave and
192
+ focus/blur update it.
194
193
  - Always render the collapsible content; hide it with CSS plus `aria-hidden`
195
194
  and `inert` when closed. Never mount it with `open && …`.
196
- - Style the open state with inner-part classes
197
- (`<component-name>-content--open`) and matching `:global()` rules, not only
198
- inline styles or refs measured during render.
199
- - Keep the root state props-only for the on-stage picker (§5).
195
+ - Style the open look with a module class (`open && styles.contentOpen`), not
196
+ only inline styles or render-time refs.
197
+ - Never toggle a `:global()` class for it (`--open`, `--visible`, …) on the
198
+ root or any part: each adds an "Open" design-panel state beside the on-stage
199
+ picker.
200
200
 
201
201
  ```tsx
202
202
  const [open, setOpen] = React.useState(Boolean(isOpen));
203
203
  React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);
204
204
 
205
205
  <div
206
- {...elementProps?.content}
206
+ {...elementProps?.infoPanelContent}
207
207
  aria-hidden={!open}
208
208
  {...(!open && { inert: '' })}
209
209
  className={classNames(
210
210
  'info-panel-content',
211
211
  styles.content,
212
- open && 'info-panel-content--open',
213
- elementProps?.content?.className,
212
+ open && styles.contentOpen,
213
+ elementProps?.infoPanelContent?.className,
214
214
  )}
215
215
  >
216
216
  <p>{text}</p>
@@ -219,7 +219,7 @@ React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);
219
219
 
220
220
  ```css
221
221
  .content { display: none; }
222
- .content:global(.info-panel-content--open) { display: block; }
222
+ .contentOpen { display: block; }
223
223
  ```
224
224
 
225
225
  ## Checklist
@@ -233,4 +233,5 @@ React.useEffect(() => setOpen(Boolean(isOpen)), [isOpen]);
233
233
  - [ ] Every state class toggled in TSX has a matching `:global()` CSS rule.
234
234
  - [ ] `ElementState<boolean>` is used only for a component-level root state.
235
235
  - [ ] An open/collapsed component exposes a props-only `isOpen` root state,
236
- syncs internal state from it, and always renders its content.
236
+ syncs internal state from it, always renders its content, and adds no
237
+ `--open` class anywhere.
@@ -24,13 +24,14 @@ would plausibly control independently in the editor. It receives:
24
24
 
25
25
  - one prefixed global class: `'<component-name>-<part-name>'`
26
26
  - one CSS Module class
27
- - one matching `elementProps` entry, spread onto the element. Declare `a11y` on
28
- the entry only when the part reads `ariaLabel`, and destructure it out before
29
- spreading (see `ACCESSIBILITY.md`)
27
+ - one matching `elementProps` entry, keyed by the camelCase of its global class
28
+ and spread onto the element. Declare `a11y` on the entry only when the part
29
+ reads `ariaLabel`, and destructure it out before spreading (see
30
+ `ACCESSIBILITY.md`)
30
31
 
31
32
  Apply this filter to every candidate:
32
33
 
33
- - A state or variant (`selected`, `active`, `open`, `disabled`) is not a part.
34
+ - A state or variant (`selected`, `active`, `checked`, `disabled`) is not a part.
34
35
  Implement it as a prefixed design-state modifier on the affected part.
35
36
  - A hidden and shown version of the same element is one part, not two.
36
37
  - A grouping or layout-only wrapper is not a part. A wrapper that isn't
@@ -49,9 +50,10 @@ The manifest generator can silently omit a named part behind conditional JSX.
49
50
  Keep an editor-controlled part mounted in the render it inspects. Hide it with
50
51
  CSS driven by a data attribute, or disable it when it should remain visible.
51
52
  After generating the manifest, confirm that its `elements` include every
52
- intended named part.
53
+ intended named part and that each `elementProps.<key>` read in code equals the
54
+ camelCase of that part's global class.
53
55
 
54
- ## Plausibility Check
56
+ ## Sanity Check
55
57
 
56
58
  For each candidate, ask:
57
59
 
@@ -31,11 +31,11 @@ Use this pattern:
31
31
 
32
32
  ```tsx
33
33
  <div
34
- {...elementProps?.progressBar}
34
+ {...elementProps?.audioPlayerProgressBar}
35
35
  className={classNames(
36
36
  'audio-player-progress-bar',
37
37
  styles.progressBar,
38
- elementProps?.progressBar?.className,
38
+ elementProps?.audioPlayerProgressBar?.className,
39
39
  )}
40
40
  >
41
41
  {/* progress UI */}
@@ -51,6 +51,26 @@ the interaction contract. `pauseButtonVisibility` in an autoplaying component
51
51
  is the canonical exception: the editor preview must be able to force the
52
52
  play/pause control visible.
53
53
 
54
+ ## Lists of Groups
55
+
56
+ When each item of a list holds its own list, the Manage panel can't edit the inner list. Use fixed
57
+ groups instead and map them internally:
58
+
59
+ - Give each group flat top-level fields named after the group, so Manage
60
+ Items shows "Starters Dishes", not "Dishes 1".
61
+ - For a requested range ("2 to 5 sections"), create the maximum and add a
62
+ count prop that shows the first N.
63
+ - Group order is fixed. If the user asks to reorder groups, don't add an
64
+ order, slot, key or label field or a separate array to emulate it; say in
65
+ your reply that groups can't be reordered.
66
+
67
+ ```ts
68
+ // startersTitle: string; startersDishes: Array<Dish>
69
+ // /** @min 2 @max 5 */ sectionCount: number
70
+ // Not: sections: Array<{ dishes: Array<Dish> }>, starters: Section,
71
+ // or sections: Array<{ slot: number }> with dishes1…dishes5
72
+ ```
73
+
54
74
  ## Examples
55
75
 
56
76
  ### Content and Behavior Props
@@ -61,19 +61,23 @@ const SDK_HANDLER_PROPS = {
61
61
  onDoubleClick: 'onDblClick',
62
62
  onMouseEnter: 'onMouseIn',
63
63
  onMouseLeave: 'onMouseOut',
64
+ onFocus: 'onFocus',
65
+ onBlur: 'onBlur',
64
66
  };
65
67
  const isSdkRoot = (tag) => /\bid=\{\s*(?:props\.)?id\s*\}/.test(tag);
66
68
 
67
- /** Every handler on the tag forwards its SDK prop, `onClick={onClick}` or `onMouseEnter={props.onMouseIn}`; anything else makes the root a control. */
69
+ const getHandlerAttributes = (tag) => [...tag.matchAll(/\b(on[A-Z]\w*)=\{([^}]*)\}/g)];
70
+
71
+ /** `onClick={onClick}` or `onMouseEnter={props.onMouseIn}`. */
72
+ function isSdkForward(name, value) {
73
+ const sdkProp = SDK_HANDLER_PROPS[name];
74
+ return Boolean(sdkProp) && value.trim().replace(/^props\./, '') === sdkProp;
75
+ }
76
+
77
+ /** Every handler on the tag forwards its SDK prop; anything else makes the root a control. */
68
78
  function forwardsOnlySdkHandlers(tag) {
69
- const handlers = [...tag.matchAll(/\b(on[A-Z]\w*)=\{([^}]*)\}/g)];
70
- return (
71
- handlers.length > 0 &&
72
- handlers.every(([, name, value]) => {
73
- const sdkProp = SDK_HANDLER_PROPS[name];
74
- return Boolean(sdkProp) && value.trim().replace(/^props\./, '') === sdkProp;
75
- })
76
- );
79
+ const handlers = getHandlerAttributes(tag);
80
+ return handlers.length > 0 && handlers.every(([, name, value]) => isSdkForward(name, value));
77
81
  }
78
82
 
79
83
  const PASSIVE_AUTOPLAY_HANDLERS = new Set([
@@ -85,11 +89,26 @@ const PASSIVE_AUTOPLAY_HANDLERS = new Set([
85
89
  'onBlurCapture',
86
90
  ]);
87
91
 
88
- /** Carousel roots observe hover/focus to pause autoplay; these handlers do not activate the region. */
92
+ /**
93
+ * Carousel roots observe hover and focus to pause autoplay; these handlers do not activate
94
+ * the region. A root needs both, so hover-only roots stay flagged. SDK forwards may sit
95
+ * beside them; any other handler makes the root a control.
96
+ */
89
97
  function pausesAutoplayOnly(tag) {
90
- if (!isSdkRoot(tag) || !tag.includes('data-pause-button-visibility=')) return false;
91
- const handlers = [...tag.matchAll(/\b(on[A-Z]\w*)=\{[^}]*\}/g)];
92
- return handlers.length > 0 && handlers.every(([, name]) => PASSIVE_AUTOPLAY_HANDLERS.has(name));
98
+ if (!isSdkRoot(tag)) return false;
99
+ const handlers = getHandlerAttributes(tag);
100
+ // `onMouseEnter={onMouseIn}`, `onMouseEnter={onMouseEnter}` or `onFocus={onFocus}` forwards a prop; it is not a pause observer.
101
+ const isPauseObserver = (name, value) =>
102
+ PASSIVE_AUTOPLAY_HANDLERS.has(name) &&
103
+ !isSdkForward(name, value) &&
104
+ value.trim().replace(/^props\./, '') !== name;
105
+ const hasPauseObserver = (pattern) =>
106
+ handlers.some(([, name, value]) => pattern.test(name) && isPauseObserver(name, value));
107
+ return (
108
+ hasPauseObserver(/^onMouse/) &&
109
+ hasPauseObserver(/^on(?:Focus|Blur)/) &&
110
+ handlers.every(([, name, value]) => isPauseObserver(name, value) || isSdkForward(name, value))
111
+ );
93
112
  }
94
113
 
95
114
  /** Roles whose native element cannot express a styled component. */
@@ -112,9 +131,13 @@ function roleHasNoNativeTag(tag) {
112
131
  */
113
132
  const EXEMPTIONS = [
114
133
  {
115
- // ANIMATED-COMPONENTS.md requires the noninteractive carousel root to
116
- // observe hover and focus so autoplay stops while a visitor is using it.
117
- rules: new Set(['jsx-a11y/no-noninteractive-element-interactions']),
134
+ // ANIMATED-COMPONENTS.md requires the carousel root to observe hover and
135
+ // focus so autoplay pauses while a visitor is using it.
136
+ rules: new Set([
137
+ 'jsx-a11y/click-events-have-key-events',
138
+ 'jsx-a11y/no-noninteractive-element-interactions',
139
+ 'jsx-a11y/no-static-element-interactions',
140
+ ]),
118
141
  tag: pausesAutoplayOnly,
119
142
  },
120
143
  {
@@ -0,0 +1,45 @@
1
+ {
2
+ "schemaVersion": 1,
3
+ "about": "Choices no SDK index can make. sdk-lookup.cjs prints a rule when one of its entities matches. `choice: site` means the namespace depends on the site, not on the install.",
4
+ "rules": [
5
+ {
6
+ "entities": ["product", "products", "catalog"],
7
+ "namespaces": ["products", "productsV3"],
8
+ "choice": "site",
9
+ "note": "Call catalogVersioning.getCatalogVersion() first: V3 sites use productsV3, V1 sites use products. Support both paths, and request both scope sets plus SCOPE.STORES.CATALOG_READ_LIMITED. To search by name, V3 has productsV3.searchProducts() (queryProducts cannot filter on name); V1 has no search method, and its builder takes name only with startsWith.",
10
+ "source": "SKILL.md (Stores), references/STORES_VERSIONING.md, references/stores/QUERY.md"
11
+ },
12
+ {
13
+ "entities": ["inventory"],
14
+ "namespaces": ["inventory", "inventoryItemsV3"],
15
+ "choice": "site",
16
+ "note": "Call catalogVersioning.getCatalogVersion() first: V3 sites use inventoryItemsV3, V1 sites use inventory. Request both scope sets.",
17
+ "source": "references/STORES_VERSIONING.md"
18
+ },
19
+ {
20
+ "entities": ["refund", "refunds"],
21
+ "namespaces": ["orders", "orderTransactions", "refunds"],
22
+ "choice": { "prefer": "orders", "intents": ["search", "read"] },
23
+ "note": "Refunded orders: orders.searchOrders with a paymentStatus filter, then orderTransactions.listTransactionsForMultipleOrders(ids) once for the visible page. Refund events are the exception: they live in @wix/payments (namespace refunds), not in @wix/ecom.",
24
+ "source": "references/dashboard-page/DATA_SOURCES.md, references/backend-event/COMMON-EVENTS.md"
25
+ },
26
+ {
27
+ "entities": ["location", "locations"],
28
+ "namespaces": ["locations"],
29
+ "note": "queryLocations is admin-only although its scope is SCOPE.DC-MULTILOCATION.READ-LOCATIONS: route it to a backend endpoint and elevate it there.",
30
+ "source": "moved from SKILL.md (Identity and Elevation Requirement), which links here"
31
+ },
32
+ {
33
+ "entities": ["member", "members"],
34
+ "namespaces": ["members"],
35
+ "note": "getMember and queryMembers withhold PRIVATE members from visitor and member callers: never elevate them to fill a sparse result.",
36
+ "source": "SKILL.md (Identity and Elevation Requirement)"
37
+ },
38
+ {
39
+ "entities": ["cart", "currentcart"],
40
+ "namespaces": ["currentCartV2", "cartV2"],
41
+ "note": "currentCartV2.addLineItemsToCurrentCart is visitor-callable although its scope is SCOPE.ECOM.MANAGE-ADMIN. Never elevate it.",
42
+ "source": "moved from SKILL.md (Identity and Elevation Requirement), which links here"
43
+ }
44
+ ]
45
+ }