@contentful/experience-design-system-cli 2.26.6-dev-build-ef7b167.0 → 2.26.6-dev-build-8dec003.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/dist/package.json +3 -3
- package/dist/src/index.js +37 -8
- package/dist/src/session/db.d.ts +1 -0
- package/package.json +7 -7
- package/skills/generate-components.md +35 -35
package/dist/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.26.6-dev-build-
|
|
3
|
+
"version": "2.26.6-dev-build-8dec003.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"commander": "^13.1.0",
|
|
45
45
|
"ink": "^4.4.1",
|
|
46
46
|
"react": "^18.3.1",
|
|
47
|
-
"react-devtools-core": "^4.
|
|
47
|
+
"react-devtools-core": "^4.19.1",
|
|
48
48
|
"react-dom": "^18.3.1",
|
|
49
49
|
"svelte": "^5.56.4",
|
|
50
50
|
"ts-morph": "^27.0.2",
|
|
@@ -56,7 +56,7 @@
|
|
|
56
56
|
"@contentful/experience-design-system-generation": "workspace:*",
|
|
57
57
|
"@tsconfig/node24": "^24.0.4",
|
|
58
58
|
"@types/node": "^24.0.3",
|
|
59
|
-
"@types/react": "^18.3.
|
|
59
|
+
"@types/react": "^18.3.24",
|
|
60
60
|
"eslint": "^9.39.5",
|
|
61
61
|
"eslint-config-prettier": "^10.1.8",
|
|
62
62
|
"eslint-plugin-prettier": "^5.5.6",
|
package/dist/src/index.js
CHANGED
|
@@ -118,6 +118,16 @@ function parseToolCallLines(stdout) {
|
|
|
118
118
|
if (typeof rec.reason === "string")
|
|
119
119
|
call.reason = rec.reason;
|
|
120
120
|
calls.push(call);
|
|
121
|
+
} else if (tool === "exclude_prop") {
|
|
122
|
+
if (typeof rec.prop !== "string" || !rec.prop) {
|
|
123
|
+
warnings.push("exclude_prop missing prop name \u2014 skipped");
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
calls.push({
|
|
127
|
+
tool: "exclude_prop",
|
|
128
|
+
prop: rec.prop,
|
|
129
|
+
reason: typeof rec.reason === "string" ? rec.reason : ""
|
|
130
|
+
});
|
|
121
131
|
} else if (tool === "classify_component") {
|
|
122
132
|
const call = { tool: "classify_component" };
|
|
123
133
|
if (typeof rec.description === "string")
|
|
@@ -373,7 +383,7 @@ var init_agent_runner = __esm({
|
|
|
373
383
|
"use strict";
|
|
374
384
|
init_agent_names();
|
|
375
385
|
VALID_SELECT_TOOL_NAMES = /* @__PURE__ */ new Set(["select_component", "reject_component"]);
|
|
376
|
-
VALID_TOOL_NAMES = /* @__PURE__ */ new Set(["classify_prop", "classify_component", "classify_slot"]);
|
|
386
|
+
VALID_TOOL_NAMES = /* @__PURE__ */ new Set(["classify_prop", "exclude_prop", "classify_component", "classify_slot"]);
|
|
377
387
|
VALID_TOKEN_TOOL_NAMES = /* @__PURE__ */ new Set(["set_token", "set_group"]);
|
|
378
388
|
VALID_CDF_TYPES = /* @__PURE__ */ new Set(["string", "richtext", "media", "enum", "token", "boolean"]);
|
|
379
389
|
VALID_CATEGORIES = /* @__PURE__ */ new Set(["content", "design", "state"]);
|
|
@@ -542,26 +552,28 @@ All input data is provided inline below \u2014 do not read any additional files.
|
|
|
542
552
|
|
|
543
553
|
Do NOT write any files or emit any JSON blobs. Instead, emit one JSON object per line to stdout for each classification decision. The CLI reads your stdout line by line and writes each decision directly to the pipeline database.
|
|
544
554
|
|
|
545
|
-
The
|
|
555
|
+
The four tool calls you may emit are:
|
|
546
556
|
|
|
547
557
|
\`\`\`
|
|
548
558
|
{"tool":"classify_component","description":"<optional component-level description>","rationale":{"description":"<why this component is classified the way it is>","props":"<why these props were chosen>","slots":"<why these slots were chosen>"}}
|
|
549
559
|
|
|
550
560
|
{"tool":"classify_prop","prop":"<propName>","cdf_type":"<type>","cdf_category":"<category>","required":<bool>,"description":"<short customer-facing description>","reason":"<full internal rationale; not customer-facing>","values":["a","b"],"token_kind":"color","default":"<value>"}
|
|
551
561
|
|
|
562
|
+
{"tool":"exclude_prop","prop":"<propName>","reason":"<why excluded>"}
|
|
563
|
+
|
|
552
564
|
{"tool":"classify_slot","slot":"<slotName>","required":<bool>,"allowed_components":["ComponentName"],"description":"<reason>","rationale":"<why this slot was kept in the catalog>"}
|
|
553
565
|
\`\`\`
|
|
554
566
|
|
|
555
567
|
Rules:
|
|
556
568
|
- Emit exactly one JSON object per line. No multi-line JSON. No markdown fences around the lines.
|
|
557
|
-
- Every prop in the input must have exactly one classify_prop
|
|
569
|
+
- Every prop in the input must have exactly one call: either classify_prop or exclude_prop.
|
|
558
570
|
- Every slot in the input must have exactly one classify_slot call.
|
|
559
571
|
- Valid cdf_type values: string, richtext, media, enum, token, boolean
|
|
560
572
|
- Valid cdf_category values: content, design, state
|
|
561
573
|
- For enum type, always include "values" (non-empty string array).
|
|
562
574
|
- For token type, always include "token_kind" (DTCG $type, e.g. "color").
|
|
563
575
|
- href and URL props \u2192 cdf_type "string", cdf_category "content". Do NOT use cdf_type "link" \u2014 it is not valid.
|
|
564
|
-
- Framework internals (ref, event handlers, test IDs) \u2192
|
|
576
|
+
- Framework internals (ref, event handlers, test IDs) \u2192 exclude_prop.
|
|
565
577
|
- CSS design props (className, style, styles, positional/geometric props: top, bottom, left, right, rotation, offset, etc.) \u2192 classify_prop, cdf_type: "string", cdf_category: "design".
|
|
566
578
|
- On classify_component, "rationale" fields are operator-facing (read-only) but may surface in customer-facing exports. The "rationale.description" field is subject to the description content rules in the skill prompt (no internal initiative names). "rationale.props" and "rationale.slots" describe your reasoning about scope; "classify_slot.rationale" explains why each slot was kept.
|
|
567
579
|
- On classify_prop, "reason" is REQUIRED and is the LLM's internal rationale \u2014 shown to the developer reviewing the import, never to end-users. "description" is the customer-facing copy and is subject to the description content rules in the skill prompt. Keep them distinct: "description" is short and customer-facing; "reason" explains your reasoning in detail.
|
|
@@ -8720,11 +8732,16 @@ function applyToolCalls(db, sessionId2, componentId, componentName, calls, incom
|
|
|
8720
8732
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
8721
8733
|
const warnings = [...incomingWarnings];
|
|
8722
8734
|
let classified = 0;
|
|
8735
|
+
let excluded = 0;
|
|
8723
8736
|
let slots = 0;
|
|
8724
8737
|
const updateProp = db.prepare(
|
|
8725
8738
|
`UPDATE raw_props SET cdf_type = ?, cdf_category = ?, cdf_token_kind = ?, required = ?, description = ?, rationale = ?
|
|
8726
8739
|
WHERE session_id = ? AND component_id = ? AND name = ?`
|
|
8727
8740
|
);
|
|
8741
|
+
const clearProp = db.prepare(
|
|
8742
|
+
`UPDATE raw_props SET cdf_type = 'excluded', cdf_category = NULL, cdf_token_kind = NULL, rationale = ?
|
|
8743
|
+
WHERE session_id = ? AND component_id = ? AND name = ?`
|
|
8744
|
+
);
|
|
8728
8745
|
const deleteAllowedValues = db.prepare(
|
|
8729
8746
|
`DELETE FROM raw_prop_allowed_values WHERE session_id = ? AND component_id = ? AND prop_name = ?`
|
|
8730
8747
|
);
|
|
@@ -8801,6 +8818,9 @@ function applyToolCalls(db, sessionId2, componentId, componentName, calls, incom
|
|
|
8801
8818
|
).run(storedDefault, sessionId2, componentId, call.prop);
|
|
8802
8819
|
}
|
|
8803
8820
|
classified++;
|
|
8821
|
+
} else if (call.tool === "exclude_prop") {
|
|
8822
|
+
clearProp.run(call.reason || null, sessionId2, componentId, call.prop);
|
|
8823
|
+
excluded++;
|
|
8804
8824
|
} else if (call.tool === "classify_slot") {
|
|
8805
8825
|
const slotRequired = call.required !== void 0 ? call.required ? 1 : 0 : 1;
|
|
8806
8826
|
const slotChanges = updateSlot.run(
|
|
@@ -8840,7 +8860,7 @@ function applyToolCalls(db, sessionId2, componentId, componentName, calls, incom
|
|
|
8840
8860
|
db.exec("ROLLBACK");
|
|
8841
8861
|
throw e;
|
|
8842
8862
|
}
|
|
8843
|
-
return { classified, slots, warnings };
|
|
8863
|
+
return { classified, excluded, slots, warnings };
|
|
8844
8864
|
}
|
|
8845
8865
|
function getOrCreateSession(db, sessionFlag, sessionName, _hints) {
|
|
8846
8866
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
@@ -9315,8 +9335,6 @@ function loadCDFComponents(db, sessionId2) {
|
|
|
9315
9335
|
).all(sessionId2);
|
|
9316
9336
|
if (components.length === 0) return [];
|
|
9317
9337
|
const props = db.prepare(
|
|
9318
|
-
// Guards against stale rows in session DBs persisted before props
|
|
9319
|
-
// were guaranteed to always be classified into content/design/state.
|
|
9320
9338
|
`SELECT component_id, name, required, default_value, description,
|
|
9321
9339
|
cdf_type, cdf_category, cdf_token_kind, position
|
|
9322
9340
|
FROM raw_props
|
|
@@ -28468,6 +28486,11 @@ function formatToolCall(obj) {
|
|
|
28468
28486
|
const cat = String(obj["cdf_category"] ?? "");
|
|
28469
28487
|
return ` ${c.green("+")} ${prop} ${c.dim(`${type} ${cat}`)}`;
|
|
28470
28488
|
}
|
|
28489
|
+
case "exclude_prop": {
|
|
28490
|
+
const prop = String(obj["prop"] ?? "");
|
|
28491
|
+
const reason = typeof obj["reason"] === "string" ? obj["reason"] : "";
|
|
28492
|
+
return ` ${c.dim("\u2013")} ${prop} ${c.dim(reason)}`;
|
|
28493
|
+
}
|
|
28471
28494
|
case "classify_slot": {
|
|
28472
28495
|
const slot = String(obj["slot"] ?? "");
|
|
28473
28496
|
const desc = typeof obj["description"] === "string" ? obj["description"] : "";
|
|
@@ -30973,6 +30996,7 @@ async function runOneComponent(agent, model, db, sessionId2, component, tokensIn
|
|
|
30973
30996
|
return {
|
|
30974
30997
|
componentName: component.name,
|
|
30975
30998
|
classified: 0,
|
|
30999
|
+
excluded: 0,
|
|
30976
31000
|
slots: 0,
|
|
30977
31001
|
warnings: [],
|
|
30978
31002
|
failed: false,
|
|
@@ -30988,6 +31012,7 @@ async function runOneComponent(agent, model, db, sessionId2, component, tokensIn
|
|
|
30988
31012
|
return {
|
|
30989
31013
|
componentName: component.name,
|
|
30990
31014
|
classified: 0,
|
|
31015
|
+
excluded: 0,
|
|
30991
31016
|
slots: 0,
|
|
30992
31017
|
warnings: [`${component.name}: source changed but human edits preserved`],
|
|
30993
31018
|
failed: false,
|
|
@@ -31056,6 +31081,7 @@ ${outputBuf}`);
|
|
|
31056
31081
|
return {
|
|
31057
31082
|
componentName: component.name,
|
|
31058
31083
|
classified: 0,
|
|
31084
|
+
excluded: 0,
|
|
31059
31085
|
slots: 0,
|
|
31060
31086
|
warnings: [],
|
|
31061
31087
|
failed: true,
|
|
@@ -31080,6 +31106,7 @@ ${outputBuf}`);
|
|
|
31080
31106
|
return {
|
|
31081
31107
|
componentName: component.name,
|
|
31082
31108
|
classified: applied.classified,
|
|
31109
|
+
excluded: applied.excluded,
|
|
31083
31110
|
slots: applied.slots,
|
|
31084
31111
|
warnings: applied.warnings,
|
|
31085
31112
|
failed: false,
|
|
@@ -31089,6 +31116,7 @@ ${outputBuf}`);
|
|
|
31089
31116
|
return {
|
|
31090
31117
|
componentName: component.name,
|
|
31091
31118
|
classified: 0,
|
|
31119
|
+
excluded: 0,
|
|
31092
31120
|
slots: 0,
|
|
31093
31121
|
warnings: [],
|
|
31094
31122
|
failed: true,
|
|
@@ -31314,11 +31342,12 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
31314
31342
|
}
|
|
31315
31343
|
}
|
|
31316
31344
|
const totalClassified = generated.reduce((s, r) => s + r.classified, 0);
|
|
31345
|
+
const totalExcluded = generated.reduce((s, r) => s + r.excluded, 0);
|
|
31317
31346
|
const totalRenamedSlots = componentResults.reduce((s, r) => s + r.renamedSlotsCount, 0);
|
|
31318
31347
|
const allOk = failed.length === 0;
|
|
31319
31348
|
const cachedNote = cachedResults.length > 0 ? c.dim(` (${cachedResults.length} cached)`) : "";
|
|
31320
31349
|
process.stderr.write(
|
|
31321
|
-
(allOk ? c.green("\u2713") : c.yellow("\u26A0")) + ` ${generated.length + cachedResults.length}/${componentResults.length} components` + cachedNote + c.dim(` ${totalClassified} classified`) + "\n"
|
|
31350
|
+
(allOk ? c.green("\u2713") : c.yellow("\u26A0")) + ` ${generated.length + cachedResults.length}/${componentResults.length} components` + cachedNote + c.dim(` ${totalClassified} classified, ${totalExcluded} excluded`) + "\n"
|
|
31322
31351
|
);
|
|
31323
31352
|
process.stdout.write(`renamed-slots: ${totalRenamedSlots}
|
|
31324
31353
|
`);
|
package/dist/src/session/db.d.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.26.6-dev-build-
|
|
3
|
+
"version": "2.26.6-dev-build-8dec003.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,26 +34,26 @@
|
|
|
34
34
|
"commander": "^13.1.0",
|
|
35
35
|
"ink": "^4.4.1",
|
|
36
36
|
"react": "^18.3.1",
|
|
37
|
-
"react-devtools-core": "^4.
|
|
37
|
+
"react-devtools-core": "^4.19.1",
|
|
38
38
|
"react-dom": "^18.3.1",
|
|
39
39
|
"svelte": "^5.56.4",
|
|
40
40
|
"ts-morph": "^27.0.2",
|
|
41
41
|
"typescript": "^5.9.3",
|
|
42
|
-
"@contentful/experience-design-system-types": "2.26.6-dev-build-
|
|
42
|
+
"@contentful/experience-design-system-types": "2.26.6-dev-build-8dec003.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@tsconfig/node24": "^24.0.4",
|
|
46
46
|
"@types/node": "^24.0.3",
|
|
47
|
-
"@types/react": "^18.3.
|
|
47
|
+
"@types/react": "^18.3.24",
|
|
48
48
|
"eslint": "^9.39.5",
|
|
49
49
|
"eslint-config-prettier": "^10.1.8",
|
|
50
50
|
"eslint-plugin-prettier": "^5.5.6",
|
|
51
51
|
"ink-testing-library": "^4.0.0",
|
|
52
52
|
"typescript-eslint": "^8.67.0",
|
|
53
53
|
"vitest": "^4.0.16",
|
|
54
|
-
"@contentful/experience-design-system-
|
|
55
|
-
"@contentful/experience-design-system-
|
|
56
|
-
"@contentful/experience-design-system-
|
|
54
|
+
"@contentful/experience-design-system-client": "2.26.6-dev-build-8dec003.0",
|
|
55
|
+
"@contentful/experience-design-system-generation": "2.26.6-dev-build-8dec003.0",
|
|
56
|
+
"@contentful/experience-design-system-extraction": "2.26.6-dev-build-8dec003.0"
|
|
57
57
|
},
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
@@ -72,21 +72,21 @@ The CLI assembles your output into CDF (Component Definition Format), a JSON sch
|
|
|
72
72
|
|
|
73
73
|
Emit one JSON object per line. The CLI parses lines starting with `{`. Lines not starting with `{` are treated as prose and ignored by the parser — use them freely for reasoning.
|
|
74
74
|
|
|
75
|
-
**
|
|
75
|
+
**Four tool calls:**
|
|
76
76
|
|
|
77
77
|
```
|
|
78
|
-
{"tool":"classify_component","description":"<required: one-sentence description of the component>","rationale":{"description":"<why this component is classified this way>","props":"<why these props were
|
|
78
|
+
{"tool":"classify_component","description":"<required: one-sentence description of the component>","rationale":{"description":"<why this component is classified this way>","props":"<why these props were chosen / excluded>","slots":"<why these slots were chosen / excluded>"}}
|
|
79
79
|
|
|
80
80
|
{"tool":"classify_prop","prop":"<propName>","cdf_type":"<type>","cdf_category":"<category>","required":<bool>,"description":"<short customer-facing description>","reason":"<full internal rationale; not customer-facing>","values":["a","b"],"token_kind":"color","default":"<value>"}
|
|
81
81
|
|
|
82
|
+
{"tool":"exclude_prop","prop":"<propName>","reason":"<why excluded>"}
|
|
83
|
+
|
|
82
84
|
{"tool":"classify_slot","slot":"<slotName>","required":<bool>,"allowed_components":["ComponentName"],"description":"<short customer-facing description>","rationale":"<why this slot was kept / its role>"}
|
|
83
85
|
```
|
|
84
86
|
|
|
85
|
-
Every prop — including framework internals, DOM/accessibility pass-through, and props with no clean flat representation — must produce a `classify_prop` call. Props that are not marketer-configurable are classified with `cdf_category: "state"`: they still ship in the CDF (visible in review, editable if someone wants to override the category), but stay unattached from the content/design editing surfaces by default. See "Valid cdf_category values" below for exactly what falls into `state`.
|
|
86
|
-
|
|
87
87
|
**Rules:**
|
|
88
88
|
- Emit exactly one JSON object per line. No multi-line JSON.
|
|
89
|
-
- Every prop in the input must produce exactly one
|
|
89
|
+
- Every prop in the input must produce exactly one call: `classify_prop` OR `exclude_prop`.
|
|
90
90
|
- Every slot must produce exactly one `classify_slot` call.
|
|
91
91
|
- Emit `classify_component` once at the start (required). The `description` field is **required** — always provide a brief description of the component's purpose.
|
|
92
92
|
- `values` is required for `cdf_type: "enum"` — must be a non-empty string array.
|
|
@@ -135,7 +135,7 @@ Exactly **6** valid types:
|
|
|
135
135
|
|---|---|
|
|
136
136
|
| `content` | Data the component *displays* — what a copywriter or editor fills in: text, labels, headings, body copy, rich text, images, media, URLs, link targets, counts, locale |
|
|
137
137
|
| `design` | Values that control *how the component looks* — what a designer sets: color, size (sm/md/lg), variant (primary/secondary/ghost), layout orientation, alignment, background, visual toggles (imageOnLeft, enableEffect), design tokens |
|
|
138
|
-
| `state` |
|
|
138
|
+
| `state` | Runtime behavioral or interactive flags — not visible in the editor's design or content panel: disabled, loading, expanded, isOpen, isSearchVisible, preview, identifiers used for analytics/tracking (componentId, sectionKey, componentName) |
|
|
139
139
|
|
|
140
140
|
The pre-classified `category` in the raw input is a starting point — correct it when it is wrong. Contentful uses this category to decide where the property appears in the editor UI, so accuracy matters.
|
|
141
141
|
|
|
@@ -145,21 +145,21 @@ The pre-classified `category` in the raw input is a starting point — correct i
|
|
|
145
145
|
|
|
146
146
|
For each `RawPropDefinition`, apply in order:
|
|
147
147
|
|
|
148
|
-
1. **Framework / DOM / accessibility pass-through?** → `
|
|
148
|
+
1. **Framework / DOM / accessibility pass-through?** → `exclude_prop`. These are escape hatches for developers, not configurable surfaces for marketers. Exposing them in the ExO editor adds noise that obscures the props that actually carry intent. Always exclude:
|
|
149
149
|
- Framework internals: `ref`, `innerRef`, event handlers (any `onSomething`), `testId`, `data-testid`, `key`
|
|
150
150
|
- DOM pass-through: `className`, `class`, `classes`, `classNames`, `rootClassName`, `prefixCls`, `style`, `styles`, `id`, `role`, `tabIndex`, `htmlFor`, `for`, `slot`, `is`, `lang`, `dir`, `hidden`, `draggable`, `spellCheck`, `contentEditable`, `inputMode`, `autoComplete`, `autoFocus`, `translate`, `part`, `exportparts`
|
|
151
151
|
- Accessibility pass-through: any `aria-*` or `ariaSomething` prop (including bare `aria` as an aria-attributes object), `aria-label`, `aria-hidden`, `aria-describedby`, `aria-controls`
|
|
152
152
|
- Data attributes: any `data-*` prop
|
|
153
153
|
- **Polymorphic component props**: `as`, `element`, `component` (when typed as an HTML tag string or component reference) — these change rendered HTML, not marketer-visible behavior
|
|
154
154
|
- **Framework theming / pass-through escape hatches**: PrimeVue's `dt` / `pt` / `ptOptions` / `unstyled`, MUI/Chakra-style `sx`, anything explicitly typed as a developer "override" / "passthrough" object
|
|
155
|
-
- **Important caveat**: only
|
|
156
|
-
2. **Common semantic props — classify
|
|
155
|
+
- **Important caveat**: only exclude when the prop is one of these *as the bare HTML attribute or framework-internal pass-through*. Compound names like `fileName`, `displayName`, `dataset`, `dataSource`, `roleDescription`, `idLabel` are not pass-through — classify them normally.
|
|
156
|
+
2. **Common semantic props — DO classify, do not exclude.** The LLM has been over-excluding these because they sound like framework internals; they are not. Classify each per the rest of this tree:
|
|
157
157
|
- `icon` / `leftIcon` / `rightIcon` / `prefixIcon` / `suffixIcon` — slot or `string` (icon name); see slot guidance below
|
|
158
|
-
- `items` / `options` / `actions` / `links` — usually array content; if the element shape is simple, classify as `string` (comma-separated names/IDs) and note in `description`. Only
|
|
159
|
-
- `value` (the bare prop, not `modelValue`) — content prop, usually `string` (or `enum` if from a fixed set). Note: Vue's `modelValue` / `modelModifiers` are
|
|
158
|
+
- `items` / `options` / `actions` / `links` — usually array content; if the element shape is simple, classify as `string` (comma-separated names/IDs) and note in `description`. Only exclude when elements are deep nested objects with no flat representation.
|
|
159
|
+
- `value` (the bare prop, not `modelValue`) — content prop, usually `string` (or `enum` if from a fixed set). Note: Vue's `modelValue` / `modelModifiers` are excluded by pre-classify because they're v-model framework wiring.
|
|
160
160
|
- `name` — content prop, usually `string`. Treat it as semantic component data, not as a DOM pass-through.
|
|
161
161
|
- `form` (when not the literal `<form>` HTML attribute) — typically content; classify as `string` unless it's a complex form-config object
|
|
162
|
-
- `inputId` / `componentId` — these CAN be content (anchor IDs, marketer-set tracking refs). Classify as `string`, `cdf_category: "content"` when the type is a plain string. Only
|
|
162
|
+
- `inputId` / `componentId` — these CAN be content (anchor IDs, marketer-set tracking refs). Classify as `string`, `cdf_category: "content"` when the type is a plain string. Only exclude if the prop is clearly internal (e.g. typed as a generated React ID).
|
|
163
163
|
- `accessibleNameRef` / `accessibleDescriptionRef` (web components) — these are ID references for a11y wiring; classify as `string`, `cdf_category: "state"` (behavioral wiring, not design or content).
|
|
164
164
|
- `eventDetails` / similar telemetry props — `cdf_category: "state"`.
|
|
165
165
|
3. **Positional/geometric design prop?** (`top`, `bottom`, `left`, `right`, `rotation`, `offset`, `zIndex`) → `classify_prop`, `cdf_type: "string"`, `cdf_category: "design"`.
|
|
@@ -169,13 +169,13 @@ For each `RawPropDefinition`, apply in order:
|
|
|
169
169
|
7. **Raw type is `string` / `number` / `boolean`?** → For `boolean`, use `cdf_type: "boolean"` with `default: true` or `false` (native boolean). For `number`, use `cdf_type: "string"` with `default` as the numeric value as a string (e.g. `"0"`). For `string`, use `cdf_type: "string"`.
|
|
170
170
|
8. **Media/image type** (`ImageProps`, `MediaSource`, asset types)? → `cdf_type: "media"`.
|
|
171
171
|
9. **Rich text / markup** (`ReactNode` used as content, HTML string)? → `cdf_type: "richtext"`.
|
|
172
|
-
10. **Complex type — resolve before
|
|
172
|
+
10. **Complex type — resolve before excluding** (see below).
|
|
173
173
|
|
|
174
174
|
---
|
|
175
175
|
|
|
176
|
-
## Resolving complex types — do not
|
|
176
|
+
## Resolving complex types — do not exclude without reasoning
|
|
177
177
|
|
|
178
|
-
A prop with a complex TypeScript type is **not automatically
|
|
178
|
+
A prop with a complex TypeScript type is **not automatically excluded**. Many props that appear complex carry real marketer-configurable information. Before excluding, ask: *"Could a marketer set this value in Contentful?"* If yes, classify it.
|
|
179
179
|
|
|
180
180
|
**Common resolvable patterns:**
|
|
181
181
|
|
|
@@ -184,25 +184,25 @@ A prop with a complex TypeScript type is **not automatically unattached**. Many
|
|
|
184
184
|
| `'primary' \| 'secondary' \| 'ghost'` (union of literals) | → `enum`, extract `values` |
|
|
185
185
|
| `HeadingSize` / `ButtonVariant` / any named type that is clearly a finite set of visual options | → `enum`, infer likely values from the prop name and context (e.g. `['sm', 'md', 'lg']` for size, `['primary', 'secondary']` for variant). Document your inference in `description`. |
|
|
186
186
|
| `Variant` / `variant` prop | Usually a visual design variant. → `enum`, `cdf_category: "design"`. Infer values from context. |
|
|
187
|
-
| `Section[]` / array of custom items where the structure is unclear | → `
|
|
188
|
-
| `ExperienceConfiguration<Variant>` / deep generic | Personalization config — → `
|
|
189
|
-
| `React.Dispatch<...>` / setter | State setter — → `
|
|
190
|
-
| `React.RefObject<...>` / `ref` | → `
|
|
191
|
-
| `() => void` / callback | → `
|
|
187
|
+
| `Section[]` / array of custom items where the structure is unclear | → `exclude_prop` only if the array elements are complex objects with no obvious flat representation. If items are simple (title, label, id), consider representing as `string` (a comma-separated IDs or keys) or note in `description` why. |
|
|
188
|
+
| `ExperienceConfiguration<Variant>` / deep generic | Personalization config — → `exclude_prop`, reason: `"personalization configuration — framework internal"` |
|
|
189
|
+
| `React.Dispatch<...>` / setter | State setter — → `exclude_prop`, reason: `"React state setter — framework internal"` |
|
|
190
|
+
| `React.RefObject<...>` / `ref` | → `exclude_prop`, reason: `"ref — framework internal"` |
|
|
191
|
+
| `() => void` / callback | → `exclude_prop`, reason: `"callback function — framework internal"` |
|
|
192
192
|
| `ReactNode` used as a slot-like prop (children, `icon`, `footer`) | → classify as a `slot` if it represents an injectable area, or `richtext` if it is inline markup content |
|
|
193
193
|
| `boolean` with a name like `hideChevron`, `imageOnLeft`, `enableBackgroundColorEffect` | → `boolean`, `cdf_category: "design"`, `default: true` or `false` — these control visual appearance |
|
|
194
194
|
| `boolean` with a name like `preview`, `hideContentForPersonalization` | → `boolean`, `cdf_category: "state"`, `default: false` — these control behavior |
|
|
195
195
|
| `string` used as a `componentId`, `sectionKey`, `componentName` | → `string`, `cdf_category: "state"` — these are identifiers for tracking/lookup |
|
|
196
196
|
| `string` locale (e.g. `locale: string`) | → `string`, `cdf_category: "state"` — locale is a behavioral/routing value |
|
|
197
197
|
|
|
198
|
-
**When to
|
|
198
|
+
**When to finally exclude:**
|
|
199
199
|
- The type is a callback signature or event handler
|
|
200
200
|
- The type is a React ref
|
|
201
201
|
- The type is a React state setter (`Dispatch`)
|
|
202
202
|
- The type is a deep generic used for personalization/A-B testing platform config (e.g. `ExperienceConfiguration<T>`)
|
|
203
203
|
- The type is an array of rich objects where no flat representation makes sense for a marketer
|
|
204
204
|
|
|
205
|
-
|
|
205
|
+
If you exclude a prop that could have been classified, the marketer loses the ability to configure it in Contentful. Prefer classifying with a reasonable inference over excluding.
|
|
206
206
|
|
|
207
207
|
---
|
|
208
208
|
|
|
@@ -228,8 +228,8 @@ Rules for nested objects:
|
|
|
228
228
|
- Flatten to max depth 2 (e.g., `item_nested_deep` is acceptable, deeper is not)
|
|
229
229
|
- Each leaf field gets its own classify_prop call with underscore-joined name
|
|
230
230
|
- Apply the same classification rules as top-level props
|
|
231
|
-
- If the object has > 10 fields, classify the most important 10
|
|
232
|
-
- If the object type cannot be resolved (opaque generic, imported interface without visible fields),
|
|
231
|
+
- If the object has > 10 fields, classify the most important 10 and exclude the rest
|
|
232
|
+
- If the object type cannot be resolved (opaque generic, imported interface without visible fields), exclude the parent prop with reason "opaque nested type"
|
|
233
233
|
|
|
234
234
|
---
|
|
235
235
|
|
|
@@ -307,17 +307,17 @@ Input:
|
|
|
307
307
|
Output:
|
|
308
308
|
```
|
|
309
309
|
Starting Button classification — 5 props, 1 slot
|
|
310
|
-
{"tool":"classify_component","description":"Primary action button with variant and state support","rationale":{"description":"Button is an atom — a single interactive control that triggers an action. It carries a label, a small set of visual variants, and a disabled flag, which is the minimal surface a marketer needs to configure a call-to-action.","props":"Kept label (content), variant (enum, design), disabled (boolean, state), and className (string, design escape hatch).
|
|
310
|
+
{"tool":"classify_component","description":"Primary action button with variant and state support","rationale":{"description":"Button is an atom — a single interactive control that triggers an action. It carries a label, a small set of visual variants, and a disabled flag, which is the minimal surface a marketer needs to configure a call-to-action.","props":"Kept label (content), variant (enum, design), disabled (boolean, state), and className (string, design escape hatch). Excluded onClick because it is an event handler — framework-internal and not configurable in Contentful.","slots":"Kept the icon slot as optional because the button renders correctly without it and the icon is purely decorative."}}
|
|
311
311
|
label is a required string content prop
|
|
312
312
|
{"tool":"classify_prop","prop":"label","cdf_type":"string","cdf_category":"content","required":true,"description":"Button label text"}
|
|
313
313
|
variant is a string union — enum type, category design
|
|
314
314
|
{"tool":"classify_prop","prop":"variant","cdf_type":"enum","cdf_category":"design","required":false,"values":["primary","secondary","ghost"],"default":"primary","description":"Visual variant"}
|
|
315
315
|
disabled is a boolean state prop — raw category says design, correcting to state
|
|
316
316
|
{"tool":"classify_prop","prop":"disabled","cdf_type":"boolean","cdf_category":"state","required":false,"default":false,"description":"Disables the button"}
|
|
317
|
-
onClick is an event handler — framework internal
|
|
318
|
-
{"tool":"
|
|
317
|
+
onClick is an event handler — framework internal
|
|
318
|
+
{"tool":"exclude_prop","prop":"onClick","reason":"event handler — framework internal"}
|
|
319
319
|
className is a DOM pass-through — developers wire CSS, marketers never set this
|
|
320
|
-
{"tool":"
|
|
320
|
+
{"tool":"exclude_prop","prop":"className","reason":"DOM pass-through — not a marketer-configurable surface"}
|
|
321
321
|
icon slot is clearly optional (decorative leading icon)
|
|
322
322
|
{"tool":"classify_slot","slot":"icon","required":false,"description":"Optional leading icon","rationale":"Icon is a decorative leading glyph — optional because the button reads cleanly without it, but kept as a slot so marketers can inject a brand-specific icon component when desired."}
|
|
323
323
|
```
|
|
@@ -381,8 +381,8 @@ href is a URL string — cdf_type string (not link), category content
|
|
|
381
381
|
|
|
382
382
|
## Edge cases
|
|
383
383
|
|
|
384
|
-
- **Prop with unresolvable type** (generics, intersection, callback) → `
|
|
385
|
-
- **Component with zero
|
|
384
|
+
- **Prop with unresolvable type** (generics, intersection, callback) → `exclude_prop` with reason `"complex type — not representable in CDF"`.
|
|
385
|
+
- **Component with zero classified props after exclusions** → still emit `classify_component`. The DB entry will have an empty `$properties` object.
|
|
386
386
|
- **tokenReference present but not in sidecar** → `cdf_type: "token"`, omit `token_kind`, add `description` warning.
|
|
387
387
|
- **Slot not in DB** → skipped with a warning; does not abort the run.
|
|
388
388
|
- **Prop not in DB** → skipped with a warning; does not abort the run.
|
|
@@ -391,14 +391,14 @@ href is a URL string — cdf_type string (not link), category content
|
|
|
391
391
|
|
|
392
392
|
Before emitting any tool calls, verify:
|
|
393
393
|
|
|
394
|
-
1. Every prop in the input has exactly one `classify_prop` call
|
|
394
|
+
1. Every prop in the input has exactly one `classify_prop` or `exclude_prop` call
|
|
395
395
|
2. Every slot has exactly one `classify_slot` call
|
|
396
396
|
3. `classify_component` is emitted exactly once
|
|
397
397
|
4. Every `cdf_type: "enum"` has a non-empty `values` array
|
|
398
398
|
5. Every `cdf_type: "token"` has `token_kind` (or a warning in `description` if lookup failed)
|
|
399
399
|
6. No `cdf_type: "link"` — all href/url props use `string`
|
|
400
400
|
7. `required` values are JSON booleans, not strings
|
|
401
|
-
8. Framework, DOM, accessibility, and data-* pass-through props are
|
|
401
|
+
8. Framework, DOM, accessibility, and data-* pass-through props are excluded — `className`/`classes`/`classNames`/`rootClassName`/`prefixCls`, `style`, `id`, `role`, `tabIndex`, `aria-*` (and bare `aria`), `data-*`, polymorphic `as`/`element`/`component`, framework theming `dt`/`pt`/`ptOptions`/`unstyled`/`sx`. Discrete positional/geometric props (`top`, `bottom`, `left`, `right`, `rotation`, etc.) ARE classified as `string` design props. Common semantic props (`icon`, `items`, `actions`, `options`, `value`, `name`, `form`, `inputId`, `componentId`) are NOT excluded — classify them per their content/design/state nature.
|
|
402
402
|
9. No `cdf_type: "link"` used — `link` is reserved and rejected by the CLI parser
|
|
403
403
|
10. No `cdf_type: "number"` used — this is not a supported type; use `"string"` with numeric defaults. `cdf_type: "boolean"` IS valid — use it for boolean toggle props.
|
|
404
404
|
11. `classify_component` includes a `rationale` object with all three sub-fields (`rationale.description`, `rationale.props`, `rationale.slots`) populated as non-empty strings.
|
|
@@ -417,9 +417,9 @@ Re-run or re-iterate on any components flagged by warnings until the output pass
|
|
|
417
417
|
|
|
418
418
|
## CRITICAL: Zero-output is a failure
|
|
419
419
|
|
|
420
|
-
You MUST produce
|
|
421
|
-
|
|
422
|
-
this is never acceptable.
|
|
420
|
+
You MUST produce at least one classify_prop call for this component. A response with zero
|
|
421
|
+
classify_prop/exclude_prop calls means the component will be pushed with no configurable
|
|
422
|
+
properties — this is never acceptable.
|
|
423
423
|
|
|
424
424
|
If you are genuinely uncertain about every prop, classify each as:
|
|
425
425
|
{"tool":"classify_prop","prop":"<name>","cdf_type":"string","cdf_category":"content","required":false,"description":"Uncertain classification — review recommended"}
|