@contentful/experience-design-system-cli 2.28.1-dev-build-33dbed8.0 → 2.28.1-dev-build-a917752.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
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -46,6 +46,7 @@ function findJsonObjectEnd(line) {
|
|
|
46
46
|
function readToolCallObjects(stdout) {
|
|
47
47
|
const objects = [];
|
|
48
48
|
const warnings = [];
|
|
49
|
+
let hadDroppedLine = false;
|
|
49
50
|
for (const raw of stdout.split("\n")) {
|
|
50
51
|
const line = raw.trim();
|
|
51
52
|
if (!line.startsWith("{"))
|
|
@@ -53,6 +54,7 @@ function readToolCallObjects(stdout) {
|
|
|
53
54
|
const end = findJsonObjectEnd(line);
|
|
54
55
|
if (end === -1) {
|
|
55
56
|
warnings.push(`unparseable line: ${line.slice(0, 120)}`);
|
|
57
|
+
hadDroppedLine = true;
|
|
56
58
|
continue;
|
|
57
59
|
}
|
|
58
60
|
let parsed;
|
|
@@ -60,6 +62,7 @@ function readToolCallObjects(stdout) {
|
|
|
60
62
|
parsed = JSON.parse(line.slice(0, end + 1));
|
|
61
63
|
} catch {
|
|
62
64
|
warnings.push(`unparseable line: ${line.slice(0, 120)}`);
|
|
65
|
+
hadDroppedLine = true;
|
|
63
66
|
continue;
|
|
64
67
|
}
|
|
65
68
|
const trailing = line.slice(end + 1);
|
|
@@ -68,9 +71,11 @@ function readToolCallObjects(stdout) {
|
|
|
68
71
|
}
|
|
69
72
|
if (typeof parsed === "object" && parsed !== null && "tool" in parsed) {
|
|
70
73
|
objects.push(parsed);
|
|
74
|
+
} else {
|
|
75
|
+
hadDroppedLine = true;
|
|
71
76
|
}
|
|
72
77
|
}
|
|
73
|
-
return { objects, warnings };
|
|
78
|
+
return { objects, warnings, hadDroppedLine };
|
|
74
79
|
}
|
|
75
80
|
function parseSelectToolCallLines(stdout) {
|
|
76
81
|
const calls = [];
|
|
@@ -101,7 +106,7 @@ function parseSelectToolCallLines(stdout) {
|
|
|
101
106
|
}
|
|
102
107
|
function parseToolCallLines(stdout) {
|
|
103
108
|
const calls = [];
|
|
104
|
-
const { objects, warnings } = readToolCallObjects(stdout);
|
|
109
|
+
const { objects, warnings, hadDroppedLine } = readToolCallObjects(stdout);
|
|
105
110
|
for (const rec of objects) {
|
|
106
111
|
if (!VALID_TOOL_NAMES.has(rec.tool)) {
|
|
107
112
|
warnings.push(`unknown tool: ${String(rec.tool)}`);
|
|
@@ -186,7 +191,7 @@ function parseToolCallLines(stdout) {
|
|
|
186
191
|
calls.push(call);
|
|
187
192
|
}
|
|
188
193
|
}
|
|
189
|
-
return { calls, warnings };
|
|
194
|
+
return { calls, warnings, hadDroppedLine };
|
|
190
195
|
}
|
|
191
196
|
function parseTokenToolCallLines(stdout) {
|
|
192
197
|
const calls = [];
|
|
@@ -9574,6 +9579,26 @@ function loadRawComponents(db, sessionId2, allowedNames) {
|
|
|
9574
9579
|
})
|
|
9575
9580
|
);
|
|
9576
9581
|
}
|
|
9582
|
+
function rewrittenSourcePaths(basePath) {
|
|
9583
|
+
for (const [emitted, sources] of TS_ESM_EXTENSION_REWRITES) {
|
|
9584
|
+
if (!basePath.endsWith(emitted)) continue;
|
|
9585
|
+
const stem = basePath.slice(0, -emitted.length);
|
|
9586
|
+
return sources.map((ext) => `${stem}${ext}`);
|
|
9587
|
+
}
|
|
9588
|
+
return [];
|
|
9589
|
+
}
|
|
9590
|
+
function identifiersIn(propTypes) {
|
|
9591
|
+
const names = /* @__PURE__ */ new Set();
|
|
9592
|
+
for (const propType of propTypes) {
|
|
9593
|
+
for (const match2 of propType.matchAll(IDENTIFIER_PATTERN)) names.add(match2[0]);
|
|
9594
|
+
}
|
|
9595
|
+
return [...names];
|
|
9596
|
+
}
|
|
9597
|
+
function declaresAnyType(text, typeNames) {
|
|
9598
|
+
return typeNames.some(
|
|
9599
|
+
(name) => new RegExp(`\\b(?:type|interface|enum|const|class)\\s+${escapeForRegExp(name)}\\b`).test(text)
|
|
9600
|
+
);
|
|
9601
|
+
}
|
|
9577
9602
|
function extractRelativeImportPaths(sourceText) {
|
|
9578
9603
|
const specifiers = /* @__PURE__ */ new Set();
|
|
9579
9604
|
for (const match2 of sourceText.matchAll(RELATIVE_IMPORT_PATTERN)) {
|
|
@@ -9585,6 +9610,7 @@ async function resolveRelativeImport(specifier, fromDir) {
|
|
|
9585
9610
|
const basePath = resolve7(fromDir, specifier);
|
|
9586
9611
|
const candidates = [
|
|
9587
9612
|
basePath,
|
|
9613
|
+
...rewrittenSourcePaths(basePath),
|
|
9588
9614
|
...SIBLING_FILE_EXTENSIONS.map((ext) => `${basePath}${ext}`),
|
|
9589
9615
|
...SIBLING_FILE_EXTENSIONS.map((ext) => resolve7(basePath, `index${ext}`))
|
|
9590
9616
|
];
|
|
@@ -9605,7 +9631,7 @@ async function resolveImportSpecifiers(sourceText, fromDir) {
|
|
|
9605
9631
|
}
|
|
9606
9632
|
return resolvedPaths;
|
|
9607
9633
|
}
|
|
9608
|
-
async function loadSiblingFiles(sourceText, sourcePath, propNames) {
|
|
9634
|
+
async function loadSiblingFiles(sourceText, sourcePath, propNames, candidateTypeNames = []) {
|
|
9609
9635
|
const visited = /* @__PURE__ */ new Set([resolve7(sourcePath)]);
|
|
9610
9636
|
const discovered = [];
|
|
9611
9637
|
let frontier = [{ text: sourceText, dir: dirname7(sourcePath) }];
|
|
@@ -9630,9 +9656,16 @@ async function loadSiblingFiles(sourceText, sourcePath, propNames) {
|
|
|
9630
9656
|
}
|
|
9631
9657
|
frontier = nextFrontier;
|
|
9632
9658
|
}
|
|
9659
|
+
const inHand = [sourceText, ...discovered.map((d) => d.content)];
|
|
9660
|
+
const declaredTypeNames = candidateTypeNames.filter((name) => inHand.some((text) => declaresAnyType(text, [name])));
|
|
9633
9661
|
const usesNotShown = /* @__PURE__ */ new Set();
|
|
9662
|
+
const searchNames = [.../* @__PURE__ */ new Set([...propNames, ...declaredTypeNames])];
|
|
9663
|
+
let enlarged = 0;
|
|
9634
9664
|
const siblings = discovered.slice(0, MAX_SIBLING_FILES).map((d) => {
|
|
9635
|
-
const
|
|
9665
|
+
const enlarge = enlarged < MAX_ENLARGED_SIBLINGS && declaresAnyType(d.content, declaredTypeNames);
|
|
9666
|
+
if (enlarge) enlarged++;
|
|
9667
|
+
const budget = enlarge ? MAX_TYPE_DECLARING_SIBLING_CHARS : MAX_SIBLING_SNIPPET_CHARS;
|
|
9668
|
+
const excerpt = excerptAroundNames(d.content, searchNames, budget);
|
|
9636
9669
|
for (const name of excerpt.usesNotShown) usesNotShown.add(name);
|
|
9637
9670
|
return { path: d.path, content: excerpt.content };
|
|
9638
9671
|
});
|
|
@@ -9640,25 +9673,32 @@ async function loadSiblingFiles(sourceText, sourcePath, propNames) {
|
|
|
9640
9673
|
return {
|
|
9641
9674
|
siblings,
|
|
9642
9675
|
truncatedCount,
|
|
9643
|
-
usesNotShown: propNames.filter((name) => usesNotShown.has(name))
|
|
9676
|
+
usesNotShown: propNames.filter((name) => usesNotShown.has(name)),
|
|
9677
|
+
declaredTypeNames
|
|
9644
9678
|
};
|
|
9645
9679
|
}
|
|
9646
|
-
async function loadComponentSourceRef(name, sourcePath, propNames = []) {
|
|
9680
|
+
async function loadComponentSourceRef(name, sourcePath, propNames = [], propTypes = []) {
|
|
9647
9681
|
let content = null;
|
|
9648
9682
|
let siblingFiles = [];
|
|
9649
9683
|
let truncatedSiblingCount = 0;
|
|
9650
9684
|
const usesNotShown = /* @__PURE__ */ new Set();
|
|
9651
9685
|
try {
|
|
9652
9686
|
const rawText = await readFile7(sourcePath, "utf8");
|
|
9653
|
-
const mainExcerpt = excerptAroundNames(rawText, propNames, MAX_COMPONENT_SOURCE_CHARS);
|
|
9654
|
-
content = mainExcerpt.content;
|
|
9655
|
-
for (const name2 of mainExcerpt.usesNotShown) usesNotShown.add(name2);
|
|
9656
9687
|
let siblingUsesNotShown;
|
|
9688
|
+
let declaredTypeNames;
|
|
9657
9689
|
({
|
|
9658
9690
|
siblings: siblingFiles,
|
|
9659
9691
|
truncatedCount: truncatedSiblingCount,
|
|
9660
|
-
usesNotShown: siblingUsesNotShown
|
|
9661
|
-
|
|
9692
|
+
usesNotShown: siblingUsesNotShown,
|
|
9693
|
+
declaredTypeNames
|
|
9694
|
+
} = await loadSiblingFiles(rawText, sourcePath, propNames, identifiersIn(propTypes)));
|
|
9695
|
+
const mainExcerpt = excerptAroundNames(
|
|
9696
|
+
rawText,
|
|
9697
|
+
[.../* @__PURE__ */ new Set([...propNames, ...declaredTypeNames])],
|
|
9698
|
+
MAX_COMPONENT_SOURCE_CHARS
|
|
9699
|
+
);
|
|
9700
|
+
content = mainExcerpt.content;
|
|
9701
|
+
for (const name2 of mainExcerpt.usesNotShown) usesNotShown.add(name2);
|
|
9662
9702
|
for (const name2 of siblingUsesNotShown) usesNotShown.add(name2);
|
|
9663
9703
|
} catch {
|
|
9664
9704
|
}
|
|
@@ -9673,13 +9713,18 @@ async function loadComponentSourceRefs(db, sessionId2) {
|
|
|
9673
9713
|
const rows = db.prepare(
|
|
9674
9714
|
`SELECT component_id, name, source, source_path FROM raw_components WHERE session_id = ? AND status = 'generated' ORDER BY rowid`
|
|
9675
9715
|
).all(sessionId2);
|
|
9676
|
-
const
|
|
9677
|
-
`SELECT name FROM raw_props WHERE session_id = ? AND component_id = ? ORDER BY position`
|
|
9716
|
+
const propsFor = db.prepare(
|
|
9717
|
+
`SELECT name, type FROM raw_props WHERE session_id = ? AND component_id = ? ORDER BY position`
|
|
9678
9718
|
);
|
|
9679
9719
|
return Promise.all(
|
|
9680
9720
|
rows.map((r) => {
|
|
9681
|
-
const
|
|
9682
|
-
return loadComponentSourceRef(
|
|
9721
|
+
const props = propsFor.all(sessionId2, r.component_id);
|
|
9722
|
+
return loadComponentSourceRef(
|
|
9723
|
+
r.name,
|
|
9724
|
+
r.source_path ?? r.source,
|
|
9725
|
+
props.map((p) => p.name),
|
|
9726
|
+
props.map((p) => p.type)
|
|
9727
|
+
);
|
|
9683
9728
|
})
|
|
9684
9729
|
);
|
|
9685
9730
|
}
|
|
@@ -10562,7 +10607,7 @@ function lookupSelectCache(db, componentHash, promptHash, cliVersion2) {
|
|
|
10562
10607
|
updatedAt: row.updated_at
|
|
10563
10608
|
};
|
|
10564
10609
|
}
|
|
10565
|
-
var SCHEMA, MAX_COMPONENT_SOURCE_CHARS, MAX_SIBLING_FILES, MAX_SIBLING_SNIPPET_CHARS, MAX_SIBLING_DEPTH, MAX_SIBLING_CANDIDATES_EXPLORED, RELATIVE_IMPORT_PATTERN, SIBLING_FILE_EXTENSIONS, _cliCacheVersionCache;
|
|
10610
|
+
var SCHEMA, MAX_COMPONENT_SOURCE_CHARS, MAX_SIBLING_FILES, MAX_SIBLING_SNIPPET_CHARS, MAX_TYPE_DECLARING_SIBLING_CHARS, MAX_ENLARGED_SIBLINGS, MAX_SIBLING_DEPTH, MAX_SIBLING_CANDIDATES_EXPLORED, RELATIVE_IMPORT_PATTERN, SIBLING_FILE_EXTENSIONS, TS_ESM_EXTENSION_REWRITES, IDENTIFIER_PATTERN, _cliCacheVersionCache;
|
|
10566
10611
|
var init_db = __esm({
|
|
10567
10612
|
"packages/experience-design-system-cli/src/session/db.ts"() {
|
|
10568
10613
|
"use strict";
|
|
@@ -10748,10 +10793,19 @@ CREATE INDEX IF NOT EXISTS idx_scanned_files_session ON scanned_files(session
|
|
|
10748
10793
|
MAX_COMPONENT_SOURCE_CHARS = 8e3;
|
|
10749
10794
|
MAX_SIBLING_FILES = 5;
|
|
10750
10795
|
MAX_SIBLING_SNIPPET_CHARS = 1200;
|
|
10796
|
+
MAX_TYPE_DECLARING_SIBLING_CHARS = 4e3;
|
|
10797
|
+
MAX_ENLARGED_SIBLINGS = 2;
|
|
10751
10798
|
MAX_SIBLING_DEPTH = 2;
|
|
10752
10799
|
MAX_SIBLING_CANDIDATES_EXPLORED = 25;
|
|
10753
10800
|
RELATIVE_IMPORT_PATTERN = /from\s+['"](\.[^'"]+)['"]/g;
|
|
10754
10801
|
SIBLING_FILE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
10802
|
+
TS_ESM_EXTENSION_REWRITES = [
|
|
10803
|
+
[".js", [".ts", ".tsx", ".js", ".jsx"]],
|
|
10804
|
+
[".jsx", [".tsx", ".jsx"]],
|
|
10805
|
+
[".mjs", [".mts", ".mjs"]],
|
|
10806
|
+
[".cjs", [".cts", ".cjs"]]
|
|
10807
|
+
];
|
|
10808
|
+
IDENTIFIER_PATTERN = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
10755
10809
|
_cliCacheVersionCache = null;
|
|
10756
10810
|
}
|
|
10757
10811
|
});
|
|
@@ -32468,7 +32522,8 @@ async function runOneComponent(agent, model, db, sessionId2, component, tokensIn
|
|
|
32468
32522
|
const sourceRef = await loadComponentSourceRef(
|
|
32469
32523
|
component.name,
|
|
32470
32524
|
component.sourcePath ?? component.source,
|
|
32471
|
-
component.props.map((p) => p.name)
|
|
32525
|
+
component.props.map((p) => p.name),
|
|
32526
|
+
component.props.map((p) => p.type)
|
|
32472
32527
|
);
|
|
32473
32528
|
const prompt2 = await buildPrompt({
|
|
32474
32529
|
skill: "components",
|
|
@@ -32516,11 +32571,16 @@ ${outputBuf}`);
|
|
|
32516
32571
|
lastError = describeAgentFailure(result);
|
|
32517
32572
|
continue;
|
|
32518
32573
|
}
|
|
32519
|
-
const { calls, warnings } = parseToolCallLines(result.stdout);
|
|
32574
|
+
const { calls, warnings, hadDroppedLine } = parseToolCallLines(result.stdout);
|
|
32520
32575
|
if (calls.length === 0) {
|
|
32521
32576
|
lastError = describeAgentFailure(result);
|
|
32522
32577
|
continue;
|
|
32523
32578
|
}
|
|
32579
|
+
if (hadDroppedLine) {
|
|
32580
|
+
lastError = `agent output dropped at least one tool-call line: ${warnings.join("; ").slice(0, 200)}`;
|
|
32581
|
+
if (attempt < maxAttempts) continue;
|
|
32582
|
+
break;
|
|
32583
|
+
}
|
|
32524
32584
|
const applied = applyToolCalls(db, sessionId2, component.component_id, component.name, calls, warnings);
|
|
32525
32585
|
if (!noCache) {
|
|
32526
32586
|
const inputHash = computeComponentInputHash(component);
|
|
@@ -32709,7 +32769,8 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
32709
32769
|
const sampleSourceRef = sampleComponent ? await loadComponentSourceRef(
|
|
32710
32770
|
sampleComponent.name,
|
|
32711
32771
|
sampleComponent.sourcePath ?? sampleComponent.source,
|
|
32712
|
-
sampleComponent.props.map((p) => p.name)
|
|
32772
|
+
sampleComponent.props.map((p) => p.name),
|
|
32773
|
+
sampleComponent.props.map((p) => p.type)
|
|
32713
32774
|
) : void 0;
|
|
32714
32775
|
const prompt2 = await buildPrompt({
|
|
32715
32776
|
skill,
|
|
@@ -32785,6 +32846,11 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
32785
32846
|
`Error: all ${componentResults.length} component(s) failed to generate \u2014 see the per-component errors above.`
|
|
32786
32847
|
);
|
|
32787
32848
|
}
|
|
32849
|
+
if (failed.length > 0) {
|
|
32850
|
+
die2(
|
|
32851
|
+
`Error: ${failed.length}/${componentResults.length} component(s) failed to generate \u2014 see the per-component errors above.`
|
|
32852
|
+
);
|
|
32853
|
+
}
|
|
32788
32854
|
} else if (skill === "tokens") {
|
|
32789
32855
|
const noCache = opts.cache === false || process.env.EDS_NO_CACHE === "1";
|
|
32790
32856
|
const tokenInputContent = rawTokensInline ?? "";
|
|
@@ -33412,7 +33478,7 @@ import { basename as basename5, resolve as resolve19 } from "node:path";
|
|
|
33412
33478
|
|
|
33413
33479
|
// packages/experience-design-system-cli/src/print/validate/validators/cdf-validator.ts
|
|
33414
33480
|
import { readFile as readFile19 } from "node:fs/promises";
|
|
33415
|
-
import { validateCDF as validateCDF2 } from "@contentful/experience-design-system-types";
|
|
33481
|
+
import { validateCDF as validateCDF2, checkCDFComponentInvariants } from "@contentful/experience-design-system-types";
|
|
33416
33482
|
function extractValue(input, path) {
|
|
33417
33483
|
if (path === "/") return void 0;
|
|
33418
33484
|
const parts = path.split("/").filter(Boolean);
|
|
@@ -33496,6 +33562,14 @@ async function validateCDFFile(filePath) {
|
|
|
33496
33562
|
}).map((e) => rewriteDiagnostic(e, parsed));
|
|
33497
33563
|
return { valid: false, summary: "", diagnostics };
|
|
33498
33564
|
}
|
|
33565
|
+
const invariantErrors = checkCDFComponentInvariants(cdfResult.components);
|
|
33566
|
+
if (invariantErrors.length > 0) {
|
|
33567
|
+
return {
|
|
33568
|
+
valid: false,
|
|
33569
|
+
summary: "",
|
|
33570
|
+
diagnostics: invariantErrors.map((e) => rewriteDiagnostic(e, parsed))
|
|
33571
|
+
};
|
|
33572
|
+
}
|
|
33499
33573
|
const count = cdfResult.components.length;
|
|
33500
33574
|
return {
|
|
33501
33575
|
valid: true,
|
package/dist/src/session/db.d.ts
CHANGED
|
@@ -107,7 +107,7 @@ export type RawComponentWithId = RawComponentDefinition & {
|
|
|
107
107
|
component_id: string;
|
|
108
108
|
};
|
|
109
109
|
export declare function loadRawComponents(db: DatabaseSync, sessionId: string, allowedNames?: Set<string>): RawComponentWithId[];
|
|
110
|
-
export declare function loadComponentSourceRef(name: string, sourcePath: string, propNames?: string[]): Promise<ComponentSourceRef>;
|
|
110
|
+
export declare function loadComponentSourceRef(name: string, sourcePath: string, propNames?: string[], propTypes?: string[]): Promise<ComponentSourceRef>;
|
|
111
111
|
export declare function loadComponentSourceRefs(db: DatabaseSync, sessionId: string): Promise<ComponentSourceRef[]>;
|
|
112
112
|
export declare function renameEmptySlots(db: DatabaseSync, sessionId: string, componentId: string, componentName: string, slotCount: number): {
|
|
113
113
|
renames: Array<{
|
|
@@ -16,6 +16,7 @@ export interface SourceExcerpt {
|
|
|
16
16
|
*/
|
|
17
17
|
usesNotShown: string[];
|
|
18
18
|
}
|
|
19
|
+
export declare function escapeForRegExp(value: string): string;
|
|
19
20
|
/**
|
|
20
21
|
* Returns `text` whole when it fits `maxChars`. Otherwise returns windows of
|
|
21
22
|
* `contextLines` lines around every line that mentions one of `names`, merged
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.28.1-dev-build-
|
|
3
|
+
"version": "2.28.1-dev-build-a917752.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,7 +39,7 @@
|
|
|
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.28.1-dev-build-
|
|
42
|
+
"@contentful/experience-design-system-types": "2.28.1-dev-build-a917752.0"
|
|
43
43
|
},
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@tsconfig/node24": "^24.0.4",
|
|
@@ -51,9 +51,9 @@
|
|
|
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-extraction": "2.28.1-dev-build-a917752.0",
|
|
55
|
+
"@contentful/experience-design-system-client": "2.28.1-dev-build-a917752.0",
|
|
56
|
+
"@contentful/experience-design-system-generation": "2.28.1-dev-build-a917752.0"
|
|
57
57
|
},
|
|
58
58
|
"repository": {
|
|
59
59
|
"type": "git",
|
|
@@ -91,7 +91,7 @@ Emit one JSON object per line. The CLI parses lines starting with `{`. Lines not
|
|
|
91
91
|
- Every prop in the input must produce exactly one call: `classify_prop` OR `exclude_prop`.
|
|
92
92
|
- Every slot must produce exactly one `classify_slot` call.
|
|
93
93
|
- Emit `classify_component` once at the start (required). The `description` field is **required** — always provide a brief description of the component's purpose.
|
|
94
|
-
- `values` is required for `cdf_type: "enum"` — must be a non-empty string array of the variant names the prop accepts.
|
|
94
|
+
- `values` is required for `cdf_type: "enum"` — must be a non-empty string array of the variant names the prop accepts. **There is no legal `enum` with a guessed list.** If the value set is shown to you anywhere in the material below — the type's own declaration, an `allowedValues` list on the raw prop, a variant/lookup map, a default, a sibling excerpt — copy it and emit `enum`. If it is shown nowhere, emit `cdf_type: "string"` and leave `values` off. `string` is an unrestricted enum: the author can still pass the real value. A guessed list silently rejects it.
|
|
95
95
|
- **Do NOT include `values` for `cdf_type: "token"`.** The two property types carry different kinds of list. An `enum` prop's list holds *variant names* the component accepts (`"primary"`, `"secondary"`). A `token` prop's list holds *design token paths* (`"color.brand.primary"`), produced by the separate token-mapping step (`$token.allowed`) — never by you. Emitting `values` on a token prop makes the definition invalid.
|
|
96
96
|
- `token_kind` is required for `cdf_type: "token"` — must be a DTCG `$type` string, e.g. `"color"`.
|
|
97
97
|
- `required` must be a JSON boolean (`true`/`false`), not a string.
|
|
@@ -364,7 +364,7 @@ as enum so the author picks "primary" and the component receives "primary".
|
|
|
364
364
|
|
|
365
365
|
---
|
|
366
366
|
|
|
367
|
-
## Category
|
|
367
|
+
## Category and required corrections
|
|
368
368
|
|
|
369
369
|
The pre-classified `category` is wrong in predictable ways. Correct silently (document in `description`):
|
|
370
370
|
|
|
@@ -375,6 +375,11 @@ The pre-classified `category` is wrong in predictable ways. Correct silently (do
|
|
|
375
375
|
|
|
376
376
|
> **Key question for category**: "Who fills this in?" — A content editor fills in `content`. A designer configures `design`. Neither fills in `state` — it comes from routing, runtime behavior, or component infrastructure.
|
|
377
377
|
|
|
378
|
+
**`required` is also a hint, and it is also wrong.** Verify it against the declaration:
|
|
379
|
+
- A prop with a default value — a `$default`, a destructure default, or a `defaultVariants` entry — is **never** `required: true`. The two together are incoherent.
|
|
380
|
+
- A prop declared `foo?:` is `required: false`, whatever the hint says.
|
|
381
|
+
- Props reached through `VariantProps<typeof x>` or a Radix/`ComponentPropsWithoutRef` base are optional unless the component's own interface redeclares them as required.
|
|
382
|
+
|
|
378
383
|
---
|
|
379
384
|
|
|
380
385
|
## Slot classification
|
|
@@ -462,14 +467,30 @@ footer is supplementary and optional
|
|
|
462
467
|
{"tool":"classify_slot","slot":"footer","required":false,"description":"Optional footer area for actions or metadata","rationale":"Footer is a supplementary region typically used for actions or metadata. Optional because most cards do not need one and the card renders correctly without it."}
|
|
463
468
|
```
|
|
464
469
|
|
|
465
|
-
### Named type (HeadingSize, ButtonVariant, etc.)
|
|
470
|
+
### Named type (HeadingSize, ButtonVariant, etc.) — do NOT infer the values
|
|
471
|
+
|
|
472
|
+
When a prop has a named TypeScript type that is not inlined as a union literal,
|
|
473
|
+
the value set is whatever that type's declaration says. A type *name* is not
|
|
474
|
+
evidence of its members: `HeadingSize` could be `h1`–`h6`, `sm|md|lg`, or
|
|
475
|
+
`s|m|l|xl`. Guessing picks one design system's vocabulary and is wrong more
|
|
476
|
+
often than it is right, and a wrong name matches no style rule — the author's
|
|
477
|
+
setting silently does nothing.
|
|
478
|
+
|
|
479
|
+
So: look for the declaration in the source shown. If it is there, copy it. If it
|
|
480
|
+
is not, **emit `string`** — `string` is an unrestricted enum, so the author stays
|
|
481
|
+
unrestricted but correct.
|
|
466
482
|
|
|
467
|
-
|
|
483
|
+
Members visible in the source shown — copy them, do not paraphrase:
|
|
484
|
+
```
|
|
485
|
+
titleSize has type HeadingSize; HeadingSize is declared in the excerpt as 'sm' | 'md' | 'lg'
|
|
486
|
+
{"tool":"classify_prop","prop":"titleSize","cdf_type":"enum","cdf_category":"design","required":false,"values":["sm","md","lg"],"description":"Heading size","reason":"Copied from the HeadingSize declaration shown in the excerpt."}
|
|
487
|
+
```
|
|
468
488
|
|
|
489
|
+
Members NOT visible in the source shown — degrade to `string`:
|
|
469
490
|
```
|
|
470
|
-
titleSize has type HeadingSize
|
|
471
|
-
|
|
472
|
-
{"tool":"classify_prop","prop":"titleSize","cdf_type":"
|
|
491
|
+
titleSize has type HeadingSize; HeadingSize is not declared anywhere in the source shown
|
|
492
|
+
no evidence for its members, so no values list — string, not a guess
|
|
493
|
+
{"tool":"classify_prop","prop":"titleSize","cdf_type":"string","cdf_category":"design","required":false,"description":"Heading size","reason":"HeadingSize is not declared in the source shown, so its members cannot be cited. Emitted as string rather than inventing a value list."}
|
|
473
494
|
```
|
|
474
495
|
|
|
475
496
|
### href prop
|
|
@@ -495,7 +516,7 @@ Before emitting any tool calls, verify:
|
|
|
495
516
|
1. Every prop in the input has exactly one `classify_prop` or `exclude_prop` call
|
|
496
517
|
2. Every slot has exactly one `classify_slot` call
|
|
497
518
|
3. `classify_component` is emitted exactly once
|
|
498
|
-
4. Every `cdf_type: "enum"` has a non-empty `values` array
|
|
519
|
+
4. Every `cdf_type: "enum"` has a non-empty `values` array taken from the material shown. A prop whose value set was shown nowhere is `cdf_type: "string"` with no `values` — never an `enum` with a plausible-looking list, and never an `exclude_prop`.
|
|
499
520
|
5. Every `cdf_type: "token"` has `token_kind` (or a warning in `description` if lookup failed) and does **not** have `values` set
|
|
500
521
|
6. No `cdf_type: "link"` — all href/url props use `string`
|
|
501
522
|
7. `required` values are JSON booleans, not strings
|
|
@@ -507,7 +528,8 @@ Before emitting any tool calls, verify:
|
|
|
507
528
|
13. `rationale.description` follows the same "Description content rules" as `description` — no internal initiative names (`INTEG-*`, `EDSI`, `DSI`, `M1`, `M2`, wave/phase references, etc.).
|
|
508
529
|
14. Every `cdf_type: "token"` prop cites a line that interpolates its value into a style **and** a token reference at that use (Q2 yes, Q3 yes), and carries no `values`. If you cannot cite both, it is `enum` or `string`; if you can, it is `token` — its type annotation is not a reason to emit `string`.
|
|
509
530
|
15. Every prop whose values are friendly names is `enum` with a non-empty `values` array, even when each name resolves to a design token internally; every prop for which Q1–Q3 could not be answered was emitted as `enum` (or `string`) with the ambiguity stated in `reason`, and no answerable prop was called ambiguous.
|
|
510
|
-
16. Every `enum`'s `values` array is copied from evidence in the
|
|
531
|
+
16. Every `enum`'s `values` array is copied from evidence in the material shown — a type declaration, an `allowedValues` list on the raw prop, a variant or lookup map, a default, in the component's own file or in any sibling excerpt — and none of it was invented. Where the value set appears nowhere, the prop is `cdf_type: "string"`, with the reason recorded in `reason`. Do not drop a prop merely because you could not enumerate its values.
|
|
532
|
+
17. No prop has `required: true` alongside a default value (`$default`, a destructure default, or a `defaultVariants` entry) — the two are incoherent. A prop declared `foo?:`, or reached through `VariantProps<typeof x>` or a Radix/`ComponentPropsWithoutRef` base without its own required redeclaration, is `required: false`.
|
|
511
533
|
|
|
512
534
|
After the run completes, the developer can validate the pipeline output with:
|
|
513
535
|
|
|
@@ -528,4 +550,6 @@ properties — this is never acceptable.
|
|
|
528
550
|
If you are genuinely uncertain about every prop, classify each as:
|
|
529
551
|
{"tool":"classify_prop","prop":"<name>","cdf_type":"string","cdf_category":"content","required":false,"description":"Uncertain classification — review recommended"}
|
|
530
552
|
|
|
531
|
-
An imperfect classification is infinitely better than no classification.
|
|
553
|
+
An imperfect classification is infinitely better than no classification. That is
|
|
554
|
+
about **classifying a prop rather than dropping it** — it is not a licence to
|
|
555
|
+
invent a `values` list. The uncertain answer is `cdf_type: "string"`, every time.
|