@contentful/experience-design-system-cli 2.30.1-dev-build-87c8cf1.0 → 2.30.1-dev-build-6f93a80.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/README.md +5 -4
- package/dist/package.json +1 -1
- package/dist/src/index.js +30 -72
- package/dist/src/session/db.d.ts +1 -1
- package/dist/src/session/source-excerpt.d.ts +0 -1
- package/package.json +6 -6
- package/skills/generate-components.md +16 -36
package/README.md
CHANGED
|
@@ -63,7 +63,7 @@ When more than one source speaks to the same relationship, the higher-precedence
|
|
|
63
63
|
|
|
64
64
|
- `--composition-agent-mode <parser|edges>` — `parser` (default; the sandboxed-parser design above) or `edges` (agent lists relationships directly; less robust).
|
|
65
65
|
- `--composition-refresh` — ignore the cache and re-resolve from scratch, forcing the agent to run.
|
|
66
|
-
- `--agent <name>` — which coding agent authors the parser (`claude`, `codex`, `opencode`, `cursor`).
|
|
66
|
+
- `--agent <name>` — which coding agent authors the parser (`claude`, `codex`, `opencode`, `cursor`, `copilot`).
|
|
67
67
|
- `--prompt composition=<file-or-text>` — override the composition stage's prompt.
|
|
68
68
|
|
|
69
69
|
Because the agent path spawns a coding agent, it adds latency and cost and is best-effort (parser quality can vary run to run). For reproducible results, prefer `--composition-map`.
|
|
@@ -86,6 +86,7 @@ If the resolved graph contains a circular slot dependency (A slots B, B slots A)
|
|
|
86
86
|
| **OpenAI Codex** (`codex`) | `npm install -g @openai/codex` | Set `OPENAI_API_KEY` |
|
|
87
87
|
| **OpenCode** (`opencode`) | `npm install -g opencode-ai` | Configure via `opencode auth` (supports multiple providers) |
|
|
88
88
|
| **Cursor** (`cursor`) | Install [Cursor](https://cursor.com) | Sign in to Cursor; exposes `cursor-agent` binary |
|
|
89
|
+
| **GitHub Copilot** (`copilot`) | `npm install -g @github/copilot` | Run `copilot` once to complete GitHub OAuth login. Free plan uses Auto model selection by default. Paid plans can pin a specific model via `EDS_AGENT_MODEL_COPILOT=<model-id>` (e.g. `claude-sonnet-4.6`) |
|
|
89
90
|
|
|
90
91
|
The CLI invokes the agent non-interactively in a subprocess. If the binary is not found in `$PATH`, the command exits 1 and prints manual fallback instructions.
|
|
91
92
|
|
|
@@ -291,7 +292,7 @@ experiences analyze extract --project <path> [--dir <src-dir>] [composition flag
|
|
|
291
292
|
| `--composition-agent` | — | Opt into agentic resolution when deterministic sources find no groups (implies `--composite`) |
|
|
292
293
|
| `--composition-refresh` | — | Bypass the composition cache and re-resolve from scratch, forcing the agent to run (implies `--composite`) |
|
|
293
294
|
| `--composition-agent-mode <mode>` | `parser` | `parser` (agent writes a sandboxed parser — deterministic) or `edges` (agent lists edges directly) |
|
|
294
|
-
| `--agent <name>` | saved by setup | Coding agent for composition resolution: `claude`, `codex`, `opencode`, `cursor` |
|
|
295
|
+
| `--agent <name>` | saved by setup | Coding agent for composition resolution: `claude`, `codex`, `opencode`, `cursor`, `copilot` |
|
|
295
296
|
| `--prompt <stage=value>` | — | Override a stage prompt (repeatable); value is a file path or literal text, e.g. `--prompt composition=./p.md` |
|
|
296
297
|
|
|
297
298
|
Scans `.tsx`, `.ts`, `.jsx`, `.js`, `.vue`, and `.astro` files. Ignores `node_modules`, `dist`, `build`, `.next`, `.nuxt`, `coverage`, `storybook-static`, `out`, `demo(s)`, and `example(s)` directories. Also ignores `*.stories.*`, `*.story.*`, `*.spec.*`, and `*.test.*` files.
|
|
@@ -339,7 +340,7 @@ experiences analyze select-agent [--agent <name>] [--session <id>]
|
|
|
339
340
|
|
|
340
341
|
| Option | Default | Description |
|
|
341
342
|
|---|---|---|
|
|
342
|
-
| `--agent <name>` | saved by `experiences setup` | Agent: `claude`, `codex`, `opencode`, or `
|
|
343
|
+
| `--agent <name>` | saved by `experiences setup` | Agent: `claude`, `codex`, `opencode`, `cursor`, or `copilot` |
|
|
343
344
|
| `--session <id>` | most recent completed `analyze extract` | Session ID from `analyze extract` |
|
|
344
345
|
| `--project-root <path>` | `cwd` | Project root for resolving component source files |
|
|
345
346
|
| `--model <name>` | agent default | Model to use |
|
|
@@ -368,7 +369,7 @@ experiences generate components [--agent <name>] [--session <id>]
|
|
|
368
369
|
|
|
369
370
|
| Option | Default | Description |
|
|
370
371
|
|---|---|---|
|
|
371
|
-
| `--agent <name>` | saved by setup | Agent: `claude`, `codex`, `opencode`, or `
|
|
372
|
+
| `--agent <name>` | saved by setup | Agent: `claude`, `codex`, `opencode`, `cursor`, or `copilot` |
|
|
372
373
|
| `--session <id>` | most recent completed `analyze extract` | Session ID from `analyze extract` |
|
|
373
374
|
| `--tokens <path>` | — | Path to `tokens.json` for token-linked prop resolution |
|
|
374
375
|
| `--token-map <path>` | — | Path to `token-name-map.json` sidecar |
|
package/dist/package.json
CHANGED
package/dist/src/index.js
CHANGED
|
@@ -16,7 +16,7 @@ var AGENT_NAMES, DEFAULT_AGENT_NAME;
|
|
|
16
16
|
var init_agent_names = __esm({
|
|
17
17
|
"packages/experience-design-system-generation/dist/src/agent-names.js"() {
|
|
18
18
|
"use strict";
|
|
19
|
-
AGENT_NAMES = ["claude", "codex", "opencode", "cursor"];
|
|
19
|
+
AGENT_NAMES = ["claude", "codex", "opencode", "cursor", "copilot"];
|
|
20
20
|
DEFAULT_AGENT_NAME = "claude";
|
|
21
21
|
}
|
|
22
22
|
});
|
|
@@ -295,6 +295,11 @@ function buildArgs(agent, prompt2, model, promptViaStdin = false) {
|
|
|
295
295
|
return ["run", ...modelArg, ...promptArg];
|
|
296
296
|
case "cursor":
|
|
297
297
|
return ["--print", ...modelArg, ...promptArg];
|
|
298
|
+
case "copilot": {
|
|
299
|
+
const copilotModel = resolveAgentModel("copilot", model);
|
|
300
|
+
const copilotModelArg = copilotModel === "Auto" ? [] : ["--model", copilotModel];
|
|
301
|
+
return ["-p", ...promptArg, ...copilotModelArg, "--allow-all-tools"];
|
|
302
|
+
}
|
|
298
303
|
}
|
|
299
304
|
}
|
|
300
305
|
async function runAgent(options) {
|
|
@@ -445,15 +450,18 @@ var init_agent_runner = __esm({
|
|
|
445
450
|
claude: "claude",
|
|
446
451
|
codex: "codex",
|
|
447
452
|
opencode: "opencode",
|
|
448
|
-
cursor: "cursor-agent"
|
|
453
|
+
cursor: "cursor-agent",
|
|
454
|
+
copilot: "copilot"
|
|
449
455
|
};
|
|
450
456
|
DEFAULT_MODELS = {
|
|
451
457
|
claude: "haiku",
|
|
452
458
|
codex: "gpt-5.4-mini",
|
|
453
459
|
// requires OPENAI_API_KEY; ChatGPT account users must pass --model
|
|
454
460
|
opencode: "claude-haiku-4-5",
|
|
455
|
-
cursor: "gpt-mini"
|
|
461
|
+
cursor: "gpt-mini",
|
|
456
462
|
// cursor alias for gpt-5.4-mini-medium; haiku not in cursor's catalog
|
|
463
|
+
copilot: "Auto"
|
|
464
|
+
// the only model guaranteed on every Copilot plan (Free/Pro/Business/Enterprise); Pro+ users override via EDS_AGENT_MODEL_COPILOT
|
|
457
465
|
};
|
|
458
466
|
}
|
|
459
467
|
});
|
|
@@ -9574,26 +9582,6 @@ function loadRawComponents(db, sessionId2, allowedNames) {
|
|
|
9574
9582
|
})
|
|
9575
9583
|
);
|
|
9576
9584
|
}
|
|
9577
|
-
function rewrittenSourcePaths(basePath) {
|
|
9578
|
-
for (const [emitted, sources] of TS_ESM_EXTENSION_REWRITES) {
|
|
9579
|
-
if (!basePath.endsWith(emitted)) continue;
|
|
9580
|
-
const stem = basePath.slice(0, -emitted.length);
|
|
9581
|
-
return sources.map((ext) => `${stem}${ext}`);
|
|
9582
|
-
}
|
|
9583
|
-
return [];
|
|
9584
|
-
}
|
|
9585
|
-
function identifiersIn(propTypes) {
|
|
9586
|
-
const names = /* @__PURE__ */ new Set();
|
|
9587
|
-
for (const propType of propTypes) {
|
|
9588
|
-
for (const match2 of propType.matchAll(IDENTIFIER_PATTERN)) names.add(match2[0]);
|
|
9589
|
-
}
|
|
9590
|
-
return [...names];
|
|
9591
|
-
}
|
|
9592
|
-
function declaresAnyType(text, typeNames) {
|
|
9593
|
-
return typeNames.some(
|
|
9594
|
-
(name) => new RegExp(`\\b(?:type|interface|enum|const|class)\\s+${escapeForRegExp(name)}\\b`).test(text)
|
|
9595
|
-
);
|
|
9596
|
-
}
|
|
9597
9585
|
function extractRelativeImportPaths(sourceText) {
|
|
9598
9586
|
const specifiers = /* @__PURE__ */ new Set();
|
|
9599
9587
|
for (const match2 of sourceText.matchAll(RELATIVE_IMPORT_PATTERN)) {
|
|
@@ -9605,7 +9593,6 @@ async function resolveRelativeImport(specifier, fromDir) {
|
|
|
9605
9593
|
const basePath = resolve7(fromDir, specifier);
|
|
9606
9594
|
const candidates = [
|
|
9607
9595
|
basePath,
|
|
9608
|
-
...rewrittenSourcePaths(basePath),
|
|
9609
9596
|
...SIBLING_FILE_EXTENSIONS.map((ext) => `${basePath}${ext}`),
|
|
9610
9597
|
...SIBLING_FILE_EXTENSIONS.map((ext) => resolve7(basePath, `index${ext}`))
|
|
9611
9598
|
];
|
|
@@ -9626,7 +9613,7 @@ async function resolveImportSpecifiers(sourceText, fromDir) {
|
|
|
9626
9613
|
}
|
|
9627
9614
|
return resolvedPaths;
|
|
9628
9615
|
}
|
|
9629
|
-
async function loadSiblingFiles(sourceText, sourcePath, propNames
|
|
9616
|
+
async function loadSiblingFiles(sourceText, sourcePath, propNames) {
|
|
9630
9617
|
const visited = /* @__PURE__ */ new Set([resolve7(sourcePath)]);
|
|
9631
9618
|
const discovered = [];
|
|
9632
9619
|
let frontier = [{ text: sourceText, dir: dirname7(sourcePath) }];
|
|
@@ -9651,16 +9638,9 @@ async function loadSiblingFiles(sourceText, sourcePath, propNames, candidateType
|
|
|
9651
9638
|
}
|
|
9652
9639
|
frontier = nextFrontier;
|
|
9653
9640
|
}
|
|
9654
|
-
const inHand = [sourceText, ...discovered.map((d) => d.content)];
|
|
9655
|
-
const declaredTypeNames = candidateTypeNames.filter((name) => inHand.some((text) => declaresAnyType(text, [name])));
|
|
9656
9641
|
const usesNotShown = /* @__PURE__ */ new Set();
|
|
9657
|
-
const searchNames = [.../* @__PURE__ */ new Set([...propNames, ...declaredTypeNames])];
|
|
9658
|
-
let enlarged = 0;
|
|
9659
9642
|
const siblings = discovered.slice(0, MAX_SIBLING_FILES).map((d) => {
|
|
9660
|
-
const
|
|
9661
|
-
if (enlarge) enlarged++;
|
|
9662
|
-
const budget = enlarge ? MAX_TYPE_DECLARING_SIBLING_CHARS : MAX_SIBLING_SNIPPET_CHARS;
|
|
9663
|
-
const excerpt = excerptAroundNames(d.content, searchNames, budget);
|
|
9643
|
+
const excerpt = excerptAroundNames(d.content, propNames, MAX_SIBLING_SNIPPET_CHARS);
|
|
9664
9644
|
for (const name of excerpt.usesNotShown) usesNotShown.add(name);
|
|
9665
9645
|
return { path: d.path, content: excerpt.content };
|
|
9666
9646
|
});
|
|
@@ -9668,32 +9648,25 @@ async function loadSiblingFiles(sourceText, sourcePath, propNames, candidateType
|
|
|
9668
9648
|
return {
|
|
9669
9649
|
siblings,
|
|
9670
9650
|
truncatedCount,
|
|
9671
|
-
usesNotShown: propNames.filter((name) => usesNotShown.has(name))
|
|
9672
|
-
declaredTypeNames
|
|
9651
|
+
usesNotShown: propNames.filter((name) => usesNotShown.has(name))
|
|
9673
9652
|
};
|
|
9674
9653
|
}
|
|
9675
|
-
async function loadComponentSourceRef(name, sourcePath, propNames = []
|
|
9654
|
+
async function loadComponentSourceRef(name, sourcePath, propNames = []) {
|
|
9676
9655
|
let content = null;
|
|
9677
9656
|
let siblingFiles = [];
|
|
9678
9657
|
let truncatedSiblingCount = 0;
|
|
9679
9658
|
const usesNotShown = /* @__PURE__ */ new Set();
|
|
9680
9659
|
try {
|
|
9681
9660
|
const rawText = await readFile7(sourcePath, "utf8");
|
|
9661
|
+
const mainExcerpt = excerptAroundNames(rawText, propNames, MAX_COMPONENT_SOURCE_CHARS);
|
|
9662
|
+
content = mainExcerpt.content;
|
|
9663
|
+
for (const name2 of mainExcerpt.usesNotShown) usesNotShown.add(name2);
|
|
9682
9664
|
let siblingUsesNotShown;
|
|
9683
|
-
let declaredTypeNames;
|
|
9684
9665
|
({
|
|
9685
9666
|
siblings: siblingFiles,
|
|
9686
9667
|
truncatedCount: truncatedSiblingCount,
|
|
9687
|
-
usesNotShown: siblingUsesNotShown
|
|
9688
|
-
|
|
9689
|
-
} = await loadSiblingFiles(rawText, sourcePath, propNames, identifiersIn(propTypes)));
|
|
9690
|
-
const mainExcerpt = excerptAroundNames(
|
|
9691
|
-
rawText,
|
|
9692
|
-
[.../* @__PURE__ */ new Set([...propNames, ...declaredTypeNames])],
|
|
9693
|
-
MAX_COMPONENT_SOURCE_CHARS
|
|
9694
|
-
);
|
|
9695
|
-
content = mainExcerpt.content;
|
|
9696
|
-
for (const name2 of mainExcerpt.usesNotShown) usesNotShown.add(name2);
|
|
9668
|
+
usesNotShown: siblingUsesNotShown
|
|
9669
|
+
} = await loadSiblingFiles(rawText, sourcePath, propNames));
|
|
9697
9670
|
for (const name2 of siblingUsesNotShown) usesNotShown.add(name2);
|
|
9698
9671
|
} catch {
|
|
9699
9672
|
}
|
|
@@ -9708,18 +9681,13 @@ async function loadComponentSourceRefs(db, sessionId2) {
|
|
|
9708
9681
|
const rows = db.prepare(
|
|
9709
9682
|
`SELECT component_id, name, source, source_path FROM raw_components WHERE session_id = ? AND status = 'generated' ORDER BY rowid`
|
|
9710
9683
|
).all(sessionId2);
|
|
9711
|
-
const
|
|
9712
|
-
`SELECT name
|
|
9684
|
+
const propNamesFor = db.prepare(
|
|
9685
|
+
`SELECT name FROM raw_props WHERE session_id = ? AND component_id = ? ORDER BY position`
|
|
9713
9686
|
);
|
|
9714
9687
|
return Promise.all(
|
|
9715
9688
|
rows.map((r) => {
|
|
9716
|
-
const
|
|
9717
|
-
return loadComponentSourceRef(
|
|
9718
|
-
r.name,
|
|
9719
|
-
r.source_path ?? r.source,
|
|
9720
|
-
props.map((p) => p.name),
|
|
9721
|
-
props.map((p) => p.type)
|
|
9722
|
-
);
|
|
9689
|
+
const propNames = propNamesFor.all(sessionId2, r.component_id).map((p) => p.name);
|
|
9690
|
+
return loadComponentSourceRef(r.name, r.source_path ?? r.source, propNames);
|
|
9723
9691
|
})
|
|
9724
9692
|
);
|
|
9725
9693
|
}
|
|
@@ -10602,7 +10570,7 @@ function lookupSelectCache(db, componentHash, promptHash, cliVersion2) {
|
|
|
10602
10570
|
updatedAt: row.updated_at
|
|
10603
10571
|
};
|
|
10604
10572
|
}
|
|
10605
|
-
var SCHEMA, MAX_COMPONENT_SOURCE_CHARS, MAX_SIBLING_FILES, MAX_SIBLING_SNIPPET_CHARS,
|
|
10573
|
+
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;
|
|
10606
10574
|
var init_db = __esm({
|
|
10607
10575
|
"packages/experience-design-system-cli/src/session/db.ts"() {
|
|
10608
10576
|
"use strict";
|
|
@@ -10788,19 +10756,10 @@ CREATE INDEX IF NOT EXISTS idx_scanned_files_session ON scanned_files(session
|
|
|
10788
10756
|
MAX_COMPONENT_SOURCE_CHARS = 8e3;
|
|
10789
10757
|
MAX_SIBLING_FILES = 5;
|
|
10790
10758
|
MAX_SIBLING_SNIPPET_CHARS = 1200;
|
|
10791
|
-
MAX_TYPE_DECLARING_SIBLING_CHARS = 4e3;
|
|
10792
|
-
MAX_ENLARGED_SIBLINGS = 2;
|
|
10793
10759
|
MAX_SIBLING_DEPTH = 2;
|
|
10794
10760
|
MAX_SIBLING_CANDIDATES_EXPLORED = 25;
|
|
10795
10761
|
RELATIVE_IMPORT_PATTERN = /from\s+['"](\.[^'"]+)['"]/g;
|
|
10796
10762
|
SIBLING_FILE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
10797
|
-
TS_ESM_EXTENSION_REWRITES = [
|
|
10798
|
-
[".js", [".ts", ".tsx", ".js", ".jsx"]],
|
|
10799
|
-
[".jsx", [".tsx", ".jsx"]],
|
|
10800
|
-
[".mjs", [".mts", ".mjs"]],
|
|
10801
|
-
[".cjs", [".cts", ".cjs"]]
|
|
10802
|
-
];
|
|
10803
|
-
IDENTIFIER_PATTERN = /[A-Za-z_$][A-Za-z0-9_$]*/g;
|
|
10804
10763
|
_cliCacheVersionCache = null;
|
|
10805
10764
|
}
|
|
10806
10765
|
});
|
|
@@ -32517,8 +32476,7 @@ async function runOneComponent(agent, model, db, sessionId2, component, tokensIn
|
|
|
32517
32476
|
const sourceRef = await loadComponentSourceRef(
|
|
32518
32477
|
component.name,
|
|
32519
32478
|
component.sourcePath ?? component.source,
|
|
32520
|
-
component.props.map((p) => p.name)
|
|
32521
|
-
component.props.map((p) => p.type)
|
|
32479
|
+
component.props.map((p) => p.name)
|
|
32522
32480
|
);
|
|
32523
32481
|
const prompt2 = await buildPrompt({
|
|
32524
32482
|
skill: "components",
|
|
@@ -32759,8 +32717,7 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
32759
32717
|
const sampleSourceRef = sampleComponent ? await loadComponentSourceRef(
|
|
32760
32718
|
sampleComponent.name,
|
|
32761
32719
|
sampleComponent.sourcePath ?? sampleComponent.source,
|
|
32762
|
-
sampleComponent.props.map((p) => p.name)
|
|
32763
|
-
sampleComponent.props.map((p) => p.type)
|
|
32720
|
+
sampleComponent.props.map((p) => p.name)
|
|
32764
32721
|
) : void 0;
|
|
32765
32722
|
const prompt2 = await buildPrompt({
|
|
32766
32723
|
skill,
|
|
@@ -36126,7 +36083,8 @@ async function setupBuild(repoRoot) {
|
|
|
36126
36083
|
var AGENT_DEFS = [
|
|
36127
36084
|
{ name: "Claude Code", binary: "claude", installHint: "npm install -g @anthropic-ai/claude-code && claude login" },
|
|
36128
36085
|
{ name: "OpenAI Codex", binary: "codex", installHint: "npm install -g @openai/codex (requires OPENAI_API_KEY)" },
|
|
36129
|
-
{ name: "OpenCode", binary: "opencode", installHint: "npm install -g opencode-ai && opencode auth" }
|
|
36086
|
+
{ name: "OpenCode", binary: "opencode", installHint: "npm install -g opencode-ai && opencode auth" },
|
|
36087
|
+
{ name: "GitHub Copilot", binary: "copilot", installHint: "npm install -g @github/copilot && copilot" }
|
|
36130
36088
|
];
|
|
36131
36089
|
function pick(items, defaultIdx = 0) {
|
|
36132
36090
|
items.forEach((item, i) => {
|
|
@@ -36161,7 +36119,7 @@ async function promptCodexModel() {
|
|
|
36161
36119
|
return void 0;
|
|
36162
36120
|
}
|
|
36163
36121
|
async function setupAgent() {
|
|
36164
|
-
section("Step 4: Coding agent (claude, codex, or
|
|
36122
|
+
section("Step 4: Coding agent (claude, codex, opencode, or copilot)", "[required]");
|
|
36165
36123
|
info("experiences import uses a coding agent to generate component definitions.");
|
|
36166
36124
|
info("");
|
|
36167
36125
|
const found = (await Promise.all(AGENT_DEFS.map(async (a) => await binaryExists(a.binary) ? a : null))).filter(
|
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[]
|
|
110
|
+
export declare function loadComponentSourceRef(name: string, sourcePath: string, propNames?: 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,7 +16,6 @@ export interface SourceExcerpt {
|
|
|
16
16
|
*/
|
|
17
17
|
usesNotShown: string[];
|
|
18
18
|
}
|
|
19
|
-
export declare function escapeForRegExp(value: string): string;
|
|
20
19
|
/**
|
|
21
20
|
* Returns `text` whole when it fits `maxChars`. Otherwise returns windows of
|
|
22
21
|
* `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.30.1-dev-build-
|
|
3
|
+
"version": "2.30.1-dev-build-6f93a80.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"svelte": "^5.56.4",
|
|
40
40
|
"ts-morph": "^27.0.2",
|
|
41
41
|
"typescript": "^5.9.3",
|
|
42
|
-
"@contentful/experience-design-system-cli-v2": "2.30.1-dev-build-
|
|
43
|
-
"@contentful/experience-design-system-types": "2.30.1-dev-build-
|
|
42
|
+
"@contentful/experience-design-system-cli-v2": "2.30.1-dev-build-6f93a80.0",
|
|
43
|
+
"@contentful/experience-design-system-types": "2.30.1-dev-build-6f93a80.0"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@tsconfig/node24": "^24.0.4",
|
|
@@ -52,9 +52,9 @@
|
|
|
52
52
|
"ink-testing-library": "^4.0.0",
|
|
53
53
|
"typescript-eslint": "^8.67.0",
|
|
54
54
|
"vitest": "^4.0.16",
|
|
55
|
-
"@contentful/experience-design-system-
|
|
56
|
-
"@contentful/experience-design-system-
|
|
57
|
-
"@contentful/experience-design-system-
|
|
55
|
+
"@contentful/experience-design-system-extraction": "2.30.1-dev-build-6f93a80.0",
|
|
56
|
+
"@contentful/experience-design-system-generation": "2.30.1-dev-build-6f93a80.0",
|
|
57
|
+
"@contentful/experience-design-system-client": "2.30.1-dev-build-6f93a80.0"
|
|
58
58
|
},
|
|
59
59
|
"repository": {
|
|
60
60
|
"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.
|
|
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.
|
|
@@ -208,14 +208,12 @@ the default "could be overridden" (every default can), or because the tie-break
|
|
|
208
208
|
below exists. Nothing downstream re-derives, corrects, or second-guesses this —
|
|
209
209
|
what you emit is what ships.
|
|
210
210
|
|
|
211
|
-
**Ambiguity resolves to `enum` — but only when Q1–Q3 cannot be answered
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
"string"` per "no legal `enum` with a guessed list" — either way, record the
|
|
218
|
-
ambiguity in `reason`. When the three
|
|
211
|
+
**Ambiguity resolves to `enum` — but only when Q1–Q3 cannot be answered.** The
|
|
212
|
+
tie-break applies solely when the source shown does not let you answer the three
|
|
213
|
+
questions: the property is interpolated in one place and looked up in another,
|
|
214
|
+
the use sits in a file you cannot see, or the source is truncated at the point
|
|
215
|
+
of use (the "uses not shown" line). In those cases emit `enum` (or `string` when
|
|
216
|
+
it has no value set) and record the ambiguity in `reason`. When the three
|
|
219
217
|
questions *can* be answered from the source shown, they are answered and the
|
|
220
218
|
tie-break does not apply: a clear Q1-yes is `enum`, a clear Q2-yes + Q3-yes is
|
|
221
219
|
`token`, and a type annotation does not create ambiguity. An `enum` is delivered
|
|
@@ -299,7 +297,7 @@ A prop with a complex TypeScript type is **not automatically excluded**. Many pr
|
|
|
299
297
|
| Raw type pattern | How to resolve |
|
|
300
298
|
|---|---|
|
|
301
299
|
| `'primary' \| 'secondary' \| 'ghost'` (union of literals) | → `enum`, extract `values` |
|
|
302
|
-
| `HeadingSize` / `ButtonVariant` / any named type that is clearly a finite set of visual options | → `enum
|
|
300
|
+
| `HeadingSize` / `ButtonVariant` / any named type that is clearly a finite set of visual options | → `enum`. **Never invent a value list.** Use only values you can cite from the source shown (the type's own declaration, a lookup table, a default). If the source shown does not enumerate the values, this is the "cannot be answered" case — apply "Ambiguity resolves to `enum`": emit `enum` (or `string` if it has no discoverable value set) and record the missing evidence in `description` (not `reason` — `description` ships to the CDF, `reason` does not), e.g. `"WARNING: values not visible in source — enum values unconfirmed"`. Do not default to a generic guess like `['sm', 'md', 'lg']` or `['primary', 'secondary']` — those are one design system's vocabulary, not a fallback for missing evidence. |
|
|
303
301
|
| `Variant` / `variant` prop | Usually a visual design variant. → `enum`, `cdf_category: "design"`. Same evidence rule as above — cite values from the source shown; do not invent them. |
|
|
304
302
|
| `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. |
|
|
305
303
|
| `ExperienceConfiguration<Variant>` / deep generic | Personalization config — → `exclude_prop`, reason: `"personalization configuration — framework internal"` |
|
|
@@ -464,30 +462,14 @@ footer is supplementary and optional
|
|
|
464
462
|
{"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."}
|
|
465
463
|
```
|
|
466
464
|
|
|
467
|
-
### Named type (HeadingSize, ButtonVariant, etc.)
|
|
465
|
+
### Named type (HeadingSize, ButtonVariant, etc.)
|
|
468
466
|
|
|
469
|
-
When a prop has a named TypeScript type that is not inlined as a union literal,
|
|
470
|
-
the value set is whatever that type's declaration says. A type *name* is not
|
|
471
|
-
evidence of its members: `HeadingSize` could be `h1`–`h6`, `sm|md|lg`, or
|
|
472
|
-
`s|m|l|xl`. Guessing picks one design system's vocabulary and is wrong more
|
|
473
|
-
often than it is right, and a wrong name matches no style rule — the author's
|
|
474
|
-
setting silently does nothing.
|
|
467
|
+
When a prop has a named TypeScript type that is not inlined as a union literal, reason from the prop name and type name to infer the finite value set.
|
|
475
468
|
|
|
476
|
-
So: look for the declaration in the source shown. If it is there, copy it. If it
|
|
477
|
-
is not, **emit `string`** — `string` is an unrestricted enum, so the author stays
|
|
478
|
-
unrestricted but correct.
|
|
479
|
-
|
|
480
|
-
Members visible in the source shown — copy them, do not paraphrase:
|
|
481
|
-
```
|
|
482
|
-
titleSize has type HeadingSize; HeadingSize is declared in the excerpt as 'sm' | 'md' | 'lg'
|
|
483
|
-
{"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."}
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
Members NOT visible in the source shown — degrade to `string`:
|
|
487
469
|
```
|
|
488
|
-
titleSize has type HeadingSize
|
|
489
|
-
|
|
490
|
-
{"tool":"classify_prop","prop":"titleSize","cdf_type":"
|
|
470
|
+
titleSize has type HeadingSize — this is a named enum controlling heading size
|
|
471
|
+
inferring likely values: ["h1", "h2", "h3", "h4", "h5", "h6"] — documenting inference
|
|
472
|
+
{"tool":"classify_prop","prop":"titleSize","cdf_type":"enum","cdf_category":"design","required":false,"values":["h1","h2","h3","h4","h5","h6"],"description":"Heading level — inferred from HeadingSize type name; actual values may be h1–h6 or sm/md/lg"}
|
|
491
473
|
```
|
|
492
474
|
|
|
493
475
|
### href prop
|
|
@@ -513,7 +495,7 @@ Before emitting any tool calls, verify:
|
|
|
513
495
|
1. Every prop in the input has exactly one `classify_prop` or `exclude_prop` call
|
|
514
496
|
2. Every slot has exactly one `classify_slot` call
|
|
515
497
|
3. `classify_component` is emitted exactly once
|
|
516
|
-
4. Every `cdf_type: "enum"` has a non-empty `values` array
|
|
498
|
+
4. Every `cdf_type: "enum"` has a non-empty `values` array
|
|
517
499
|
5. Every `cdf_type: "token"` has `token_kind` (or a warning in `description` if lookup failed) and does **not** have `values` set
|
|
518
500
|
6. No `cdf_type: "link"` — all href/url props use `string`
|
|
519
501
|
7. `required` values are JSON booleans, not strings
|
|
@@ -525,7 +507,7 @@ Before emitting any tool calls, verify:
|
|
|
525
507
|
13. `rationale.description` follows the same "Description content rules" as `description` — no internal initiative names (`INTEG-*`, `EDSI`, `DSI`, `M1`, `M2`, wave/phase references, etc.).
|
|
526
508
|
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`.
|
|
527
509
|
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.
|
|
528
|
-
16. Every `enum`'s `values` array is copied from evidence in the
|
|
510
|
+
16. Every `enum`'s `values` array is copied from evidence in the source shown (a type declaration, lookup table, or default) — none of it was invented. Where no evidence exists for the values, the `description` states so (e.g. `"WARNING: values not visible in source"`), not just `reason`.
|
|
529
511
|
|
|
530
512
|
After the run completes, the developer can validate the pipeline output with:
|
|
531
513
|
|
|
@@ -546,6 +528,4 @@ properties — this is never acceptable.
|
|
|
546
528
|
If you are genuinely uncertain about every prop, classify each as:
|
|
547
529
|
{"tool":"classify_prop","prop":"<name>","cdf_type":"string","cdf_category":"content","required":false,"description":"Uncertain classification — review recommended"}
|
|
548
530
|
|
|
549
|
-
An imperfect classification is infinitely better than no classification.
|
|
550
|
-
about **classifying a prop rather than dropping it** — it is not a licence to
|
|
551
|
-
invent a `values` list. The uncertain answer is `cdf_type: "string"`, every time.
|
|
531
|
+
An imperfect classification is infinitely better than no classification.
|