@contentful/experience-design-system-generation 2.26.1

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.
@@ -0,0 +1,9 @@
1
+ export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
2
+ export type { AgentName } from './agent-names.js';
3
+ export { buildArgs, checkAgentAuth, describeAgentFailure, extractSentinelOutput, parseSelectToolCallLines, parseTokenToolCallLines, parseToolCallLines, resolveAgentModel, resolveBinary, runAgent, } from './agent-runner.js';
4
+ export type { AgentAuthStatus, AgentDebugEvent, AgentRunResult, ClassifyComponentCall, ClassifyPropCall, ClassifySlotCall, ExcludePropCall, ParsedSelectToolCalls, ParsedTokenToolCalls, ParsedToolCalls, RejectComponentCall, SelectComponentCall, SelectToolCall, SetGroupCall, SetTokenCall, ToolCall, TokenToolCall, } from './agent-runner.js';
5
+ export { createLocalCliAgentInvoker } from './agent-invoker.js';
6
+ export type { AgentInvoker, CreateLocalCliAgentInvokerOptions, InvokeAgentOptions } from './agent-invoker.js';
7
+ export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './prompt-builder.js';
8
+ export type { Mode, PromptOptions, Skill } from './prompt-builder.js';
9
+ export { formatGenerateProgressLine } from './progress.js';
@@ -0,0 +1,10 @@
1
+ // Agent identity
2
+ export { AGENT_NAMES, DEFAULT_AGENT_NAME, isAgentName } from './agent-names.js';
3
+ // Agent invocation (low-level)
4
+ export { buildArgs, checkAgentAuth, describeAgentFailure, extractSentinelOutput, parseSelectToolCallLines, parseTokenToolCallLines, parseToolCallLines, resolveAgentModel, resolveBinary, runAgent, } from './agent-runner.js';
5
+ // Agent invocation (interface)
6
+ export { createLocalCliAgentInvoker } from './agent-invoker.js';
7
+ // Prompt building
8
+ export { buildPrompt, formatCustomPromptBanner, resolveSkillPath } from './prompt-builder.js';
9
+ // Progress reporting
10
+ export { formatGenerateProgressLine } from './progress.js';
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Formats a structured generation-progress line for the wizard parser.
3
+ *
4
+ * Emitted at terminal completion of each component (success, cache hit,
5
+ * pinned hit, or final failure). `done` represents the count of completed
6
+ * components, which is monotonically non-decreasing — unlike the legacy
7
+ * `[index+1/total]` line, which reports input position.
8
+ */
9
+ export declare function formatGenerateProgressLine(done: number, total: number, name: string): string;
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Formats a structured generation-progress line for the wizard parser.
3
+ *
4
+ * Emitted at terminal completion of each component (success, cache hit,
5
+ * pinned hit, or final failure). `done` represents the count of completed
6
+ * components, which is monotonically non-decreasing — unlike the legacy
7
+ * `[index+1/total]` line, which reports input position.
8
+ */
9
+ export function formatGenerateProgressLine(done, total, name) {
10
+ return `progress=generate:${done}/${total}:${name}`;
11
+ }
@@ -0,0 +1,32 @@
1
+ /** `components` — classify component props; `tokens` — classify design tokens; `select` — decide whether a component belongs in Contentful Experience Orchestration */
2
+ export type Skill = 'components' | 'tokens' | 'select';
3
+ export type Mode = 'autonomous';
4
+ /**
5
+ * Render the warning banner shown when a custom skill prompt is active.
6
+ * Always cites the bundled invariants that the override bypasses so the
7
+ * operator cannot miss it.
8
+ */
9
+ export declare function formatCustomPromptBanner(skill: 'components' | 'select', path: string): string;
10
+ export interface PromptOptions {
11
+ skill: Skill;
12
+ mode: Mode;
13
+ rawComponentsInline?: string;
14
+ rawTokensInline?: string;
15
+ /** Original filename for raw tokens — used to set the correct code fence language. */
16
+ rawTokensFilename?: string;
17
+ tokensInline?: string;
18
+ tokenMapInline?: string;
19
+ outDir: string;
20
+ /** For components skill only: the single component's name (used in error messages). */
21
+ componentName?: string;
22
+ /**
23
+ * Feature 8: custom prompt path override. When set, this absolute or relative
24
+ * `.md` path is read in place of the bundled skill file. The bundled-prompt
25
+ * invariants (utility-wrapper rejection, description content rules, etc.) do
26
+ * NOT apply under an override — callers are responsible for showing the
27
+ * appropriate warning banner.
28
+ */
29
+ skillPathOverride?: string;
30
+ }
31
+ export declare function buildPrompt(options: PromptOptions): Promise<string>;
32
+ export declare function resolveSkillPath(skill: Skill): string;
@@ -0,0 +1,204 @@
1
+ import { existsSync } from 'node:fs';
2
+ import { readFile } from 'node:fs/promises';
3
+ import { dirname, join, resolve } from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ /**
6
+ * Render the warning banner shown when a custom skill prompt is active.
7
+ * Always cites the bundled invariants that the override bypasses so the
8
+ * operator cannot miss it.
9
+ */
10
+ export function formatCustomPromptBanner(skill, path) {
11
+ return (`WARNING: Custom prompt active for ${skill}: ${path}\n` +
12
+ ` Bundled invariants (utility-wrapper rejection, description content rules) do NOT apply.\n` +
13
+ ` You are responsible for the prompt's correctness.\n`);
14
+ }
15
+ const SKILL_FILES = {
16
+ components: 'generate-components.md',
17
+ tokens: 'generate-tokens.md',
18
+ select: 'select-components.md',
19
+ };
20
+ export async function buildPrompt(options) {
21
+ const skillContent = await readSkillFile(options.skill, options.skillPathOverride);
22
+ const preamble = buildPreamble(options);
23
+ return `${preamble}\n\nSkill instructions follow:\n---\n${skillContent}`;
24
+ }
25
+ export function resolveSkillPath(skill) {
26
+ if (!(skill in SKILL_FILES))
27
+ throw new Error(`Invalid skill: ${skill}`);
28
+ // Walk up until we find the skills/ directory (works from both src/ and dist/src/ contexts)
29
+ const thisDir = dirname(fileURLToPath(import.meta.url));
30
+ let dir = thisDir;
31
+ for (;;) {
32
+ const candidate = join(dir, 'skills');
33
+ if (existsSync(candidate))
34
+ return join(candidate, SKILL_FILES[skill]);
35
+ const parent = resolve(dir, '..');
36
+ if (parent === dir) {
37
+ throw new Error(`skill file missing from CLI installation (could not locate skills/ directory from: ${thisDir})`);
38
+ }
39
+ dir = parent;
40
+ }
41
+ }
42
+ async function readSkillFile(skill, override) {
43
+ if (override) {
44
+ const skillPath = resolve(override);
45
+ try {
46
+ return await readFile(skillPath, 'utf8');
47
+ }
48
+ catch {
49
+ throw new Error(`custom prompt file not found (skill: ${skill}, path: ${skillPath})`);
50
+ }
51
+ }
52
+ const skillPath = resolveSkillPath(skill);
53
+ try {
54
+ return await readFile(skillPath, 'utf8');
55
+ }
56
+ catch {
57
+ throw new Error(`skill file missing from CLI installation — try reinstalling the CLI (looked for: ${skillPath})`);
58
+ }
59
+ }
60
+ function inferFenceLang(filename) {
61
+ if (!filename)
62
+ return 'json';
63
+ const ext = filename.split('.').pop()?.toLowerCase() ?? '';
64
+ const map = {
65
+ js: 'js',
66
+ mjs: 'js',
67
+ cjs: 'js',
68
+ ts: 'ts',
69
+ mts: 'ts',
70
+ cts: 'ts',
71
+ scss: 'scss',
72
+ sass: 'scss',
73
+ css: 'css',
74
+ json: 'json',
75
+ json5: 'json',
76
+ };
77
+ return map[ext] ?? 'text';
78
+ }
79
+ function buildPreamble(options) {
80
+ const { skill, rawComponentsInline, rawTokensInline, rawTokensFilename, tokensInline, tokenMapInline } = options;
81
+ const sections = [];
82
+ if (rawComponentsInline) {
83
+ sections.push(`Raw component data (JSON):\n\`\`\`json\n${rawComponentsInline}\n\`\`\``);
84
+ }
85
+ if (rawTokensInline) {
86
+ const lang = inferFenceLang(rawTokensFilename);
87
+ const label = rawTokensFilename ? `Raw token source (${rawTokensFilename})` : 'Raw token source';
88
+ sections.push(`${label}:\n\`\`\`${lang}\n${rawTokensInline}\n\`\`\``);
89
+ }
90
+ if (tokensInline) {
91
+ sections.push(`DTCG token data (for token kind lookups):\n\`\`\`json\n${tokensInline}\n\`\`\``);
92
+ }
93
+ if (tokenMapInline) {
94
+ sections.push(`Token-name sidecar (raw name → DTCG path):\n\`\`\`json\n${tokenMapInline}\n\`\`\``);
95
+ }
96
+ const inputBlock = sections.length > 0 ? `\n\n${sections.join('\n\n')}` : '';
97
+ if (skill === 'components') {
98
+ return buildComponentsAutonomousPreamble(inputBlock);
99
+ }
100
+ if (skill === 'select') {
101
+ return buildSelectAutonomousPreamble(inputBlock);
102
+ }
103
+ return buildTokensAutonomousPreamble(inputBlock);
104
+ }
105
+ function buildComponentsAutonomousPreamble(inputBlock) {
106
+ return `You are running as part of the experience-design-system-cli generate pipeline in AUTONOMOUS mode. The developer is not present to answer questions.
107
+
108
+ Context: You are classifying a React component for **Contentful Experience Orchestration**. The result is a Component Type — a schema that tells Contentful what a marketer can configure. Properties fall into three categories:
109
+ - **design**: controls how the component looks (variant, size, color, layout toggles)
110
+ - **content**: the data a content editor fills in (text, images, URLs, rich text)
111
+ - **state**: runtime behavioral flags (disabled, loading, expanded, identifiers)
112
+
113
+ For props with complex TypeScript types (named types, enums): reason from the prop name and type name to classify them. Do not automatically exclude a prop just because its type is a named reference — infer the likely values and classify it as enum if it controls appearance.
114
+
115
+ Your task: classify every prop and slot in the component below. Apply all judgment calls yourself — do not pause to ask for confirmation. Include a "description" field on each tool call to document your reasoning so the developer can review it afterward.
116
+
117
+ All input data is provided inline below — do not read any additional files.${inputBlock}
118
+
119
+ ## Output protocol
120
+
121
+ 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.
122
+
123
+ The four tool calls you may emit are:
124
+
125
+ \`\`\`
126
+ {"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>"}}
127
+
128
+ {"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>"}
129
+
130
+ {"tool":"exclude_prop","prop":"<propName>","reason":"<why excluded>"}
131
+
132
+ {"tool":"classify_slot","slot":"<slotName>","required":<bool>,"allowed_components":["ComponentName"],"description":"<reason>","rationale":"<why this slot was kept in the catalog>"}
133
+ \`\`\`
134
+
135
+ Rules:
136
+ - Emit exactly one JSON object per line. No multi-line JSON. No markdown fences around the lines.
137
+ - Every prop in the input must have exactly one call: either classify_prop or exclude_prop.
138
+ - Every slot in the input must have exactly one classify_slot call.
139
+ - Valid cdf_type values: string, richtext, media, enum, token, boolean
140
+ - Valid cdf_category values: content, design, state
141
+ - For enum type, always include "values" (non-empty string array).
142
+ - For token type, always include "token_kind" (DTCG \$type, e.g. "color").
143
+ - href and URL props → cdf_type "string", cdf_category "content". Do NOT use cdf_type "link" — it is not valid.
144
+ - Framework internals (ref, event handlers, test IDs) → exclude_prop.
145
+ - CSS design props (className, style, styles, positional/geometric props: top, bottom, left, right, rotation, offset, etc.) → classify_prop, cdf_type: "string", cdf_category: "design".
146
+ - 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.
147
+ - On classify_prop, "reason" is REQUIRED and is the LLM's internal rationale — 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.
148
+ - You may emit prose lines (not starting with {) anywhere — they are ignored by the parser and serve as your reasoning log.`;
149
+ }
150
+ function buildSelectAutonomousPreamble(inputBlock) {
151
+ return `You are running as part of the experience-design-system-cli import pipeline in AUTONOMOUS mode. The developer is not present to answer questions.
152
+
153
+ Your task: review the components provided below and decide whether each belongs in Contentful Experience Orchestration as a Component Type. The input is a JSON array — you may receive 1–N components in a single message. Emit one tool call per input component, named after the component. Apply all judgment calls yourself — do not pause to ask for confirmation. Include a brief "reason" to document your reasoning for each decision.
154
+
155
+ Key rule: accept any component that renders visible UI — atoms, molecules, and organisms are all valid Component Types in Contentful Experience Orchestration. Reject only components that produce zero visual output: React hooks, pure context providers, A/B testing or variant-routing wrappers, analytics trackers, and security utilities. Do NOT reject a component because it has few props, is low-level, or has some A/B testing or personalization-related props mixed in — those props are handled in the generate step.
156
+
157
+ All input data is provided inline below — do not read any additional files.${inputBlock}
158
+
159
+ ## Output protocol
160
+
161
+ Do NOT write any files or emit any JSON blobs. Instead, emit JSON tool calls one per line to stdout. The CLI reads your stdout line by line.
162
+
163
+ The two tool calls — emit exactly one per input component:
164
+
165
+ \`\`\`
166
+ {"tool":"select_component","name":"<ComponentName>","reason":"<brief reason>"}
167
+
168
+ {"tool":"reject_component","name":"<ComponentName>","reason":"<brief reason>"}
169
+ \`\`\`
170
+
171
+ Rules:
172
+ - Emit exactly one JSON object per line. No multi-line JSON. No markdown fences.
173
+ - Emit exactly one tool call per input component. The "name" field must match a component name from the input array exactly. Tool calls may appear in any order.
174
+ - You may emit prose lines (not starting with {) to reason before each tool call — they are ignored by the parser.`;
175
+ }
176
+ function buildTokensAutonomousPreamble(inputBlock) {
177
+ return `You are running as part of the experience-design-system-cli generate pipeline in AUTONOMOUS mode. The developer is not present to answer questions.
178
+
179
+ Your task: classify every raw token from the input below into a DTCG token tree. Apply all judgment calls yourself — do not pause to ask for confirmation. Include a "description" field on each set_token call to document your reasoning.
180
+
181
+ All input data is provided inline below — do not read any additional files.${inputBlock}
182
+
183
+ ## Output protocol
184
+
185
+ Do NOT write any files or emit any JSON blobs. Instead, emit one JSON object per line to stdout for each token or group. The CLI reads your stdout line by line and writes each entry directly to the pipeline database.
186
+
187
+ The two tool calls you may emit are:
188
+
189
+ \`\`\`
190
+ {"tool":"set_group","path":"<dot.notation.path>","description":"<optional group description>"}
191
+
192
+ {"tool":"set_token","path":"<dot.notation.path>","type":"<DTCG type>","value":<value>,"description":"<reason>"}
193
+ \`\`\`
194
+
195
+ Rules:
196
+ - Emit exactly one JSON object per line. No multi-line JSON. No markdown fences.
197
+ - Emit a set_group call for every intermediate group node in the tree.
198
+ - Emit a set_token call for every leaf token.
199
+ - "path" is dot-notation, e.g. "colors.brand.primary" — no leading dots or slashes.
200
+ - "type" must be one of the 13 valid DTCG types: color, dimension, fontFamily, fontWeight, duration, cubicBezier, number, strokeStyle, border, transition, shadow, gradient, typography.
201
+ - "value" must be valid JSON (string, number, array, or object depending on the type). Do NOT wrap it in quotes if it is a complex type.
202
+ - Emit set_group calls before the set_token calls that fall under them.
203
+ - You may emit prose lines (not starting with {) anywhere — they are ignored by the parser and serve as your reasoning log.`;
204
+ }
package/package.json ADDED
@@ -0,0 +1,56 @@
1
+ {
2
+ "name": "@contentful/experience-design-system-generation",
3
+ "version": "2.26.1",
4
+ "description": "Agent-invocation and skill-prompt engine for the Contentful Experience Design System SDK",
5
+ "license": "MIT",
6
+ "type": "module",
7
+ "main": "./dist/src/index.js",
8
+ "types": "./dist/src/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/src/index.d.ts",
12
+ "import": "./dist/src/index.js",
13
+ "node": "./dist/src/index.js"
14
+ }
15
+ },
16
+ "publishConfig": {
17
+ "access": "public",
18
+ "registry": "https://npm.pkg.github.com/"
19
+ },
20
+ "files": [
21
+ "dist/",
22
+ "skills/"
23
+ ],
24
+ "dependencies": {
25
+ "@contentful/experience-design-system-types": "2.26.1"
26
+ },
27
+ "devDependencies": {
28
+ "@tsconfig/node24": "^24.0.4",
29
+ "@types/node": "^24.0.3",
30
+ "eslint": "^9.39.5",
31
+ "eslint-config-prettier": "^10.1.8",
32
+ "eslint-plugin-prettier": "^5.5.6",
33
+ "typescript-eslint": "^8.67.0",
34
+ "vitest": "^4.0.16"
35
+ },
36
+ "repository": {
37
+ "type": "git",
38
+ "url": "git+https://github.com/contentful/experience-design-system-sdk-public.git",
39
+ "directory": "packages/experience-design-system-generation"
40
+ },
41
+ "homepage": "https://github.com/contentful/experience-design-system-sdk-public#readme",
42
+ "bugs": {
43
+ "url": "https://github.com/contentful/experience-design-system-sdk-public/issues"
44
+ },
45
+ "engines": {
46
+ "node": ">=24"
47
+ },
48
+ "scripts": {
49
+ "build": "nx build experience-design-system-generation",
50
+ "typecheck": "nx typecheck experience-design-system-generation",
51
+ "clean": "nx clean experience-design-system-generation",
52
+ "test": "nx test experience-design-system-generation",
53
+ "lint": "nx lint experience-design-system-generation",
54
+ "lint:fix": "nx lint:fix experience-design-system-generation"
55
+ }
56
+ }