@contentful/experience-design-system-cli 2.2.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.
Files changed (165) hide show
  1. package/README.md +532 -0
  2. package/bin/cli.js +58 -0
  3. package/dist/package.json +56 -0
  4. package/dist/src/analyze/command.d.ts +3 -0
  5. package/dist/src/analyze/command.js +175 -0
  6. package/dist/src/analyze/extract/astro.d.ts +5 -0
  7. package/dist/src/analyze/extract/astro.js +280 -0
  8. package/dist/src/analyze/extract/pipeline.d.ts +6 -0
  9. package/dist/src/analyze/extract/pipeline.js +298 -0
  10. package/dist/src/analyze/extract/react.d.ts +2 -0
  11. package/dist/src/analyze/extract/react.js +1949 -0
  12. package/dist/src/analyze/extract/slot-detection.d.ts +35 -0
  13. package/dist/src/analyze/extract/slot-detection.js +101 -0
  14. package/dist/src/analyze/extract/stencil.d.ts +2 -0
  15. package/dist/src/analyze/extract/stencil.js +293 -0
  16. package/dist/src/analyze/extract/tsx-shared.d.ts +8 -0
  17. package/dist/src/analyze/extract/tsx-shared.js +263 -0
  18. package/dist/src/analyze/extract/vue-tsx.d.ts +2 -0
  19. package/dist/src/analyze/extract/vue-tsx.js +498 -0
  20. package/dist/src/analyze/extract/vue.d.ts +5 -0
  21. package/dist/src/analyze/extract/vue.js +647 -0
  22. package/dist/src/analyze/extract/web-components.d.ts +2 -0
  23. package/dist/src/analyze/extract/web-components.js +866 -0
  24. package/dist/src/analyze/pre-classify.d.ts +17 -0
  25. package/dist/src/analyze/pre-classify.js +144 -0
  26. package/dist/src/analyze/select/command.d.ts +2 -0
  27. package/dist/src/analyze/select/command.js +256 -0
  28. package/dist/src/analyze/select/index.d.ts +6 -0
  29. package/dist/src/analyze/select/index.js +5 -0
  30. package/dist/src/analyze/select/parser.d.ts +6 -0
  31. package/dist/src/analyze/select/parser.js +53 -0
  32. package/dist/src/analyze/select/persistence.d.ts +9 -0
  33. package/dist/src/analyze/select/persistence.js +42 -0
  34. package/dist/src/analyze/select/stdout.d.ts +7 -0
  35. package/dist/src/analyze/select/stdout.js +3 -0
  36. package/dist/src/analyze/select/tui/App.d.ts +8 -0
  37. package/dist/src/analyze/select/tui/App.js +491 -0
  38. package/dist/src/analyze/select/tui/components/ComponentDetail.d.ts +20 -0
  39. package/dist/src/analyze/select/tui/components/ComponentDetail.js +43 -0
  40. package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +11 -0
  41. package/dist/src/analyze/select/tui/components/FieldEditor.js +531 -0
  42. package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +10 -0
  43. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +15 -0
  44. package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +7 -0
  45. package/dist/src/analyze/select/tui/components/HelpOverlay.js +11 -0
  46. package/dist/src/analyze/select/tui/components/JsonEditor.d.ts +11 -0
  47. package/dist/src/analyze/select/tui/components/JsonEditor.js +154 -0
  48. package/dist/src/analyze/select/tui/components/JsonPanel.d.ts +11 -0
  49. package/dist/src/analyze/select/tui/components/JsonPanel.js +62 -0
  50. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.d.ts +8 -0
  51. package/dist/src/analyze/select/tui/components/PreviewSummaryBar.js +29 -0
  52. package/dist/src/analyze/select/tui/components/QuitDialog.d.ts +8 -0
  53. package/dist/src/analyze/select/tui/components/QuitDialog.js +14 -0
  54. package/dist/src/analyze/select/tui/components/Sidebar.d.ts +15 -0
  55. package/dist/src/analyze/select/tui/components/Sidebar.js +48 -0
  56. package/dist/src/analyze/select/tui/components/SourcePanel.d.ts +11 -0
  57. package/dist/src/analyze/select/tui/components/SourcePanel.js +52 -0
  58. package/dist/src/analyze/select/tui/components/StatusBar.d.ts +11 -0
  59. package/dist/src/analyze/select/tui/components/StatusBar.js +6 -0
  60. package/dist/src/analyze/select/tui/components/TopBar.d.ts +10 -0
  61. package/dist/src/analyze/select/tui/components/TopBar.js +5 -0
  62. package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +24 -0
  63. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +68 -0
  64. package/dist/src/analyze/select/tui/hooks/useKeymap.d.ts +24 -0
  65. package/dist/src/analyze/select/tui/hooks/useKeymap.js +67 -0
  66. package/dist/src/analyze/select/tui/hooks/useSession.d.ts +19 -0
  67. package/dist/src/analyze/select/tui/hooks/useSession.js +52 -0
  68. package/dist/src/analyze/select/tui/hooks/useUndo.d.ts +8 -0
  69. package/dist/src/analyze/select/tui/hooks/useUndo.js +26 -0
  70. package/dist/src/analyze/select/types.d.ts +46 -0
  71. package/dist/src/analyze/select/types.js +20 -0
  72. package/dist/src/analyze/select-agent/command.d.ts +2 -0
  73. package/dist/src/analyze/select-agent/command.js +208 -0
  74. package/dist/src/analyze/tui/AnalyzeView.d.ts +24 -0
  75. package/dist/src/analyze/tui/AnalyzeView.js +38 -0
  76. package/dist/src/apply/api-client.d.ts +35 -0
  77. package/dist/src/apply/api-client.js +143 -0
  78. package/dist/src/apply/command.d.ts +6 -0
  79. package/dist/src/apply/command.js +787 -0
  80. package/dist/src/apply/manifest.d.ts +1 -0
  81. package/dist/src/apply/manifest.js +1 -0
  82. package/dist/src/apply/tui/SelectView.d.ts +18 -0
  83. package/dist/src/apply/tui/SelectView.js +34 -0
  84. package/dist/src/apply/tui/ServerApplyView.d.ts +32 -0
  85. package/dist/src/apply/tui/ServerApplyView.js +42 -0
  86. package/dist/src/apply/tui/ServerPreviewView.d.ts +9 -0
  87. package/dist/src/apply/tui/ServerPreviewView.js +21 -0
  88. package/dist/src/credentials-store.d.ts +8 -0
  89. package/dist/src/credentials-store.js +30 -0
  90. package/dist/src/generate/agent-runner.d.ts +86 -0
  91. package/dist/src/generate/agent-runner.js +314 -0
  92. package/dist/src/generate/command.d.ts +2 -0
  93. package/dist/src/generate/command.js +545 -0
  94. package/dist/src/generate/edit/command.d.ts +2 -0
  95. package/dist/src/generate/edit/command.js +126 -0
  96. package/dist/src/generate/prompt-builder.d.ts +18 -0
  97. package/dist/src/generate/prompt-builder.js +202 -0
  98. package/dist/src/generate/tui/GenerateView.d.ts +12 -0
  99. package/dist/src/generate/tui/GenerateView.js +10 -0
  100. package/dist/src/import/command.d.ts +2 -0
  101. package/dist/src/import/command.js +96 -0
  102. package/dist/src/import/orchestrator.d.ts +37 -0
  103. package/dist/src/import/orchestrator.js +374 -0
  104. package/dist/src/import/path-utils.d.ts +15 -0
  105. package/dist/src/import/path-utils.js +30 -0
  106. package/dist/src/import/tui/WizardApp.d.ts +10 -0
  107. package/dist/src/import/tui/WizardApp.js +906 -0
  108. package/dist/src/import/tui/steps/CredentialsStep.d.ts +15 -0
  109. package/dist/src/import/tui/steps/CredentialsStep.js +79 -0
  110. package/dist/src/import/tui/steps/DoneStep.d.ts +20 -0
  111. package/dist/src/import/tui/steps/DoneStep.js +17 -0
  112. package/dist/src/import/tui/steps/ErrorStep.d.ts +8 -0
  113. package/dist/src/import/tui/steps/ErrorStep.js +11 -0
  114. package/dist/src/import/tui/steps/GateStep.d.ts +14 -0
  115. package/dist/src/import/tui/steps/GateStep.js +20 -0
  116. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +8 -0
  117. package/dist/src/import/tui/steps/GenerateReviewStep.js +208 -0
  118. package/dist/src/import/tui/steps/PathValidationStep.d.ts +10 -0
  119. package/dist/src/import/tui/steps/PathValidationStep.js +151 -0
  120. package/dist/src/import/tui/steps/PreviewStep.d.ts +21 -0
  121. package/dist/src/import/tui/steps/PreviewStep.js +36 -0
  122. package/dist/src/import/tui/steps/RunningStep.d.ts +10 -0
  123. package/dist/src/import/tui/steps/RunningStep.js +20 -0
  124. package/dist/src/import/tui/steps/TokenInputStep.d.ts +8 -0
  125. package/dist/src/import/tui/steps/TokenInputStep.js +70 -0
  126. package/dist/src/import/tui/steps/WelcomeStep.d.ts +7 -0
  127. package/dist/src/import/tui/steps/WelcomeStep.js +33 -0
  128. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +15 -0
  129. package/dist/src/import/tui/steps/WizardPreviewStep.js +121 -0
  130. package/dist/src/import/tui/steps/preview-diff.d.ts +10 -0
  131. package/dist/src/import/tui/steps/preview-diff.js +132 -0
  132. package/dist/src/index.d.ts +1 -0
  133. package/dist/src/index.js +2 -0
  134. package/dist/src/output/format.d.ts +23 -0
  135. package/dist/src/output/format.js +110 -0
  136. package/dist/src/print/command.d.ts +2 -0
  137. package/dist/src/print/command.js +199 -0
  138. package/dist/src/print/validate/tui/ValidateView.d.ts +15 -0
  139. package/dist/src/print/validate/tui/ValidateView.js +37 -0
  140. package/dist/src/print/validate/validators/cdf-validator.d.ts +2 -0
  141. package/dist/src/print/validate/validators/cdf-validator.js +104 -0
  142. package/dist/src/print/validate/validators/dtcg-validator.d.ts +2 -0
  143. package/dist/src/print/validate/validators/dtcg-validator.js +110 -0
  144. package/dist/src/print/validate/validators/format-errors.d.ts +12 -0
  145. package/dist/src/print/validate/validators/format-errors.js +18 -0
  146. package/dist/src/program.d.ts +2 -0
  147. package/dist/src/program.js +25 -0
  148. package/dist/src/session/command.d.ts +2 -0
  149. package/dist/src/session/command.js +261 -0
  150. package/dist/src/session/db.d.ts +111 -0
  151. package/dist/src/session/db.js +1114 -0
  152. package/dist/src/session/migration.d.ts +4 -0
  153. package/dist/src/session/migration.js +117 -0
  154. package/dist/src/session/session-id.d.ts +1 -0
  155. package/dist/src/session/session-id.js +212 -0
  156. package/dist/src/session/stats.d.ts +27 -0
  157. package/dist/src/session/stats.js +89 -0
  158. package/dist/src/setup/command.d.ts +2 -0
  159. package/dist/src/setup/command.js +765 -0
  160. package/dist/src/types.d.ts +48 -0
  161. package/dist/src/types.js +1 -0
  162. package/package.json +55 -0
  163. package/skills/generate-components.md +361 -0
  164. package/skills/generate-tokens.md +194 -0
  165. package/skills/select-components.md +180 -0
