@contentful/experience-design-system-cli 2.15.2-dev-build-a2c2d9e.0 → 2.15.2-dev-build-31eb989.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 +1 -1
- package/dist/src/analyze/command.js +3 -7
- package/dist/src/analyze/select-agent/command.js +4 -4
- package/dist/src/apply/command.js +26 -18
- package/dist/src/apply/error-parser.d.ts +12 -0
- package/dist/src/apply/error-parser.js +129 -4
- package/dist/src/apply/tui/ServerApplyView.js +2 -5
- package/dist/src/generate/command.js +3 -2
- package/dist/src/import/command.js +4 -7
- package/dist/src/import/orchestrator.js +11 -7
- package/dist/src/import/tui/WizardApp.js +13 -3
- package/dist/src/import/tui/steps/DoneStep.d.ts +6 -1
- package/dist/src/import/tui/steps/DoneStep.js +2 -2
- package/dist/src/import/tui/steps/PreviewValidationErrorStep.js +10 -1
- package/package.json +3 -3
- package/dist/src/lib/agent-model-options.d.ts +0 -13
- package/dist/src/lib/agent-model-options.js +0 -14
package/dist/package.json
CHANGED
|
@@ -2,7 +2,6 @@ import { createElement } from 'react';
|
|
|
2
2
|
import { render } from 'ink';
|
|
3
3
|
import { mkdir, readdir, readFile, stat, writeFile } from 'node:fs/promises';
|
|
4
4
|
import { isAbsolute, join, relative, resolve } from 'node:path';
|
|
5
|
-
import { addAgentModelOptions } from '../lib/agent-model-options.js';
|
|
6
5
|
import { extractComponents, preClassifyComponent, isNonAuthorableComponent, computeExtractionScore, deriveNeedsReview, describeReviewReasons, inspectComponentSource, validateExtractedComponents, } from '@contentful/experience-design-system-extraction';
|
|
7
6
|
import { AnalyzeView } from './tui/AnalyzeView.js';
|
|
8
7
|
import { registerAnalyzeEditCommand } from './select/command.js';
|
|
@@ -166,7 +165,7 @@ export function registerAnalyzeCommand(program) {
|
|
|
166
165
|
const analyze = program
|
|
167
166
|
.command('analyze')
|
|
168
167
|
.description('Extract component definitions from a project, or correct analysis output');
|
|
169
|
-
|
|
168
|
+
analyze
|
|
170
169
|
.command('extract')
|
|
171
170
|
.description('Extract component definitions from a project')
|
|
172
171
|
.requiredOption('--project <path>', 'Path to the project root')
|
|
@@ -178,11 +177,8 @@ export function registerAnalyzeCommand(program) {
|
|
|
178
177
|
.option('--composition-agent', 'Opt into agentic mapping resolution when deterministic sources find no groups (implies --composite)')
|
|
179
178
|
.option('--composition-refresh', 'Force the mapping agent to run even where deterministic sources answered (implies --composite)')
|
|
180
179
|
.option('--generate-map <path>', 'Write a skeleton interchange map from resolved composition (implies --composite)')
|
|
181
|
-
.option('--prompt <stage=value>', 'Override a stage prompt (repeatable). value is a file path or literal text, e.g. --prompt composition=./p.md', (v, acc) => [...acc, v], [])
|
|
182
|
-
|
|
183
|
-
includeModel: false,
|
|
184
|
-
agentDescription: 'Coding agent for composition mapping resolution (claude|codex|opencode|cursor)',
|
|
185
|
-
})
|
|
180
|
+
.option('--prompt <stage=value>', 'Override a stage prompt (repeatable). value is a file path or literal text, e.g. --prompt composition=./p.md', (v, acc) => [...acc, v], [])
|
|
181
|
+
.option('--agent <name>', 'Coding agent for composition mapping resolution (claude|codex|opencode|cursor)')
|
|
186
182
|
.option('--composition-agent-mode <mode>', "Agent resolution mode: 'parser' (agent writes a sandboxed parser — deterministic, default) or 'edges' (agent lists edges directly)", 'parser')
|
|
187
183
|
.action(async (opts) => {
|
|
188
184
|
const resolveUnreachable = (() => {
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { addAgentModelOptions } from '../../lib/agent-model-options.js';
|
|
2
1
|
import { openPipelineDb, loadRawComponents, loadScannedFiles, createStep, updateStep, computeComponentInputHash, lookupSelectCache, storeSelectCache, getCliCacheVersion, } from '../../session/db.js';
|
|
3
2
|
import { hashPromptForSkill } from '../../session/cache-keys.js';
|
|
4
3
|
import { appendReviewEvent, getRefineArtifactsRoot, ensureRefineSession, getRefineSessionPaths, saveReviewState, } from '../select/persistence.js';
|
|
@@ -301,12 +300,13 @@ async function selectAllComponents(agent, model, components, verbose, skillPathO
|
|
|
301
300
|
return results;
|
|
302
301
|
}
|
|
303
302
|
export function registerAnalyzeSelectAgentCommand(program) {
|
|
304
|
-
|
|
303
|
+
program
|
|
305
304
|
.command('select-agent')
|
|
306
305
|
.description('Use an AI agent to select components for Contentful Experience Orchestration')
|
|
307
306
|
.option('--session <id>', 'Session ID from analyze extract (defaults to most recent)')
|
|
308
|
-
.option('--project-root <path>', 'Project root for resolving component source files')
|
|
309
|
-
|
|
307
|
+
.option('--project-root <path>', 'Project root for resolving component source files')
|
|
308
|
+
.option('--agent <name>', 'Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)')
|
|
309
|
+
.option('--model <name>', 'Model to use (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)')
|
|
310
310
|
.option('--verbose', 'Show full agent output including reasoning text')
|
|
311
311
|
.option('--dry-run', 'Print the prompt for the first component without invoking the agent')
|
|
312
312
|
.option('--exclude-invalid', 'Auto-reject components with validation errors instead of failing loud (LLM cannot fix structural issues)')
|
|
@@ -4,6 +4,7 @@ import { access, readFile, readdir, stat } from 'node:fs/promises';
|
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
import { validateCDF, flattenDTCG, validateDTCG, buildManifest, buildFilteredManifest, } from '@contentful/experience-design-system-types';
|
|
6
6
|
import { ApiError, ImportApiClient } from './api-client.js';
|
|
7
|
+
import { formatEdsiError } from './error-parser.js';
|
|
7
8
|
import { openPipelineDb, loadCDFComponents } from '../session/db.js';
|
|
8
9
|
import { findSlotCycles, suggestCycleBreakEdge, formatCyclePath } from '../analyze/cycle-detection.js';
|
|
9
10
|
import { isEmptyPreview } from './preview-utils.js';
|
|
@@ -17,6 +18,13 @@ function die(message) {
|
|
|
17
18
|
process.stderr.write(`${message}\n`);
|
|
18
19
|
process.exit(1);
|
|
19
20
|
}
|
|
21
|
+
function formatApiError(error, verbose = false) {
|
|
22
|
+
const formatted = formatEdsiError(error.body || error.message, { verbose, raw: error.body }) || error.message;
|
|
23
|
+
const phase = error.message.split('\n', 1)[0];
|
|
24
|
+
return /^(?:apply|preview|poll) failed: \d+$/.test(phase) && formatted !== phase
|
|
25
|
+
? `${phase}\n${formatted}`
|
|
26
|
+
: formatted;
|
|
27
|
+
}
|
|
20
28
|
async function pathExists(p) {
|
|
21
29
|
return access(p)
|
|
22
30
|
.then(() => true)
|
|
@@ -289,7 +297,7 @@ function buildApplyOutput(operation, spaceId, environmentId, host) {
|
|
|
289
297
|
.map((item) => ({
|
|
290
298
|
entityType: item.entityType,
|
|
291
299
|
entityId: item.id,
|
|
292
|
-
error: item.error,
|
|
300
|
+
error: formatEdsiError(item.error),
|
|
293
301
|
})),
|
|
294
302
|
};
|
|
295
303
|
}
|
|
@@ -435,7 +443,7 @@ export function registerApplyCommand(program) {
|
|
|
435
443
|
}
|
|
436
444
|
catch (e) {
|
|
437
445
|
if (e instanceof ApiError)
|
|
438
|
-
die(`Error: ${e
|
|
446
|
+
die(`Error: ${formatApiError(e)}`);
|
|
439
447
|
throw e;
|
|
440
448
|
}
|
|
441
449
|
const { components, tokens, client } = inputs;
|
|
@@ -444,7 +452,7 @@ export function registerApplyCommand(program) {
|
|
|
444
452
|
}
|
|
445
453
|
catch (e) {
|
|
446
454
|
if (e instanceof ApiError)
|
|
447
|
-
die(`Error: ${e
|
|
455
|
+
die(`Error: ${formatApiError(e)}`);
|
|
448
456
|
const cause = e instanceof Error && e.cause instanceof Error ? e.cause.message : '';
|
|
449
457
|
die(`Error: unable to connect to API host${cause ? `: ${cause}` : ''}`);
|
|
450
458
|
}
|
|
@@ -455,7 +463,7 @@ export function registerApplyCommand(program) {
|
|
|
455
463
|
}
|
|
456
464
|
catch (e) {
|
|
457
465
|
if (e instanceof ApiError)
|
|
458
|
-
die(`Error: ${e
|
|
466
|
+
die(`Error: ${formatApiError(e)}`);
|
|
459
467
|
throw e;
|
|
460
468
|
}
|
|
461
469
|
const spaceId = opts.spaceId;
|
|
@@ -501,7 +509,7 @@ export function registerApplyCommand(program) {
|
|
|
501
509
|
}
|
|
502
510
|
catch (e) {
|
|
503
511
|
if (e instanceof ApiError)
|
|
504
|
-
die(`Error: ${e.
|
|
512
|
+
die(`Error: ${formatApiError(e, opts.verbose)}`);
|
|
505
513
|
throw e;
|
|
506
514
|
}
|
|
507
515
|
const { components, tokens, client } = inputs;
|
|
@@ -511,7 +519,7 @@ export function registerApplyCommand(program) {
|
|
|
511
519
|
}
|
|
512
520
|
catch (e) {
|
|
513
521
|
if (e instanceof ApiError)
|
|
514
|
-
die(`Error: ${e.
|
|
522
|
+
die(`Error: ${formatApiError(e, opts.verbose)}`);
|
|
515
523
|
throw e;
|
|
516
524
|
}
|
|
517
525
|
const manifest = buildManifest(components, tokens);
|
|
@@ -521,7 +529,7 @@ export function registerApplyCommand(program) {
|
|
|
521
529
|
}
|
|
522
530
|
catch (e) {
|
|
523
531
|
if (e instanceof ApiError)
|
|
524
|
-
die(`Error: ${e.
|
|
532
|
+
die(`Error: ${formatApiError(e, opts.verbose)}`);
|
|
525
533
|
throw e;
|
|
526
534
|
}
|
|
527
535
|
const spaceId = opts.spaceId;
|
|
@@ -566,7 +574,7 @@ export function registerApplyCommand(program) {
|
|
|
566
574
|
}
|
|
567
575
|
catch (e) {
|
|
568
576
|
if (e instanceof ApiError)
|
|
569
|
-
die(`Error: ${e.
|
|
577
|
+
die(`Error: ${formatApiError(e, opts.verbose)}`);
|
|
570
578
|
throw e;
|
|
571
579
|
}
|
|
572
580
|
process.stderr.write(`Apply operation started: ${operation.sys.id}\n`);
|
|
@@ -575,7 +583,7 @@ export function registerApplyCommand(program) {
|
|
|
575
583
|
}
|
|
576
584
|
catch (e) {
|
|
577
585
|
if (e instanceof ApiError)
|
|
578
|
-
die(`Error: ${e.
|
|
586
|
+
die(`Error: ${formatApiError(e, opts.verbose)}`);
|
|
579
587
|
throw e;
|
|
580
588
|
}
|
|
581
589
|
const summary = buildApplyOutput(operation, spaceId, environmentId, opts.host);
|
|
@@ -600,7 +608,7 @@ export function registerApplyCommand(program) {
|
|
|
600
608
|
spaceId,
|
|
601
609
|
environmentId,
|
|
602
610
|
status: 'error',
|
|
603
|
-
error: e.
|
|
611
|
+
error: formatApiError(e, opts.verbose),
|
|
604
612
|
}));
|
|
605
613
|
return;
|
|
606
614
|
}
|
|
@@ -621,7 +629,7 @@ export function registerApplyCommand(program) {
|
|
|
621
629
|
spaceId,
|
|
622
630
|
environmentId,
|
|
623
631
|
status: 'error',
|
|
624
|
-
error: e.
|
|
632
|
+
error: formatApiError(e, opts.verbose),
|
|
625
633
|
}));
|
|
626
634
|
return;
|
|
627
635
|
}
|
|
@@ -677,7 +685,7 @@ export function registerApplyCommand(program) {
|
|
|
677
685
|
}
|
|
678
686
|
catch (e) {
|
|
679
687
|
if (e instanceof ApiError)
|
|
680
|
-
die(`Error: ${e
|
|
688
|
+
die(`Error: ${formatApiError(e)}`);
|
|
681
689
|
throw e;
|
|
682
690
|
}
|
|
683
691
|
const { components, tokens, client } = inputs;
|
|
@@ -687,7 +695,7 @@ export function registerApplyCommand(program) {
|
|
|
687
695
|
}
|
|
688
696
|
catch (e) {
|
|
689
697
|
if (e instanceof ApiError)
|
|
690
|
-
die(`Error: ${e
|
|
698
|
+
die(`Error: ${formatApiError(e)}`);
|
|
691
699
|
throw e;
|
|
692
700
|
}
|
|
693
701
|
const fullManifest = buildManifest(components, tokens);
|
|
@@ -697,7 +705,7 @@ export function registerApplyCommand(program) {
|
|
|
697
705
|
}
|
|
698
706
|
catch (e) {
|
|
699
707
|
if (e instanceof ApiError)
|
|
700
|
-
die(`Error: ${e
|
|
708
|
+
die(`Error: ${formatApiError(e)}`);
|
|
701
709
|
throw e;
|
|
702
710
|
}
|
|
703
711
|
const spaceId = opts.spaceId;
|
|
@@ -735,7 +743,7 @@ export function registerApplyCommand(program) {
|
|
|
735
743
|
}
|
|
736
744
|
catch (e) {
|
|
737
745
|
if (e instanceof ApiError)
|
|
738
|
-
die(`Error: ${e
|
|
746
|
+
die(`Error: ${formatApiError(e)}`);
|
|
739
747
|
throw e;
|
|
740
748
|
}
|
|
741
749
|
process.stderr.write(`Apply operation started: ${operation.sys.id}\n`);
|
|
@@ -744,7 +752,7 @@ export function registerApplyCommand(program) {
|
|
|
744
752
|
}
|
|
745
753
|
catch (e) {
|
|
746
754
|
if (e instanceof ApiError)
|
|
747
|
-
die(`Error: ${e
|
|
755
|
+
die(`Error: ${formatApiError(e)}`);
|
|
748
756
|
throw e;
|
|
749
757
|
}
|
|
750
758
|
const summary = buildApplyOutput(operation, spaceId, environmentId, opts.host);
|
|
@@ -781,7 +789,7 @@ export function registerApplyCommand(program) {
|
|
|
781
789
|
spaceId,
|
|
782
790
|
environmentId,
|
|
783
791
|
status: 'error',
|
|
784
|
-
error: e
|
|
792
|
+
error: formatApiError(e),
|
|
785
793
|
}));
|
|
786
794
|
return;
|
|
787
795
|
}
|
|
@@ -802,7 +810,7 @@ export function registerApplyCommand(program) {
|
|
|
802
810
|
spaceId,
|
|
803
811
|
environmentId,
|
|
804
812
|
status: 'error',
|
|
805
|
-
error: e
|
|
813
|
+
error: formatApiError(e),
|
|
806
814
|
}));
|
|
807
815
|
return;
|
|
808
816
|
}
|
|
@@ -7,6 +7,14 @@ export interface ParsedEdsiError {
|
|
|
7
7
|
cycle: string[] | null;
|
|
8
8
|
/** True when the message survived cleaning as-is (no parseable structure). */
|
|
9
9
|
raw: boolean;
|
|
10
|
+
/** Validation details supplied by the API, normalized for terminal output. */
|
|
11
|
+
diagnostics?: ErrorDiagnostic[];
|
|
12
|
+
}
|
|
13
|
+
export interface ErrorDiagnostic {
|
|
14
|
+
message: string;
|
|
15
|
+
component?: string;
|
|
16
|
+
path?: string;
|
|
17
|
+
pathMissing?: boolean;
|
|
10
18
|
}
|
|
11
19
|
export declare function stripLambdaLogPrefix(body: string): string;
|
|
12
20
|
export declare function parseEdsiError(rawInput: string | undefined | null): ParsedEdsiError;
|
|
@@ -14,3 +22,7 @@ export declare function formatParsedEdsiError(parsed: ParsedEdsiError, opts?: {
|
|
|
14
22
|
verbose?: boolean;
|
|
15
23
|
raw?: string;
|
|
16
24
|
}): string;
|
|
25
|
+
export declare function formatEdsiError(raw: unknown, opts?: {
|
|
26
|
+
verbose?: boolean;
|
|
27
|
+
raw?: string;
|
|
28
|
+
}): string;
|
|
@@ -26,6 +26,66 @@ function parseObjectLiteralTail(body) {
|
|
|
26
26
|
}
|
|
27
27
|
return { code, cycle };
|
|
28
28
|
}
|
|
29
|
+
function asRecord(value) {
|
|
30
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value)
|
|
31
|
+
? value
|
|
32
|
+
: null;
|
|
33
|
+
}
|
|
34
|
+
function displayValue(value) {
|
|
35
|
+
if (typeof value === 'string')
|
|
36
|
+
return value;
|
|
37
|
+
if (typeof value === 'number' || typeof value === 'boolean')
|
|
38
|
+
return String(value);
|
|
39
|
+
if (value === null || value === undefined)
|
|
40
|
+
return null;
|
|
41
|
+
try {
|
|
42
|
+
return JSON.stringify(value);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function formatPath(value) {
|
|
49
|
+
if (typeof value === 'string')
|
|
50
|
+
return value ? { path: value } : { pathMissing: true };
|
|
51
|
+
if (Array.isArray(value)) {
|
|
52
|
+
const parts = value.filter((part) => typeof part === 'string' || typeof part === 'number');
|
|
53
|
+
return parts.length > 0 ? { path: parts.join(' › ') } : { pathMissing: true };
|
|
54
|
+
}
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
function componentFromPath(path) {
|
|
58
|
+
if (!path)
|
|
59
|
+
return undefined;
|
|
60
|
+
const manifestMatch = path.match(/manifest:components\/([^/›]+)/);
|
|
61
|
+
if (manifestMatch?.[1])
|
|
62
|
+
return manifestMatch[1];
|
|
63
|
+
const parts = path
|
|
64
|
+
.split(/[›/]/)
|
|
65
|
+
.map((part) => part.trim())
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
const componentsIndex = parts.findIndex((part) => part === 'components');
|
|
68
|
+
return componentsIndex === -1 ? undefined : parts[componentsIndex + 1];
|
|
69
|
+
}
|
|
70
|
+
function parseValidationDiagnostics(details) {
|
|
71
|
+
if (!Array.isArray(details.errors))
|
|
72
|
+
return [];
|
|
73
|
+
const diagnostics = [];
|
|
74
|
+
for (const rawError of details.errors) {
|
|
75
|
+
const error = asRecord(rawError);
|
|
76
|
+
if (!error)
|
|
77
|
+
continue;
|
|
78
|
+
const location = formatPath(error.path);
|
|
79
|
+
const component = displayValue(error.component ?? error.componentName ?? error.componentId) ?? componentFromPath(location.path);
|
|
80
|
+
const message = displayValue(error.message) ??
|
|
81
|
+
displayValue(error.details) ??
|
|
82
|
+
displayValue(error.error) ??
|
|
83
|
+
displayValue(error.name) ??
|
|
84
|
+
'Validation failed';
|
|
85
|
+
diagnostics.push({ message, ...location, ...(component ? { component } : {}) });
|
|
86
|
+
}
|
|
87
|
+
return diagnostics;
|
|
88
|
+
}
|
|
29
89
|
function parseJsonBody(body) {
|
|
30
90
|
let parsed;
|
|
31
91
|
try {
|
|
@@ -34,12 +94,12 @@ function parseJsonBody(body) {
|
|
|
34
94
|
catch {
|
|
35
95
|
return null;
|
|
36
96
|
}
|
|
37
|
-
|
|
97
|
+
const p = asRecord(parsed);
|
|
98
|
+
if (!p)
|
|
38
99
|
return null;
|
|
39
|
-
const
|
|
40
|
-
const details = (p.details && typeof p.details === 'object' ? p.details : {});
|
|
100
|
+
const details = asRecord(p.details) ?? {};
|
|
41
101
|
const pick = (k) => (p[k] !== undefined ? p[k] : details[k]);
|
|
42
|
-
const codeRaw = pick('code');
|
|
102
|
+
const codeRaw = pick('code') ?? asRecord(p.sys)?.id;
|
|
43
103
|
const messageRaw = pick('message');
|
|
44
104
|
const cycleRaw = pick('cycle');
|
|
45
105
|
const out = {};
|
|
@@ -52,8 +112,30 @@ function parseJsonBody(body) {
|
|
|
52
112
|
if (strs.length > 0)
|
|
53
113
|
out.cycle = strs;
|
|
54
114
|
}
|
|
115
|
+
const diagnostics = parseValidationDiagnostics(details);
|
|
116
|
+
if (diagnostics.length > 0)
|
|
117
|
+
out.diagnostics = diagnostics;
|
|
55
118
|
return out;
|
|
56
119
|
}
|
|
120
|
+
function parseBindingDiagnostics(body) {
|
|
121
|
+
if (!/binding configurations are invalid|Pointer path does not exist/i.test(body))
|
|
122
|
+
return null;
|
|
123
|
+
const locationMatches = [...body.matchAll(/(?:^|\.\s)([A-Za-z0-9_$-]+(?:\s*›\s*[A-Za-z0-9_$-]+){2,})/g)];
|
|
124
|
+
const location = locationMatches.at(-1)?.[1];
|
|
125
|
+
const pointerMessage = body.match(/Pointer path does not exist for '\[object Object\]':\s*(.+?)(?=\.\s*Default\s*›|$)/i)?.[1];
|
|
126
|
+
const graphQlMessage = body.match(/return GraphQL validation error:\s*(.+?)(?=\.\s*Default\s*›|$)/i)?.[1];
|
|
127
|
+
const heading = body.match(/^(.*?)(?=\.?\s*Pointer path does not exist|\.?\s*Default\s*›)/i)?.[1]?.trim();
|
|
128
|
+
const diagnostics = [];
|
|
129
|
+
if (pointerMessage)
|
|
130
|
+
diagnostics.push({ message: pointerMessage.trim(), ...(location ? { path: location } : {}) });
|
|
131
|
+
if (graphQlMessage)
|
|
132
|
+
diagnostics.push({ message: graphQlMessage.trim(), ...(location ? { path: location } : {}) });
|
|
133
|
+
return {
|
|
134
|
+
code: 'BindingValidationFailed',
|
|
135
|
+
message: heading || 'One or more binding configurations are invalid.',
|
|
136
|
+
...(diagnostics.length > 0 ? { diagnostics } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
57
139
|
// ApiError.message shape: `${phasePrefix}\n${body}` where phasePrefix looks
|
|
58
140
|
// like `apply failed: 400`, `preview failed: 422`, `poll failed: 500`. We
|
|
59
141
|
// only strip the prefix line when the first line matches this shape —
|
|
@@ -75,12 +157,33 @@ export function parseEdsiError(rawInput) {
|
|
|
75
157
|
}
|
|
76
158
|
const cleaned = stripLambdaLogPrefix(body);
|
|
77
159
|
const json = parseJsonBody(cleaned) ?? parseJsonBody(body);
|
|
160
|
+
const bindingFromJson = typeof json?.message === 'string' ? parseBindingDiagnostics(json.message) : null;
|
|
161
|
+
if (bindingFromJson) {
|
|
162
|
+
return {
|
|
163
|
+
code: bindingFromJson.code ?? json?.code ?? null,
|
|
164
|
+
message: bindingFromJson.message ?? cleaned,
|
|
165
|
+
cycle: null,
|
|
166
|
+
raw: false,
|
|
167
|
+
...(bindingFromJson.diagnostics ? { diagnostics: bindingFromJson.diagnostics } : {}),
|
|
168
|
+
};
|
|
169
|
+
}
|
|
78
170
|
if (json && (json.code || json.message || json.cycle)) {
|
|
79
171
|
return {
|
|
80
172
|
code: json.code ?? null,
|
|
81
173
|
message: json.message ?? (cleaned || prefix),
|
|
82
174
|
cycle: json.cycle ?? null,
|
|
83
175
|
raw: false,
|
|
176
|
+
...(json.diagnostics ? { diagnostics: json.diagnostics } : {}),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
const binding = parseBindingDiagnostics(cleaned);
|
|
180
|
+
if (binding) {
|
|
181
|
+
return {
|
|
182
|
+
code: binding.code ?? null,
|
|
183
|
+
message: binding.message ?? cleaned,
|
|
184
|
+
cycle: null,
|
|
185
|
+
raw: false,
|
|
186
|
+
...(binding.diagnostics ? { diagnostics: binding.diagnostics } : {}),
|
|
84
187
|
};
|
|
85
188
|
}
|
|
86
189
|
const literal = parseObjectLiteralTail(cleaned);
|
|
@@ -104,6 +207,22 @@ export function formatParsedEdsiError(parsed, opts = {}) {
|
|
|
104
207
|
if (parsed.message) {
|
|
105
208
|
lines.push(parsed.message);
|
|
106
209
|
}
|
|
210
|
+
for (const diagnostic of parsed.diagnostics ?? []) {
|
|
211
|
+
const context = [
|
|
212
|
+
diagnostic.component ? `Component: ${diagnostic.component}` : null,
|
|
213
|
+
diagnostic.path ? `Path: ${diagnostic.path}` : null,
|
|
214
|
+
].filter(Boolean);
|
|
215
|
+
lines.push(`- ${diagnostic.message}${context.length > 0 ? ` (${context.join('; ')})` : ''}`);
|
|
216
|
+
if (diagnostic.pathMissing) {
|
|
217
|
+
lines.push(' Location: not provided by the server. Review the submitted manifest; no component or field was identified.');
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
if (parsed.code === 'BindingValidationFailed') {
|
|
221
|
+
const location = parsed.diagnostics?.find((diagnostic) => diagnostic.path)?.path;
|
|
222
|
+
lines.push(location
|
|
223
|
+
? `Next action: review the binding at ${location} and correct its pointer or GraphQL selection.`
|
|
224
|
+
: 'Next action: review the binding pointer and GraphQL selection.');
|
|
225
|
+
}
|
|
107
226
|
if (parsed.cycle && parsed.cycle.length > 0) {
|
|
108
227
|
lines.push(`Cycle: ${parsed.cycle.join(' → ')} → ${parsed.cycle[0]}`);
|
|
109
228
|
lines.push('Break the cycle by removing at least one $allowedComponents entry.');
|
|
@@ -115,3 +234,9 @@ export function formatParsedEdsiError(parsed, opts = {}) {
|
|
|
115
234
|
}
|
|
116
235
|
return lines.filter(Boolean).join('\n');
|
|
117
236
|
}
|
|
237
|
+
export function formatEdsiError(raw, opts = {}) {
|
|
238
|
+
const input = typeof raw === 'string' ? raw : displayValue(raw);
|
|
239
|
+
if (!input)
|
|
240
|
+
return '';
|
|
241
|
+
return formatParsedEdsiError(parseEdsiError(input), { ...opts, raw: opts.raw ?? input });
|
|
242
|
+
}
|
|
@@ -2,6 +2,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { Box, Text, useInput } from 'ink';
|
|
3
3
|
import { ServerPreviewView } from './ServerPreviewView.js';
|
|
4
4
|
import { buildPostPushUrl } from '../../lib/contentful-urls.js';
|
|
5
|
+
import { formatEdsiError } from '../error-parser.js';
|
|
5
6
|
export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, onConfirm, onCancel, }) {
|
|
6
7
|
useInput((input, key) => {
|
|
7
8
|
if (key.return)
|
|
@@ -35,9 +36,5 @@ export function ServerApplyDone({ operation, spaceId, environmentId, host }) {
|
|
|
35
36
|
return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Import complete \u2014 ", environmentId, " @ ", spaceId] }), _jsx(Text, { children: " " }), _jsxs(Text, { color: "green", children: [" \u2713 ", operation.summary.succeeded, " succeeded"] }), operation.summary.failed > 0 && _jsxs(Text, { color: "red", children: [" \u2717 ", operation.summary.failed, " failed"] }), operation.summary.failed === 0 && _jsx(Text, { dimColor: true, children: " All entities imported successfully." }), failures.length > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsx(Text, { bold: true, children: " Failures:" }), failures.map((item, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { color: "red", children: [' ', "\u2717 ", item.entityType, ": ", item.id] }), item.error && _jsxs(Text, { dimColor: true, children: [" ", formatItemError(item.error)] })] }, i)))] })), operation.sys.status === 'succeeded' && operation.summary.succeeded > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " View your design system:" }), _jsxs(Text, { color: "cyan", children: [" ", buildPostPushUrl({ host: host ?? 'api.contentful.com', spaceId, environmentId })] })] })), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: " Press Q to exit." })] }));
|
|
36
37
|
}
|
|
37
38
|
function formatItemError(error) {
|
|
38
|
-
|
|
39
|
-
return '';
|
|
40
|
-
if (typeof error === 'string')
|
|
41
|
-
return error;
|
|
42
|
-
return `${error.code}: ${error.message}`;
|
|
39
|
+
return formatEdsiError(error);
|
|
43
40
|
}
|
|
@@ -14,7 +14,6 @@ import { openPipelineDb, loadRawComponents, applyToolCalls, applyTokenToolCalls,
|
|
|
14
14
|
import { hashPromptForSkill } from '../session/cache-keys.js';
|
|
15
15
|
import { getRefineArtifactsRoot, getRefineSessionPaths } from '../analyze/select/persistence.js';
|
|
16
16
|
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
17
|
-
import { addAgentModelOptions } from '../lib/agent-model-options.js';
|
|
18
17
|
const execFileAsync = promisify(execFile);
|
|
19
18
|
const VALID_AGENTS = new Set(['claude', 'codex', 'opencode', 'cursor']);
|
|
20
19
|
const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
|
|
@@ -569,7 +568,9 @@ async function runGenerateSkill(skill, opts, verbose = false) {
|
|
|
569
568
|
}
|
|
570
569
|
}
|
|
571
570
|
function addAgentFlags(cmd) {
|
|
572
|
-
return
|
|
571
|
+
return cmd
|
|
572
|
+
.option('--agent <name>', 'Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)')
|
|
573
|
+
.option('--model <name>', 'Model to use (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)')
|
|
573
574
|
.option('--verbose', 'Show full agent output including reasoning text')
|
|
574
575
|
.option('--dry-run', 'Print the prompt without invoking the agent')
|
|
575
576
|
.option('--no-cache', 'Bypass ALL fine-grained caches (extract, select, generate) and force AI re-run. ' +
|
|
@@ -2,7 +2,6 @@ import { resolve, join } from 'node:path';
|
|
|
2
2
|
import { runPipeline } from './orchestrator.js';
|
|
3
3
|
import { resolveAutoFilter } from './auto-filter-resolve.js';
|
|
4
4
|
import { resolveAgent, resolveModel } from './agent-model-resolve.js';
|
|
5
|
-
import { addAgentModelOptions } from '../lib/agent-model-options.js';
|
|
6
5
|
import { resolveCompositionMode } from '../lib/composition-mode.js';
|
|
7
6
|
import { readExperiencesCredentials } from '../credentials-store.js';
|
|
8
7
|
import { DEFAULT_CONFIGURED_HOST, toConfiguredHost } from '../host-utils.js';
|
|
@@ -12,18 +11,16 @@ import { resolvePromptFlags } from './print-prompt.js';
|
|
|
12
11
|
import { shouldShowRunPicker } from '../runs/run-picker-mount.js';
|
|
13
12
|
import { dispatchPickerSelection } from './picker-dispatch.js';
|
|
14
13
|
export function registerImportCommand(program) {
|
|
15
|
-
|
|
14
|
+
program
|
|
16
15
|
.command('import')
|
|
17
16
|
.description('Run the full pipeline: analyze → select → generate → push')
|
|
18
17
|
.option('--space-id <id>', 'Contentful space ID (required unless --skip-apply)')
|
|
19
18
|
.option('--environment-id <id>', 'Contentful environment ID (required unless --skip-apply)')
|
|
20
19
|
.option('--cma-token <token>', 'CMA personal access token (or set CONTENTFUL_MANAGEMENT_TOKEN)')
|
|
21
20
|
.option('--project <path>', 'Path to the project root to analyze', '.')
|
|
22
|
-
.option('--out <path>', 'Output directory for pipeline artifacts')
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
modelDescription: 'Model to use for generate components (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)',
|
|
26
|
-
})
|
|
21
|
+
.option('--out <path>', 'Output directory for pipeline artifacts')
|
|
22
|
+
.option('--agent <name>', 'Agent to use for generate components (overrides credentials.json; falls back to "claude")')
|
|
23
|
+
.option('--model <name>', 'Model to use for generate components (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)')
|
|
27
24
|
.option('--tokens <path>', 'Path to a DTCG tokens.json file to push alongside generated components')
|
|
28
25
|
.option('--raw-tokens <path>', 'Path to a raw token source file (SCSS, CSS variables, JS/TS, Style Dictionary, etc.) to classify and import alongside components. Bypasses the interactive token prompt.')
|
|
29
26
|
.option('--select-all', 'Select all extracted components for generation (default)')
|
|
@@ -61,16 +61,20 @@ export function parseCycleComponentNames(report) {
|
|
|
61
61
|
return [...new Set(names)];
|
|
62
62
|
}
|
|
63
63
|
export function isPreviewValidationError(result) {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
64
|
+
if (result.exitCode === 0 || !result.stderr.includes(`${PREVIEW_ERROR_PREFIX} 422`))
|
|
65
|
+
return false;
|
|
66
|
+
return result.stderr.includes(VALIDATION_FAILED_CODE) || result.stderr.includes('[ValidationFailed]');
|
|
67
67
|
}
|
|
68
68
|
export function parseOffendingComponentNames(output) {
|
|
69
69
|
const jsonStart = output.indexOf('{');
|
|
70
|
-
if (jsonStart
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
if (jsonStart !== -1) {
|
|
71
|
+
const errors = parsePreviewValidationErrors(output.slice(jsonStart));
|
|
72
|
+
const names = [...new Set(errors.map((error) => error.componentName))];
|
|
73
|
+
if (names.length > 0)
|
|
74
|
+
return names;
|
|
75
|
+
}
|
|
76
|
+
const names = [...output.matchAll(/Component:\s*([^;\n)]+)/g)].map((match) => match[1].trim()).filter(Boolean);
|
|
77
|
+
return [...new Set(names)];
|
|
74
78
|
}
|
|
75
79
|
export function buildPushStepResult(args) {
|
|
76
80
|
const { created, updated, failed, durationMs, stderr, excludedByRetry, totalFailure } = args;
|
|
@@ -37,7 +37,7 @@ import { ImportApiClient, ApiError } from '../../apply/api-client.js';
|
|
|
37
37
|
import { detectSlotCycles, extractComponentsFromManifest, formatSlotCycleReport } from '../../apply/command.js';
|
|
38
38
|
import { findSlotCycles } from '../../analyze/cycle-detection.js';
|
|
39
39
|
import { buildComponentGraph } from '../../analyze/slot-graph.js';
|
|
40
|
-
import { parseEdsiError, formatParsedEdsiError } from '../../apply/error-parser.js';
|
|
40
|
+
import { parseEdsiError, formatEdsiError, formatParsedEdsiError } from '../../apply/error-parser.js';
|
|
41
41
|
import { handlePreview422, applySkipValidationErrors, clearedValidationErrorState } from './wizard-422-helpers.js';
|
|
42
42
|
import { parseGenerateStderrChunk } from './wizard-generate-progress.js';
|
|
43
43
|
import { spawnGenerateChild } from './spawn-generate.js';
|
|
@@ -927,7 +927,10 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
927
927
|
update({
|
|
928
928
|
step: 'error',
|
|
929
929
|
errorStep: 'apply preview',
|
|
930
|
-
errorMessage: e.message,
|
|
930
|
+
errorMessage: formatParsedEdsiError(parseEdsiError(e.body || e.message), {
|
|
931
|
+
verbose: process.env['EDSI_VERBOSE_ERRORS'] === '1',
|
|
932
|
+
raw: e.body,
|
|
933
|
+
}) || e.message,
|
|
931
934
|
errorAllowCredentialRetry: true,
|
|
932
935
|
});
|
|
933
936
|
return;
|
|
@@ -1056,6 +1059,13 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
1056
1059
|
failed: items.filter((i) => i.entityType === 'DesignToken' && i.status === 'failed').length,
|
|
1057
1060
|
},
|
|
1058
1061
|
summary: operation.summary,
|
|
1062
|
+
failures: items
|
|
1063
|
+
.filter((item) => item.status === 'failed')
|
|
1064
|
+
.map((item) => ({
|
|
1065
|
+
entityType: item.entityType,
|
|
1066
|
+
entityId: item.id,
|
|
1067
|
+
message: formatEdsiError(item.error),
|
|
1068
|
+
})),
|
|
1059
1069
|
};
|
|
1060
1070
|
}
|
|
1061
1071
|
else {
|
|
@@ -1645,7 +1655,7 @@ export function WizardApp({ initialSpaceId = '', initialEnvironmentId = 'master'
|
|
|
1645
1655
|
case 'done': {
|
|
1646
1656
|
const totalFailed = state.pushResult.componentTypes.failed + state.pushResult.designTokens.failed;
|
|
1647
1657
|
const teaser = buildRunTeaserLine(state.lastRunId);
|
|
1648
|
-
return (_jsx(DoneStep, { componentTypes: state.pushResult.componentTypes, designTokens: state.pushResult.designTokens, summary: state.pushResult.summary, spaceId: state.spaceId, environmentId: state.environmentId, host: state.host, ...(teaser ? { runTeaser: teaser } : {}), onExit: () => process.exit(totalFailed > 0 ? 1 : 0) }));
|
|
1658
|
+
return (_jsx(DoneStep, { componentTypes: state.pushResult.componentTypes, designTokens: state.pushResult.designTokens, summary: state.pushResult.summary, failures: state.pushResult.failures, spaceId: state.spaceId, environmentId: state.environmentId, host: state.host, ...(teaser ? { runTeaser: teaser } : {}), onExit: () => process.exit(totalFailed > 0 ? 1 : 0) }));
|
|
1649
1659
|
}
|
|
1650
1660
|
case 'preview-validation-error': {
|
|
1651
1661
|
return (_jsx(PreviewValidationErrorStep, { errors: state.previewValidationErrors, missingNames: state.previewValidationMissingNames, onEdit: () => {
|
|
@@ -17,7 +17,12 @@ type DoneStepProps = {
|
|
|
17
17
|
environmentId: string;
|
|
18
18
|
host?: string;
|
|
19
19
|
runTeaser?: string;
|
|
20
|
+
failures?: Array<{
|
|
21
|
+
entityType: string;
|
|
22
|
+
entityId: string;
|
|
23
|
+
message: string;
|
|
24
|
+
}>;
|
|
20
25
|
onExit: () => void;
|
|
21
26
|
};
|
|
22
|
-
export declare function DoneStep({ componentTypes, designTokens, summary, spaceId, environmentId, host, runTeaser, onExit, }: DoneStepProps): React.ReactElement;
|
|
27
|
+
export declare function DoneStep({ componentTypes, designTokens, summary, spaceId, environmentId, host, runTeaser, failures, onExit, }: DoneStepProps): React.ReactElement;
|
|
23
28
|
export {};
|
|
@@ -3,7 +3,7 @@ import { PALETTE } from '../../../analyze/select/tui/theme.js';
|
|
|
3
3
|
import { Box, Text } from 'ink';
|
|
4
4
|
import { useImmediateInput } from '../../../analyze/select/tui/hooks/useImmediateInput.js';
|
|
5
5
|
import { buildPostPushUrl } from '../../../lib/contentful-urls.js';
|
|
6
|
-
export function DoneStep({ componentTypes, designTokens, summary, spaceId, environmentId, host, runTeaser, onExit, }) {
|
|
6
|
+
export function DoneStep({ componentTypes, designTokens, summary, spaceId, environmentId, host, runTeaser, failures = [], onExit, }) {
|
|
7
7
|
useImmediateInput((input, key) => {
|
|
8
8
|
if (key.return || input === 'q' || key.escape) {
|
|
9
9
|
onExit();
|
|
@@ -20,5 +20,5 @@ export function DoneStep({ componentTypes, designTokens, summary, spaceId, envir
|
|
|
20
20
|
function EntityRows({ entity, label }) {
|
|
21
21
|
return (_jsxs(_Fragment, { children: [entity.created > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713" }), _jsxs(Text, { children: [entity.created, " ", label, entity.created !== 1 ? 's' : '', " created"] })] })), entity.updated > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713" }), _jsxs(Text, { children: [entity.updated, " ", label, entity.updated !== 1 ? 's' : '', " updated"] })] })), entity.removed > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713" }), _jsxs(Text, { children: [entity.removed, " ", label, entity.removed !== 1 ? 's' : '', " removed"] })] })), entity.failed > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.error, children: "\u2717" }), _jsxs(Text, { color: PALETTE.error, children: [entity.failed, " ", label, entity.failed !== 1 ? 's' : '', " failed \u2014 check logs above"] })] }))] }));
|
|
22
22
|
}
|
|
23
|
-
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [success ? (_jsx(Text, { bold: true, color: PALETTE.success, children: "Done!" })) : (_jsx(Text, { bold: true, color: PALETTE.warning, children: "\u26A0 Finished with errors" })), totalPushed === 0 && totalFailed === 0 && !summary ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Nothing was pushed \u2014 everything was already up to date." }) })) : (_jsxs(Box, { flexDirection: "column", gap: 0, marginTop: 1, children: [_jsx(EntityRows, { entity: componentTypes, label: "Component Type" }), _jsx(EntityRows, { entity: designTokens, label: "Design Token" }), summary && (_jsxs(Box, { gap: 1, marginTop: 1, children: [_jsxs(Text, { dimColor: true, children: ["Server: ", summary.succeeded, "/", summary.total, " succeeded"] }), summary.failed > 0 && _jsxs(Text, { color: PALETTE.error, children: [", ", summary.failed, " failed"] })] }))] })), _jsxs(Box, { gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Space:" }), _jsx(Text, { children: spaceId }), _jsx(Text, { dimColor: true, children: "/" }), _jsx(Text, { dimColor: true, children: "Environment:" }), _jsx(Text, { children: environmentId })] }), success && totalPushed > 0 && (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Your design system is now in Contentful ExO." }), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: "View it here:" }), _jsx(Text, { color: PALETTE.info, children: buildPostPushUrl({ host: host ?? 'api.contentful.com', spaceId, environmentId }) })] })] })), runTeaser && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: runTeaser }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[Enter / q] Exit" }) })] }));
|
|
23
|
+
return (_jsxs(Box, { flexDirection: "column", gap: 1, paddingX: 2, paddingY: 1, children: [success ? (_jsx(Text, { bold: true, color: PALETTE.success, children: "Done!" })) : (_jsx(Text, { bold: true, color: PALETTE.warning, children: "\u26A0 Finished with errors" })), totalPushed === 0 && totalFailed === 0 && !summary ? (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Nothing was pushed \u2014 everything was already up to date." }) })) : (_jsxs(Box, { flexDirection: "column", gap: 0, marginTop: 1, children: [_jsx(EntityRows, { entity: componentTypes, label: "Component Type" }), _jsx(EntityRows, { entity: designTokens, label: "Design Token" }), summary && (_jsxs(Box, { gap: 1, marginTop: 1, children: [_jsxs(Text, { dimColor: true, children: ["Server: ", summary.succeeded, "/", summary.total, " succeeded"] }), summary.failed > 0 && _jsxs(Text, { color: PALETTE.error, children: [", ", summary.failed, " failed"] })] }))] })), failures.length > 0 && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { bold: true, color: PALETTE.error, children: "Failure details" }), failures.map((failure) => (_jsxs(Text, { color: PALETTE.error, children: [failure.entityType, " ", failure.entityId, ": ", failure.message] }, `${failure.entityType}:${failure.entityId}`)))] })), _jsxs(Box, { gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Space:" }), _jsx(Text, { children: spaceId }), _jsx(Text, { dimColor: true, children: "/" }), _jsx(Text, { dimColor: true, children: "Environment:" }), _jsx(Text, { children: environmentId })] }), success && totalPushed > 0 && (_jsxs(Box, { flexDirection: "column", gap: 1, marginTop: 1, children: [_jsx(Text, { dimColor: true, children: "Your design system is now in Contentful ExO." }), _jsxs(Box, { flexDirection: "column", gap: 0, children: [_jsx(Text, { dimColor: true, children: "View it here:" }), _jsx(Text, { color: PALETTE.info, children: buildPostPushUrl({ host: host ?? 'api.contentful.com', spaceId, environmentId }) })] })] })), runTeaser && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: runTeaser }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[Enter / q] Exit" }) })] }));
|
|
24
24
|
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
1
|
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
2
|
import { GateStep } from './GateStep.js';
|
|
3
|
+
import { formatParsedEdsiError } from '../../../apply/error-parser.js';
|
|
3
4
|
export function PreviewValidationErrorStep({ errors, missingNames, onEdit, onSkip, onQuit, }) {
|
|
4
5
|
const uniqueNames = [...new Set(errors.map((e) => e.componentName))];
|
|
5
6
|
const matchedNames = uniqueNames.filter((n) => !missingNames.includes(n));
|
|
6
|
-
const errorLines = errors
|
|
7
|
+
const errorLines = errors
|
|
8
|
+
.map((error) => formatParsedEdsiError({
|
|
9
|
+
code: null,
|
|
10
|
+
message: '',
|
|
11
|
+
cycle: null,
|
|
12
|
+
raw: false,
|
|
13
|
+
diagnostics: [{ message: error.message, component: error.componentName, path: error.path }],
|
|
14
|
+
}))
|
|
15
|
+
.join('\n');
|
|
7
16
|
const missingNote = missingNames.length > 0
|
|
8
17
|
? `\n\nNote: ${missingNames.length} component name${missingNames.length === 1 ? '' : 's'} from the server (${missingNames.join(', ')}) ${missingNames.length === 1 ? 'does' : 'do'} not match anything in this session — they cannot be edited or skipped from here.`
|
|
9
18
|
: '';
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@contentful/experience-design-system-cli",
|
|
3
|
-
"version": "2.15.2-dev-build-
|
|
3
|
+
"version": "2.15.2-dev-build-31eb989.0",
|
|
4
4
|
"description": "Contentful Experiences design system import CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -34,8 +34,8 @@
|
|
|
34
34
|
"react": "^18.3.1",
|
|
35
35
|
"react-devtools-core": "^4.19.1",
|
|
36
36
|
"react-dom": "^18.3.1",
|
|
37
|
-
"@contentful/experience-design-system-
|
|
38
|
-
"@contentful/experience-design-system-
|
|
37
|
+
"@contentful/experience-design-system-extraction": "2.15.2-dev-build-31eb989.0",
|
|
38
|
+
"@contentful/experience-design-system-types": "2.15.2-dev-build-31eb989.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@tsconfig/node24": "^24.0.3",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
import type { Command } from 'commander';
|
|
2
|
-
export declare const AGENT_DESCRIPTION = "Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)";
|
|
3
|
-
export declare const MODEL_DESCRIPTION = "Model to use (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)";
|
|
4
|
-
export interface AgentModelOptionsConfig {
|
|
5
|
-
agentDescription?: string;
|
|
6
|
-
includeModel?: boolean;
|
|
7
|
-
modelDescription?: string;
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Register --agent and optionally --model flags to a Commander command, with customizable descriptions.
|
|
11
|
-
* Pass `includeModel: false` for commands where the model dimension is not applicable (e.g., mapping-resolution agents that select an agent for logic, not model choice).
|
|
12
|
-
*/
|
|
13
|
-
export declare function addAgentModelOptions(cmd: Command, config?: AgentModelOptionsConfig): Command;
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
export const AGENT_DESCRIPTION = 'Agent to use: claude, codex, opencode, cursor (defaults to value saved by experiences setup)';
|
|
2
|
-
export const MODEL_DESCRIPTION = 'Model to use (defaults to a lightweight per-agent model; override with EDS_AGENT_MODEL_<AGENT>)';
|
|
3
|
-
/**
|
|
4
|
-
* Register --agent and optionally --model flags to a Commander command, with customizable descriptions.
|
|
5
|
-
* Pass `includeModel: false` for commands where the model dimension is not applicable (e.g., mapping-resolution agents that select an agent for logic, not model choice).
|
|
6
|
-
*/
|
|
7
|
-
export function addAgentModelOptions(cmd, config = {}) {
|
|
8
|
-
const { agentDescription = AGENT_DESCRIPTION, includeModel = true, modelDescription = MODEL_DESCRIPTION } = config;
|
|
9
|
-
cmd.option('--agent <name>', agentDescription);
|
|
10
|
-
if (includeModel) {
|
|
11
|
-
cmd.option('--model <name>', modelDescription);
|
|
12
|
-
}
|
|
13
|
-
return cmd;
|
|
14
|
-
}
|