@@ -0,0 +1,374 @@
1
+ import { mkdir } from 'node:fs/promises';
2
+ import { join, resolve } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { execFile } from 'node:child_process';
5
+ import { openPipelineDb, getOrCreateSession, createStep, updateStep, findLatestSessionForCommand, } from '../session/db.js';
6
+ function findCliPath() {
7
+ return join(fileURLToPath(import.meta.url), '..', '..', '..', '..', 'bin', 'cli.js');
8
+ }
9
+ async function runStep(args, cliPath, env = {}, streamStderr = false) {
10
+ return new Promise((res) => {
11
+ const child = execFile('node', [cliPath, ...args], {
12
+ env: { ...process.env, ...env },
13
+ });
14
+ let stdout = '';
15
+ let stderr = '';
16
+ child.stdout?.on('data', (chunk) => {
17
+ stdout += chunk.toString();
18
+ });
19
+ child.stderr?.on('data', (chunk) => {
20
+ const text = chunk.toString();
21
+ stderr += text;
22
+ if (streamStderr)
23
+ process.stderr.write(text);
24
+ });
25
+ child.on('close', (code) => {
26
+ res({ exitCode: code ?? 0, stdout, stderr });
27
+ });
28
+ });
29
+ }
30
+ export async function runPipeline(opts, progressWriter, cliPathOverride) {
31
+ const projectRoot = resolve(opts.project);
32
+ const outDir = resolve(opts.out);
33
+ const componentsPath = join(outDir, 'components.json');
34
+ const cliPath = cliPathOverride ?? findCliPath();
35
+ const db = openPipelineDb();
36
+ const { sessionId } = getOrCreateSession(db, undefined, undefined, {
37
+ command: 'import',
38
+ inputPath: projectRoot,
39
+ outDir,
40
+ });
41
+ progressWriter(`Experience Design System CLI — Pipeline Import`);
42
+ progressWriter(`Project: ${projectRoot}`);
43
+ progressWriter(`Output: ${outDir}`);
44
+ if (opts.spaceId)
45
+ progressWriter(`Space: ${opts.spaceId} (${opts.environmentId})`);
46
+ progressWriter(`Session: ${sessionId}`);
47
+ progressWriter('');
48
+ await mkdir(outDir, { recursive: true });
49
+ const steps = [];
50
+ let stepNum = 0;
51
+ // print components is an optional step; adjust total accordingly
52
+ const totalSteps = 4 + (opts.print ? 1 : 0);
53
+ function stepLabel(name) {
54
+ stepNum++;
55
+ return ` Step ${stepNum}/${totalSteps} ${name} `;
56
+ }
57
+ // ── Step 1: analyze extract ──────────────────────────────────────────────
58
+ const analyzeLabel = stepLabel('Statically analyzing project');
59
+ const analyzeSkipped = !opts.noCache && opts.skipAnalyze;
60
+ let extractSessionId = null;
61
+ if (analyzeSkipped) {
62
+ progressWriter(`${analyzeLabel}– skipped (--skip-analyze)`);
63
+ steps.push({
64
+ step: 'analyze extract',
65
+ status: 'skipped',
66
+ reason: '--skip-analyze',
67
+ });
68
+ // Try to find a prior extract session to hand to downstream steps
69
+ const prior = findLatestSessionForCommand(db, 'analyze extract');
70
+ extractSessionId = prior ?? null;
71
+ }
72
+ else {
73
+ const stepId = createStep(db, sessionId, 'analyze extract', {
74
+ project: projectRoot,
75
+ });
76
+ const t0 = Date.now();
77
+ const analyzeArgs = ['analyze', 'extract', '--project', projectRoot];
78
+ const r = await runStep(analyzeArgs, cliPath);
79
+ const durationMs = Date.now() - t0;
80
+ if (r.exitCode !== 0) {
81
+ if (r.stderr)
82
+ process.stderr.write(r.stderr);
83
+ updateStep(db, stepId, 'failed', {}, r.stderr);
84
+ progressWriter(`${analyzeLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
85
+ steps.push({
86
+ step: 'analyze extract',
87
+ status: 'failed',
88
+ durationMs,
89
+ error: r.stderr,
90
+ });
91
+ db.close();
92
+ return { session: sessionId, project: projectRoot, steps };
93
+ }
94
+ // Read session ID from DB — avoids dependence on subprocess stdout format.
95
+ extractSessionId = findLatestSessionForCommand(db, 'analyze extract') ?? null;
96
+ const componentMatch = /Extracted (\d+) component/.exec(r.stderr);
97
+ const componentCount = componentMatch ? Number(componentMatch[1]) : 0;
98
+ updateStep(db, stepId, 'complete', {
99
+ extractSessionId: extractSessionId ?? '',
100
+ });
101
+ progressWriter(`${analyzeLabel}✓ ${componentCount} component${componentCount === 1 ? '' : 's'} found (${(durationMs / 1000).toFixed(1)}s)`);
102
+ steps.push({
103
+ step: 'analyze extract',
104
+ status: 'complete',
105
+ durationMs,
106
+ detail: { components: componentCount },
107
+ });
108
+ }
109
+ // ── Step 2: analyze select ───────────────────────────────────────────────
110
+ const editLabel = stepLabel('Filtering components');
111
+ if (analyzeSkipped) {
112
+ progressWriter(`${editLabel}– skipped (--skip-analyze)`);
113
+ steps.push({
114
+ step: 'analyze select',
115
+ status: 'skipped',
116
+ reason: '--skip-analyze',
117
+ });
118
+ }
119
+ else if (extractSessionId) {
120
+ const editStepId = createStep(db, sessionId, 'analyze select', {
121
+ extractSession: extractSessionId,
122
+ });
123
+ const t0Edit = Date.now();
124
+ // Use agentic select when an agent is available and no manual select/deselect patterns are given.
125
+ const useAgentSelect = !opts.selectAll && (!opts.select || opts.select.length === 0) && (!opts.deselect || opts.deselect.length === 0);
126
+ let editArgs;
127
+ if (useAgentSelect) {
128
+ editArgs = ['analyze', 'select-agent', '--session', extractSessionId, '--agent', opts.agent];
129
+ if (opts.model)
130
+ editArgs.push('--model', opts.model);
131
+ }
132
+ else {
133
+ editArgs = ['analyze', 'select', '--session', extractSessionId];
134
+ if (opts.select && opts.select.length > 0) {
135
+ for (const p of opts.select)
136
+ editArgs.push('--select', p);
137
+ }
138
+ else if (opts.deselect && opts.deselect.length > 0) {
139
+ for (const p of opts.deselect)
140
+ editArgs.push('--deselect', p);
141
+ editArgs.push('--select-all'); // select-all with deselect patterns = select all except matches
142
+ }
143
+ else {
144
+ editArgs.push('--select-all');
145
+ }
146
+ }
147
+ const rEdit = await runStep(editArgs, cliPath, { FORCE_COLOR: '1' }, useAgentSelect);
148
+ const editDurationMs = Date.now() - t0Edit;
149
+ if (rEdit.exitCode !== 0) {
150
+ if (rEdit.stderr && !useAgentSelect)
151
+ process.stderr.write(rEdit.stderr);
152
+ updateStep(db, editStepId, 'failed', {}, rEdit.stderr);
153
+ progressWriter(`${editLabel}✗ failed (${(editDurationMs / 1000).toFixed(1)}s)`);
154
+ steps.push({
155
+ step: 'analyze select',
156
+ status: 'failed',
157
+ durationMs: editDurationMs,
158
+ error: rEdit.stderr,
159
+ });
160
+ db.close();
161
+ return { session: sessionId, project: projectRoot, steps };
162
+ }
163
+ const acceptedMatch = /Accepted: (\d+)/.exec(rEdit.stderr);
164
+ const acceptedCount = acceptedMatch ? Number(acceptedMatch[1]) : 0;
165
+ updateStep(db, editStepId, 'complete', {
166
+ extractSession: extractSessionId,
167
+ });
168
+ progressWriter(`${editLabel}✓ ${acceptedCount} accepted (${(editDurationMs / 1000).toFixed(1)}s)`);
169
+ steps.push({
170
+ step: 'analyze select',
171
+ status: 'complete',
172
+ durationMs: editDurationMs,
173
+ detail: { accepted: acceptedCount },
174
+ });
175
+ }
176
+ else {
177
+ progressWriter(`${editLabel}– skipped (no extract session)`);
178
+ steps.push({
179
+ step: 'analyze select',
180
+ status: 'skipped',
181
+ reason: 'no extract session',
182
+ });
183
+ }
184
+ // ── Step 3: generate components ──────────────────────────────────────────
185
+ if (opts.skipGenerate) {
186
+ const generateLabel = stepLabel('Categorizing component props');
187
+ progressWriter(`${generateLabel}– skipped (--skip-generate)`);
188
+ steps.push({
189
+ step: 'generate components',
190
+ status: 'skipped',
191
+ reason: '--skip-generate',
192
+ });
193
+ }
194
+ else {
195
+ const generateLabel = stepLabel('Categorizing component props');
196
+ const generateArgs = ['generate', 'components', '--agent', opts.agent];
197
+ if (opts.model)
198
+ generateArgs.push('--model', opts.model);
199
+ if (extractSessionId)
200
+ generateArgs.push('--session', extractSessionId);
201
+ if (opts.dryRun)
202
+ generateArgs.push('--dry-run');
203
+ if (opts.verbose)
204
+ generateArgs.push('--verbose');
205
+ const stepId = createStep(db, sessionId, 'generate components', {
206
+ extractSession: extractSessionId ?? '',
207
+ });
208
+ const t0 = Date.now();
209
+ const r = await runStep(generateArgs, cliPath, { FORCE_COLOR: '1' }, true);
210
+ const durationMs = Date.now() - t0;
211
+ if (r.exitCode !== 0) {
212
+ updateStep(db, stepId, 'failed', {}, r.stderr);
213
+ progressWriter(`${generateLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
214
+ steps.push({
215
+ step: 'generate components',
216
+ status: 'failed',
217
+ durationMs,
218
+ error: r.stderr,
219
+ });
220
+ db.close();
221
+ return { session: sessionId, project: projectRoot, steps };
222
+ }
223
+ if (opts.dryRun) {
224
+ updateStep(db, stepId, 'complete', { dryRun: 'true' });
225
+ progressWriter(`${generateLabel}✓ prompt printed (${(durationMs / 1000).toFixed(1)}s)`);
226
+ steps.push({
227
+ step: 'generate components',
228
+ status: 'complete',
229
+ durationMs,
230
+ });
231
+ db.close();
232
+ return { session: sessionId, project: projectRoot, steps };
233
+ }
234
+ updateStep(db, stepId, 'complete', {
235
+ extractSession: extractSessionId ?? '',
236
+ });
237
+ progressWriter(`${generateLabel}✓ components stored locally (${(durationMs / 1000).toFixed(1)}s)`);
238
+ steps.push({ step: 'generate components', status: 'complete', durationMs });
239
+ }
240
+ // ── Step 4 (optional): print components ─────────────────────────────────
241
+ if (opts.print) {
242
+ const printLabel = stepLabel('Writing components.json');
243
+ const printArgs = ['print', 'components', '--out', componentsPath];
244
+ if (extractSessionId)
245
+ printArgs.push('--session', extractSessionId);
246
+ const stepId = createStep(db, sessionId, 'print components', {
247
+ out: componentsPath,
248
+ });
249
+ const t0 = Date.now();
250
+ const r = await runStep(printArgs, cliPath);
251
+ const durationMs = Date.now() - t0;
252
+ if (r.exitCode !== 0) {
253
+ if (r.stderr)
254
+ process.stderr.write(r.stderr);
255
+ updateStep(db, stepId, 'failed', {}, r.stderr);
256
+ progressWriter(`${printLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
257
+ steps.push({
258
+ step: 'print components',
259
+ status: 'failed',
260
+ durationMs,
261
+ error: r.stderr,
262
+ });
263
+ db.close();
264
+ return { session: sessionId, project: projectRoot, steps };
265
+ }
266
+ updateStep(db, stepId, 'complete', { components: componentsPath });
267
+ progressWriter(`${printLabel}✓ components.json written (${(durationMs / 1000).toFixed(1)}s)`);
268
+ steps.push({ step: 'print components', status: 'complete', durationMs });
269
+ }
270
+ // ── Step 4/5: apply push ─────────────────────────────────────────────────
271
+ const applyLabelText = opts.spaceId && opts.environmentId
272
+ ? `Applying changes to Space: ${opts.spaceId} Environment: ${opts.environmentId}`
273
+ : 'Applying changes to Contentful';
274
+ if (opts.skipApply) {
275
+ const pushLabel = stepLabel(applyLabelText);
276
+ progressWriter(`${pushLabel}– skipped (--skip-apply)`);
277
+ steps.push({
278
+ step: 'apply push',
279
+ status: 'skipped',
280
+ reason: '--skip-apply',
281
+ });
282
+ }
283
+ else {
284
+ const pushLabel = stepLabel(applyLabelText);
285
+ if (!opts.spaceId || !opts.environmentId || !opts.cmaToken) {
286
+ process.stderr.write('Error: --space-id, --environment-id, and --cma-token are required for apply push. Use --skip-apply to skip.\n');
287
+ db.close();
288
+ process.exit(1);
289
+ }
290
+ const pushArgs = [
291
+ 'apply',
292
+ 'push',
293
+ '--space-id',
294
+ opts.spaceId,
295
+ '--environment-id',
296
+ opts.environmentId,
297
+ '--cma-token',
298
+ opts.cmaToken,
299
+ ];
300
+ // Components are stored under the extract session ID (generate command uses resolveSessionId
301
+ // which returns the passed --session value, i.e. the extract session). Pass that directly so
302
+ // apply push reads from the DB without needing a components.json file.
303
+ // Fall back to --components so the step fails with a clear error rather than a generic one.
304
+ if (extractSessionId) {
305
+ pushArgs.push('--session', extractSessionId);
306
+ }
307
+ else {
308
+ pushArgs.push('--components', componentsPath);
309
+ }
310
+ if (opts.tokens)
311
+ pushArgs.push('--tokens', opts.tokens);
312
+ if (opts.viewports)
313
+ pushArgs.push('--viewports', opts.viewports);
314
+ if (opts.host)
315
+ pushArgs.push('--host', opts.host);
316
+ if (opts.verbose)
317
+ pushArgs.push('--verbose');
318
+ pushArgs.push('--yes'); // always non-interactive in subprocess context
319
+ const pushStepId = createStep(db, sessionId, 'apply push', {
320
+ components: componentsPath,
321
+ });
322
+ const t0 = Date.now();
323
+ const r = await runStep(pushArgs, cliPath, { FORCE_COLOR: '1' }, true);
324
+ const durationMs = Date.now() - t0;
325
+ let pushResult = null;
326
+ try {
327
+ pushResult = JSON.parse(r.stdout);
328
+ }
329
+ catch {
330
+ // stdout wasn't JSON — fall back to regex parsing
331
+ }
332
+ const created = pushResult
333
+ ? (pushResult.componentTypes?.created ?? 0) + (pushResult.designTokens?.created ?? 0)
334
+ : Number(/(\d+) created/.exec(r.stdout + r.stderr)?.[1] ?? 0);
335
+ const updated = pushResult
336
+ ? (pushResult.componentTypes?.updated ?? 0) + (pushResult.designTokens?.updated ?? 0)
337
+ : Number(/(\d+) updated/.exec(r.stdout + r.stderr)?.[1] ?? 0);
338
+ const failed = pushResult
339
+ ? (pushResult.componentTypes?.failed ?? 0) + (pushResult.designTokens?.failed ?? 0)
340
+ : Number(/(\d+) failed/.exec(r.stdout + r.stderr)?.[1] ?? 0);
341
+ const totalPushed = created + updated + failed;
342
+ if (r.exitCode !== 0 && (totalPushed === 0 || failed === totalPushed)) {
343
+ // Total failure — nothing was pushed
344
+ updateStep(db, pushStepId, 'failed', {}, r.stderr);
345
+ progressWriter(`${pushLabel}✗ failed (${(durationMs / 1000).toFixed(1)}s)`);
346
+ steps.push({
347
+ step: 'apply push',
348
+ status: 'failed',
349
+ durationMs,
350
+ error: r.stderr,
351
+ });
352
+ db.close();
353
+ return { session: sessionId, project: projectRoot, steps };
354
+ }
355
+ const stepStatus = failed > 0 ? 'failed' : 'complete';
356
+ updateStep(db, pushStepId, stepStatus === 'complete' ? 'complete' : 'failed', { components: componentsPath });
357
+ const statusIcon = failed > 0 ? '⚠' : '✓';
358
+ progressWriter(`${pushLabel}${statusIcon} ${created} created, ${updated} updated, ${failed} failed (${(durationMs / 1000).toFixed(1)}s)`);
359
+ steps.push({
360
+ step: 'apply push',
361
+ status: stepStatus,
362
+ durationMs,
363
+ detail: { created, updated, failed },
364
+ });
365
+ }
366
+ progressWriter('');
367
+ progressWriter(`Pipeline complete. Session: ${sessionId}`);
368
+ if (opts.spaceId && opts.environmentId && !opts.skipApply) {
369
+ progressWriter('');
370
+ progressWriter(`View your design system: https://app.contentful.com/spaces/${opts.spaceId}/environments/${opts.environmentId}/exo/components`);
371
+ }
372
+ db.close();
373
+ return { session: sessionId, project: projectRoot, steps };
374
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Expands a leading `~` to the user's home directory.
3
+ * Exported separately so it can be unit-tested and reused.
4
+ */
5
+ export declare function expandTilde(input: string): string;
6
+ /**
7
+ * Normalizes a user-entered path to an absolute path, handling all common
8
+ * terminal path styles:
9
+ * - surrounding quotes ("~/path" or '~/path') → stripped
10
+ * - tilde prefix (~/projects/mylib) → expanded to $HOME
11
+ * - relative paths (../mylib, ./src, mylib) → resolved against CWD
12
+ * - absolute paths (/Users/ryun/projects/mylib) → passed through
13
+ * - trailing slashes, repeated slashes → normalized by path.resolve
14
+ */
15
+ export declare function normalizePath(input: string): string;
@@ -0,0 +1,30 @@
1
+ import { resolve } from 'node:path';
2
+ import { homedir } from 'node:os';
3
+ /**
4
+ * Expands a leading `~` to the user's home directory.
5
+ * Exported separately so it can be unit-tested and reused.
6
+ */
7
+ export function expandTilde(input) {
8
+ if (input === '~' || input.startsWith('~/') || input.startsWith('~\\')) {
9
+ return homedir() + input.slice(1);
10
+ }
11
+ return input;
12
+ }
13
+ /**
14
+ * Normalizes a user-entered path to an absolute path, handling all common
15
+ * terminal path styles:
16
+ * - surrounding quotes ("~/path" or '~/path') → stripped
17
+ * - tilde prefix (~/projects/mylib) → expanded to $HOME
18
+ * - relative paths (../mylib, ./src, mylib) → resolved against CWD
19
+ * - absolute paths (/Users/ryun/projects/mylib) → passed through
20
+ * - trailing slashes, repeated slashes → normalized by path.resolve
21
+ */
22
+ export function normalizePath(input) {
23
+ let p = input.trim();
24
+ // strip surrounding single or double quotes
25
+ if (p.length >= 2 && ((p.startsWith('"') && p.endsWith('"')) || (p.startsWith("'") && p.endsWith("'")))) {
26
+ p = p.slice(1, -1);
27
+ }
28
+ p = expandTilde(p);
29
+ return resolve(p);
30
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ export type WizardAppProps = {
3
+ initialSpaceId?: string;
4
+ initialEnvironmentId?: string;
5
+ initialCmaToken?: string;
6
+ initialAgent?: string;
7
+ initialProjectPath?: string;
8
+ host?: string;
9
+ };
10
+ export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialAgent, initialProjectPath, host, }?: WizardAppProps): React.ReactElement;