@contentful/experience-design-system-cli 2.18.1-dev-build-0f85b83.0 → 2.18.1-dev-build-83cbc0a.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.
Files changed (59) hide show
  1. package/README.md +1 -2
  2. package/dist/package.json +3 -3
  3. package/dist/src/analyze/command.js +3 -3
  4. package/dist/src/analyze/composition/parse-map-edges.d.ts +1 -1
  5. package/dist/src/analyze/select/tui/App.js +1 -4
  6. package/dist/src/analyze/select-agent/command.js +6 -5
  7. package/dist/src/apply/api-client.d.ts +2 -5
  8. package/dist/src/apply/api-client.js +4 -7
  9. package/dist/src/apply/command.js +10 -27
  10. package/dist/src/apply/preview-utils.d.ts +1 -5
  11. package/dist/src/apply/preview-utils.js +1 -5
  12. package/dist/src/apply/tui/ServerApplyView.d.ts +3 -6
  13. package/dist/src/apply/tui/ServerApplyView.js +6 -15
  14. package/dist/src/apply/tui/ServerPreviewView.d.ts +1 -2
  15. package/dist/src/apply/tui/ServerPreviewView.js +3 -3
  16. package/dist/src/generate/command.d.ts +0 -6
  17. package/dist/src/generate/command.js +7 -17
  18. package/dist/src/import/command.js +1 -6
  19. package/dist/src/import/orchestrator.d.ts +0 -1
  20. package/dist/src/import/orchestrator.js +0 -2
  21. package/dist/src/import/tui/WizardApp.d.ts +1 -2
  22. package/dist/src/import/tui/WizardApp.js +9 -9
  23. package/dist/src/import/tui/final-review-host.d.ts +1 -2
  24. package/dist/src/import/tui/final-review-host.js +2 -2
  25. package/dist/src/import/tui/runLivePreview.d.ts +0 -3
  26. package/dist/src/import/tui/runLivePreview.js +1 -4
  27. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +1 -2
  28. package/dist/src/import/tui/steps/GenerateReviewStep.js +3 -3
  29. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +2 -6
  30. package/dist/src/import/tui/steps/WizardPreviewStep.js +3 -12
  31. package/dist/src/import/tui/useFinalizePreview.d.ts +0 -2
  32. package/dist/src/import/tui/useFinalizePreview.js +0 -0
  33. package/dist/src/import/tui/useLivePreview.d.ts +0 -2
  34. package/dist/src/import/tui/useLivePreview.js +0 -1
  35. package/dist/src/lib/agent-model-options.js +1 -1
  36. package/dist/src/lib/command-options.d.ts +0 -1
  37. package/dist/src/lib/command-options.js +0 -3
  38. package/dist/src/runs/modify-launcher.d.ts +0 -2
  39. package/dist/src/runs/modify-launcher.js +0 -2
  40. package/dist/src/runs/push-helpers.d.ts +0 -1
  41. package/dist/src/runs/push-helpers.js +0 -3
  42. package/dist/src/runs/replay-helpers.d.ts +0 -4
  43. package/dist/src/runs/replay-helpers.js +0 -3
  44. package/dist/src/session/cache-keys.d.ts +1 -1
  45. package/dist/src/session/cache-keys.js +1 -1
  46. package/dist/src/session/db.d.ts +1 -1
  47. package/dist/src/session/db.js +1 -1
  48. package/package.json +6 -6
  49. package/dist/src/generate/agent-runner.d.ts +0 -125
  50. package/dist/src/generate/agent-runner.js +0 -427
  51. package/dist/src/generate/progress.d.ts +0 -9
  52. package/dist/src/generate/progress.js +0 -11
  53. package/dist/src/generate/prompt-builder.d.ts +0 -26
  54. package/dist/src/generate/prompt-builder.js +0 -213
  55. package/dist/src/lib/agent-names.d.ts +0 -4
  56. package/dist/src/lib/agent-names.js +0 -5
  57. package/skills/generate-components.md +0 -427
  58. package/skills/generate-tokens.md +0 -194
  59. package/skills/select-components.md +0 -224
package/README.md CHANGED
@@ -214,7 +214,6 @@ Pass `--select-prompt-path <path>` and/or `--generate-prompt-path <path>` to swa
214
214
  | `--host <url>` | `https://api.contentful.com` | Override API base URL |
215
215
  | `--on-conflict <mode>` | _(prompt via `<SaveConflictGate>`)_ | Headless conflict resolution when a file already exists at the save path: `overwrite`, `skip`, or `fail`. Bypasses the wizard's interactive save-conflict gate. Mutex with `--no-save`. |
216
216
  | `--print-prompt` | — | Print the generate prompt to stdout and exit. Replaces the prompt-print semantics of `--dry-run`. |
217
- | `--allow-deletions` | off (non-destructive) | Allow the push to delete remote ComponentTypes/DesignTokens missing from the manifest. Default skips them instead of deleting. Without this flag, preview responses suppress the removed-entity list and return a count instead; interactive confirm screens show an opt-out toggle (never opt-in) only when the flag is passed. Forwarded to headless subprocess pushes and `--push-from-run`. |
218
217
  | `--dry-run` | _(deprecated)_ | Deprecated alias for `--print-prompt`. Emits a stderr deprecation notice; prompt-print semantics will be removed in a future release. |
219
218
 
220
219
  ### Run-picker at wizard start
@@ -419,7 +418,7 @@ experiences apply select --space-id <id> --environment-id <env> --session <id>
419
418
  experiences apply push --space-id <id> --environment-id <env> --session <id> [--yes]
420
419
  ```
421
420
 
422
- Shared flags: `--components`, `--tokens`, `--session`, `--space-id`, `--environment-id`, `--cma-token`, `--host`, `--viewports`. `apply preview` adds `--include-unchanged`. `apply select` adds `--select-all`, `--select`, `--deselect`, `--force`, `--allow-deletions`. `apply push` adds `--yes`, `--verbose`, `--force`, `--dry-run`, `--allow-deletions`. By default, remote ComponentTypes and DesignTokens missing from the pushed manifest are skipped, not deleted; pass `--allow-deletions` to restore the prior delete behavior.
421
+ Shared flags: `--components`, `--tokens`, `--session`, `--space-id`, `--environment-id`, `--cma-token`, `--host`, `--viewports`. `apply preview` adds `--include-unchanged`. `apply select` adds `--select-all`, `--select`, `--deselect`, `--force`. `apply push` adds `--yes`, `--verbose`, `--force`, `--dry-run`.
423
422
 
424
423
  Design tokens are written first (component types may reference token kinds). Each entity write is recorded in the session database atomically — interrupted pushes resume from where they left off.
425
424
 
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@contentful/experience-design-system-cli",
3
- "version": "2.18.1-dev-build-0f85b83.0",
3
+ "version": "2.18.1-dev-build-83cbc0a.0",
4
4
  "description": "Contentful Experiences design system import CLI",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -25,7 +25,6 @@
25
25
  "files": [
26
26
  "bin/",
27
27
  "dist/",
28
- "skills/",
29
28
  "prompts/"
30
29
  ],
31
30
  "scripts": {
@@ -40,6 +39,7 @@
40
39
  "dependencies": {
41
40
  "@contentful/experience-design-system-client": "workspace:*",
42
41
  "@contentful/experience-design-system-extraction": "workspace:*",
42
+ "@contentful/experience-design-system-generation": "workspace:*",
43
43
  "@contentful/experience-design-system-types": "workspace:*",
44
44
  "commander": "^13.1.0",
45
45
  "ink": "^4.4.1",
@@ -48,7 +48,7 @@
48
48
  "react-dom": "^18.3.1"
49
49
  },
50
50
  "devDependencies": {
51
- "@tsconfig/node24": "^24.0.3",
51
+ "@tsconfig/node24": "^24.0.4",
52
52
  "@types/node": "^24.0.3",
53
53
  "@types/react": "^18.3.24",
54
54
  "eslint": "^9.39.2",
@@ -19,11 +19,11 @@ import { buildCompositionInputHash } from './composition/composition-cache-key.j
19
19
  import { runParserInSandbox } from './composition/agent-parser/sandbox.js';
20
20
  import { resolveViaAgentParser } from './composition/agent-parser/resolve-via-parser.js';
21
21
  import { parsePromptOverrides, resolvePromptOverride } from '../lib/prompt-overrides.js';
22
- import { DEFAULT_AGENT_NAME, isAgentName } from '../lib/agent-names.js';
23
- import { runAgent } from '../generate/agent-runner.js';
22
+ import { DEFAULT_AGENT_NAME, isAgentName, runAgent, } from '@contentful/experience-design-system-generation';
24
23
  import { readExperiencesCredentials } from '../credentials-store.js';
25
24
  import { buildAnalyzeViewRows, partitionGlobalWarnings } from './build-analyze-view-rows.js';
26
25
  import { getInteractiveTerminalSupport } from '../lib/terminal-capabilities.js';
26
+ import { getDebugLogger } from '../lib/debug-logger.js';
27
27
  const SCANNED_FILE_EXTENSIONS = new Set(['.astro', '.js', '.jsx', '.svelte', '.ts', '.tsx', '.vue']);
28
28
  const IGNORED_DIRECTORY_NAMES = new Set([
29
29
  '.git',
@@ -351,9 +351,9 @@ export function registerAnalyzeCommand(program) {
351
351
  const res = await runAgent({
352
352
  agent: resolverAgent,
353
353
  prompt,
354
- interactive: false,
355
354
  timeoutMs: 120_000,
356
355
  promptViaStdin: true,
356
+ onDebugEvent: (name, payload) => getDebugLogger().event('agent', name, payload),
357
357
  });
358
358
  lastAgentExitCode = res.exitCode;
359
359
  if (res.exitCode !== 0 && res.stderr.trim()) {
@@ -2,7 +2,7 @@ import type { CompositionEdge } from './interchange-schema.js';
2
2
  /**
3
3
  * Lenient JSONL parser for the composition-mapping agent's tool-call output
4
4
  * (spec T4). Mirrors `parseSelectToolCallLines` / `parseToolCallLines` in
5
- * `src/generate/agent-runner.ts`: one JSON object per line, each with a `tool`
5
+ * `@contentful/experience-design-system-generation`'s `agent-runner.ts`: one JSON object per line, each with a `tool`
6
6
  * field from a fixed allowlist. A line that fails JSON.parse or validation is
7
7
  * DROPPED into `warnings[]` (with a reason) and parsing CONTINUES — no retry,
8
8
  * no throw. Non-`{` prose and blank lines are skipped silently.
@@ -80,10 +80,7 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
80
80
  if (!manifest.componentsManifest)
81
81
  manifest.componentsManifest = {};
82
82
  const client = new ImportApiClient({ cmaToken, spaceId, environmentId });
83
- // This preview is display-only (component-picker annotations); it never
84
- // triggers an apply, so always request the full diff to keep the
85
- // existing "removed" annotation working.
86
- const preview = await client.previewImport(manifest, true);
83
+ const preview = await client.previewImport(manifest);
87
84
  const annotations = {};
88
85
  for (const item of preview.components.new) {
89
86
  const name = item.name ?? '';
@@ -3,19 +3,21 @@ import { openPipelineDb, loadRawComponents, loadScannedFiles, createStep, update
3
3
  import { hashPromptForSkill } from '../../session/cache-keys.js';
4
4
  import { appendReviewEvent, getRefineArtifactsRoot, ensureRefineSession, getRefineSessionPaths, saveReviewState, } from '../select/persistence.js';
5
5
  import { loadReviewInput } from '../select/parser.js';
6
- import { buildPrompt } from '../../generate/prompt-builder.js';
7
- import { formatCustomPromptBanner } from '../../generate/command.js';
8
- import { AGENT_NAMES, isAgentName, parseSelectToolCallLines, runAgent } from '../../generate/agent-runner.js';
6
+ import { AGENT_NAMES, buildPrompt, createLocalCliAgentInvoker, formatCustomPromptBanner, isAgentName, parseSelectToolCallLines, } from '@contentful/experience-design-system-generation';
9
7
  import { access } from 'node:fs/promises';
10
8
  import { readExperiencesCredentials } from '../../credentials-store.js';
11
9
  import { OutputFormatter, c } from '../../output/format.js';
12
10
  import { buildRepoContextIndex, buildSelectionContext } from './context-builder.js';
13
11
  import { runShowRationale } from './show-rationale.js';
14
12
  import { isAbsolute, resolve } from 'node:path';
13
+ import { getDebugLogger } from '../../lib/debug-logger.js';
15
14
  import { validateExtractedComponents, shouldExcludeDueToValidation, formatExclusionWarning, } from '@contentful/experience-design-system-extraction';
16
15
  const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
17
16
  export const DEFAULT_CONCURRENCY = 10;
18
17
  export const DEFAULT_BATCH_SIZE = 5;
18
+ const invoker = createLocalCliAgentInvoker({
19
+ onDebugEvent: (name, payload) => getDebugLogger().event('agent', name, payload),
20
+ });
19
21
  function resolveBatchSize() {
20
22
  const raw = process.env.EDS_SELECT_BATCH_SIZE;
21
23
  if (!raw)
@@ -109,11 +111,10 @@ async function selectBatch(agent, model, batch, total, verbose, skillPathOverrid
109
111
  const formatter = new OutputFormatter(verbose, (s) => {
110
112
  outputBuf += s;
111
113
  });
112
- const result = await runAgent({
114
+ const result = await invoker.invoke({
113
115
  agent,
114
116
  model,
115
117
  prompt,
116
- interactive: false,
117
118
  timeoutMs: DEFAULT_TIMEOUT_MS,
118
119
  onOutput: (chunk) => formatter.push(chunk),
119
120
  });
@@ -50,11 +50,8 @@ export declare class ImportApiClient {
50
50
  private headers;
51
51
  private requestWithRetry;
52
52
  validateToken(): Promise<void>;
53
- previewImport(manifest: ManifestPayload, allowDeletions?: boolean): Promise<ServerPreviewResponse>;
54
- applyImport(manifest: ManifestPayload, options: {
55
- acknowledgeBreakingChanges: boolean;
56
- allowDeletions?: boolean;
57
- }): Promise<ApplyOperationResponse>;
53
+ previewImport(manifest: ManifestPayload): Promise<ServerPreviewResponse>;
54
+ applyImport(manifest: ManifestPayload, acknowledgeBreakingChanges: boolean): Promise<ApplyOperationResponse>;
58
55
  pollOperation(operationId: string, opts?: {
59
56
  intervalMs?: number;
60
57
  maxIntervalMs?: number;
@@ -268,20 +268,19 @@ export class ImportApiClient {
268
268
  throw new ApiError(`unexpected error validating token: ${res.status}`, res.status, await res.text());
269
269
  }
270
270
  }
271
- async previewImport(manifest, allowDeletions = false) {
271
+ async previewImport(manifest) {
272
272
  const debug = getDebugLogger();
273
273
  const startedAt = Date.now();
274
274
  debug.event('apply', 'preview.request', {
275
275
  url: `${this.base()}/design_systems/imports/preview`,
276
276
  componentCount: manifest.components?.length ?? 0,
277
277
  tokenCount: manifest.designTokens?.length ?? 0,
278
- allowDeletions,
279
278
  });
280
279
  const result = await this.requestWithRetry('preview', PREVIEW_ERROR_PREFIX, () => designSystemImportSourcelessPreview({
281
280
  baseUrl: this.host,
282
281
  headers: this.headers(),
283
282
  path: { spaceId: this.spaceId, environmentId: this.environmentId },
284
- body: { ...manifest, allowDeletions },
283
+ body: manifest,
285
284
  parseAs: 'json',
286
285
  }));
287
286
  if (!result.response.ok) {
@@ -297,14 +296,12 @@ export class ImportApiClient {
297
296
  debug.event('apply', 'preview.ok', { status: result.response.status, durationMs: Date.now() - startedAt });
298
297
  return sanitizePreviewResponse(parsed);
299
298
  }
300
- async applyImport(manifest, options) {
301
- const { acknowledgeBreakingChanges, allowDeletions = false } = options;
299
+ async applyImport(manifest, acknowledgeBreakingChanges) {
302
300
  const debug = getDebugLogger();
303
301
  const startedAt = Date.now();
304
302
  debug.event('apply', 'apply.request', {
305
303
  url: `${this.base()}/design_systems/imports/apply`,
306
304
  acknowledgeBreakingChanges,
307
- allowDeletions,
308
305
  });
309
306
  let result;
310
307
  try {
@@ -312,7 +309,7 @@ export class ImportApiClient {
312
309
  baseUrl: this.host,
313
310
  headers: this.headers(),
314
311
  path: { spaceId: this.spaceId, environmentId: this.environmentId },
315
- body: { ...manifest, acknowledgeBreakingChanges, allowDeletions },
312
+ body: { ...manifest, acknowledgeBreakingChanges },
316
313
  parseAs: 'json',
317
314
  });
318
315
  }
@@ -12,7 +12,7 @@ import { ServerPreviewApp, ServerPreviewConfirm, ServerApplyProgress, ServerAppl
12
12
  import { SelectView, makeSelectKey } from './tui/SelectView.js';
13
13
  import { buildPostPushUrl } from '../lib/contentful-urls.js';
14
14
  import { resolveCompositionMode } from '../lib/composition-mode.js';
15
- import { addAllowDeletionsOption, addArtifactInputOptions, addCompositionOptions, addContentfulTargetOptions, addSelectionOptions, } from '../lib/command-options.js';
15
+ import { addArtifactInputOptions, addCompositionOptions, addContentfulTargetOptions, addSelectionOptions, } from '../lib/command-options.js';
16
16
  import { stripAllowedComponents } from '../import/strip-allowed-components.js';
17
17
  import { readExperiencesCredentials } from '../credentials-store.js';
18
18
  import { getInteractiveTerminalSupport, requireInteractiveTerminal } from '../lib/terminal-capabilities.js';
@@ -457,7 +457,6 @@ export function registerApplyCommand(program) {
457
457
  preview,
458
458
  spaceId,
459
459
  environmentId,
460
- allowDeletions: false,
461
460
  }));
462
461
  await waitUntilExit();
463
462
  }
@@ -470,7 +469,6 @@ export function registerApplyCommand(program) {
470
469
  addArtifactInputOptions(pushCmd);
471
470
  addContentfulTargetOptions(pushCmd);
472
471
  addCompositionOptions(pushCmd);
473
- addAllowDeletionsOption(pushCmd);
474
472
  pushCmd
475
473
  .option('--yes', 'Skip interactive confirmation')
476
474
  .option('--verbose', 'Show all entity progress including skipped/unchanged')
@@ -504,7 +502,7 @@ export function registerApplyCommand(program) {
504
502
  const manifest = buildManifest(components, tokens);
505
503
  let preview;
506
504
  try {
507
- preview = await client.previewImport(manifest, opts.allowDeletions === true);
505
+ preview = await client.previewImport(manifest);
508
506
  }
509
507
  catch (e) {
510
508
  if (e instanceof ApiError)
@@ -519,7 +517,6 @@ export function registerApplyCommand(program) {
519
517
  preview,
520
518
  spaceId,
521
519
  environmentId,
522
- allowDeletions: opts.allowDeletions === true,
523
520
  }));
524
521
  await waitUntilExit();
525
522
  }
@@ -550,10 +547,7 @@ export function registerApplyCommand(program) {
550
547
  }
551
548
  let operation;
552
549
  try {
553
- operation = await client.applyImport(manifest, {
554
- acknowledgeBreakingChanges: breakingWithImpact || opts.force === true,
555
- allowDeletions: opts.allowDeletions === true,
556
- });
550
+ operation = await client.applyImport(manifest, breakingWithImpact || opts.force === true);
557
551
  }
558
552
  catch (e) {
559
553
  if (e instanceof ApiError)
@@ -575,7 +569,7 @@ export function registerApplyCommand(program) {
575
569
  return;
576
570
  }
577
571
  await new Promise((resolvePromise) => {
578
- const runApply = async (acknowledge, applyDeletions) => {
572
+ const runApply = async (acknowledge) => {
579
573
  instance.rerender(createElement(ServerApplyProgress, {
580
574
  spaceId,
581
575
  environmentId,
@@ -583,10 +577,7 @@ export function registerApplyCommand(program) {
583
577
  }));
584
578
  let operation;
585
579
  try {
586
- operation = await client.applyImport(manifest, {
587
- acknowledgeBreakingChanges: acknowledge,
588
- allowDeletions: applyDeletions,
589
- });
580
+ operation = await client.applyImport(manifest, acknowledge);
590
581
  }
591
582
  catch (e) {
592
583
  if (e instanceof ApiError) {
@@ -634,9 +625,8 @@ export function registerApplyCommand(program) {
634
625
  spaceId,
635
626
  environmentId,
636
627
  breakingWithImpact,
637
- allowDeletions: opts.allowDeletions === true,
638
- onConfirm: (acknowledge, applyDeletions) => {
639
- void runApply(acknowledge, applyDeletions);
628
+ onConfirm: (acknowledge) => {
629
+ void runApply(acknowledge);
640
630
  },
641
631
  onCancel: () => {
642
632
  process.exit(0);
@@ -650,7 +640,6 @@ export function registerApplyCommand(program) {
650
640
  addContentfulTargetOptions(selectCmd);
651
641
  addCompositionOptions(selectCmd);
652
642
  addSelectionOptions(selectCmd);
653
- addAllowDeletionsOption(selectCmd);
654
643
  selectCmd.option('--force', 'Skip confirmation for breaking changes').action(async (opts) => {
655
644
  const nonInteractive = opts.selectAll || (opts.select ?? []).length > 0 || (opts.deselect ?? []).length > 0;
656
645
  if (!nonInteractive) {
@@ -680,7 +669,7 @@ export function registerApplyCommand(program) {
680
669
  const fullManifest = buildManifest(components, tokens);
681
670
  let preview;
682
671
  try {
683
- preview = await client.previewImport(fullManifest, opts.allowDeletions === true);
672
+ preview = await client.previewImport(fullManifest);
684
673
  }
685
674
  catch (e) {
686
675
  if (e instanceof ApiError)
@@ -718,10 +707,7 @@ export function registerApplyCommand(program) {
718
707
  }
719
708
  let operation;
720
709
  try {
721
- operation = await client.applyImport(filteredManifest, {
722
- acknowledgeBreakingChanges: hasBreaking || opts.force === true,
723
- allowDeletions: opts.allowDeletions === true,
724
- });
710
+ operation = await client.applyImport(filteredManifest, hasBreaking || opts.force === true);
725
711
  }
726
712
  catch (e) {
727
713
  if (e instanceof ApiError)
@@ -763,10 +749,7 @@ export function registerApplyCommand(program) {
763
749
  }));
764
750
  let operation;
765
751
  try {
766
- operation = await client.applyImport(filteredManifest, {
767
- acknowledgeBreakingChanges: hasBreaking,
768
- allowDeletions: opts.allowDeletions === true,
769
- });
752
+ operation = await client.applyImport(filteredManifest, hasBreaking);
770
753
  }
771
754
  catch (e) {
772
755
  if (e instanceof ApiError) {
@@ -7,10 +7,6 @@ import type { ServerPreviewResponse } from '@contentful/experience-design-system
7
7
  * would be a pure no-op (INTEG-4411 refined guard).
8
8
  *
9
9
  * A push that produces ANY entry in ANY bucket — including a rejection that
10
- * removes a server-side component — is NOT empty. Note the server hides
11
- * removals entirely (empty removed[]) unless the preview was fetched with
12
- * allowDeletions: true, so "empty" here also correctly describes a preview
13
- * with pending-but-hidden deletions: pushing without --allow-deletions is a
14
- * genuine no-op in that case too, since the apply gate hides them the same way.
10
+ * removes a server-side component — is NOT empty.
15
11
  */
16
12
  export declare function isEmptyPreview(preview: ServerPreviewResponse): boolean;
@@ -6,11 +6,7 @@
6
6
  * would be a pure no-op (INTEG-4411 refined guard).
7
7
  *
8
8
  * A push that produces ANY entry in ANY bucket — including a rejection that
9
- * removes a server-side component — is NOT empty. Note the server hides
10
- * removals entirely (empty removed[]) unless the preview was fetched with
11
- * allowDeletions: true, so "empty" here also correctly describes a preview
12
- * with pending-but-hidden deletions: pushing without --allow-deletions is a
13
- * genuine no-op in that case too, since the apply gate hides them the same way.
9
+ * removes a server-side component — is NOT empty.
14
10
  */
15
11
  export function isEmptyPreview(preview) {
16
12
  const { components, tokens, taxonomies } = preview;
@@ -5,19 +5,16 @@ interface ServerPreviewConfirmProps {
5
5
  spaceId: string;
6
6
  environmentId: string;
7
7
  breakingWithImpact: boolean;
8
- /** The value the preview was actually fetched with. */
9
- allowDeletions: boolean;
10
- onConfirm: (acknowledge: boolean, allowDeletions: boolean) => void;
8
+ onConfirm: (acknowledge: boolean) => void;
11
9
  onCancel: () => void;
12
10
  }
13
- export declare function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions: fetchedAllowDeletions, onConfirm, onCancel, }: ServerPreviewConfirmProps): React.ReactElement;
11
+ export declare function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, onConfirm, onCancel, }: ServerPreviewConfirmProps): React.ReactElement;
14
12
  interface ServerPreviewAppProps {
15
13
  preview: ServerPreviewResponse;
16
14
  spaceId: string;
17
15
  environmentId: string;
18
- allowDeletions: boolean;
19
16
  }
20
- export declare function ServerPreviewApp({ preview, spaceId, environmentId, allowDeletions, }: ServerPreviewAppProps): React.ReactElement;
17
+ export declare function ServerPreviewApp({ preview, spaceId, environmentId }: ServerPreviewAppProps): React.ReactElement;
21
18
  interface ServerApplyProgressProps {
22
19
  spaceId: string;
23
20
  environmentId: string;
@@ -1,32 +1,23 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { useState } from 'react';
3
2
  import { Box, Text, useInput } from 'ink';
4
3
  import { ServerPreviewView } from './ServerPreviewView.js';
5
4
  import { buildPostPushUrl } from '../../lib/contentful-urls.js';
6
5
  import { formatEdsiError } from '../../lib/error-parser.js';
7
- export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, allowDeletions: fetchedAllowDeletions, onConfirm, onCancel, }) {
8
- const [allowDeletions, setAllowDeletions] = useState(fetchedAllowDeletions);
9
- const removedCount = preview.components.removed.length + preview.tokens.removed.length;
6
+ export function ServerPreviewConfirm({ preview, spaceId, environmentId, breakingWithImpact, onConfirm, onCancel, }) {
10
7
  useInput((input, key) => {
11
- if (key.return) {
12
- onConfirm(breakingWithImpact, allowDeletions);
13
- return;
14
- }
15
- if ((input === 'x' || input === 'X') && fetchedAllowDeletions && removedCount > 0) {
16
- setAllowDeletions((prev) => !prev);
17
- return;
18
- }
8
+ if (key.return)
9
+ onConfirm(breakingWithImpact);
19
10
  if (key.escape || input === 'q')
20
11
  onCancel();
21
12
  });
22
- return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId, allowDeletions: allowDeletions }), _jsxs(Box, { paddingX: 2, flexDirection: "column", children: [breakingWithImpact && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply."] })), allowDeletions && removedCount > 0 && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 ", removedCount, " missing ", removedCount === 1 ? 'entity' : 'entities', " will be permanently deleted. Press Enter to confirm."] })), fetchedAllowDeletions && removedCount > 0 && (_jsxs(Text, { dimColor: true, children: [' ', "[x] ", allowDeletions ? '[✓]' : '[ ]', " Also delete ", removedCount, " ", removedCount === 1 ? 'entity' : 'entities'] })), _jsxs(Text, { children: [' ', "Press ", _jsx(Text, { bold: true, children: "Enter" }), " to apply, ", _jsx(Text, { bold: true, children: "Esc" }), " to cancel"] })] })] }));
13
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId }), _jsxs(Box, { paddingX: 2, flexDirection: "column", children: [breakingWithImpact && (_jsxs(Text, { color: "red", bold: true, children: [' ', "\u26A0 Breaking changes will affect downstream entities. Press Enter to acknowledge and apply."] })), _jsxs(Text, { children: [' ', "Press ", _jsx(Text, { bold: true, children: "Enter" }), " to apply, ", _jsx(Text, { bold: true, children: "Esc" }), " to cancel"] })] })] }));
23
14
  }
24
- export function ServerPreviewApp({ preview, spaceId, environmentId, allowDeletions, }) {
15
+ export function ServerPreviewApp({ preview, spaceId, environmentId }) {
25
16
  useInput((input, key) => {
26
17
  if (key.escape || input === 'q')
27
18
  process.exit(0);
28
19
  });
29
- return (_jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId, allowDeletions: allowDeletions }));
20
+ return _jsx(ServerPreviewView, { preview: preview, spaceId: spaceId, environmentId: environmentId });
30
21
  }
31
22
  export function ServerApplyProgress({ spaceId, environmentId, status, operationId, error, }) {
32
23
  useInput((_input, key) => {
@@ -4,7 +4,6 @@ interface ServerPreviewViewProps {
4
4
  preview: ServerPreviewResponse;
5
5
  spaceId: string;
6
6
  environmentId: string;
7
- allowDeletions: boolean;
8
7
  }
9
- export declare function ServerPreviewView({ preview, spaceId, environmentId, allowDeletions, }: ServerPreviewViewProps): React.ReactElement;
8
+ export declare function ServerPreviewView({ preview, spaceId, environmentId }: ServerPreviewViewProps): React.ReactElement;
10
9
  export {};
@@ -13,11 +13,11 @@ function DraftWarning({ hasDraft }) {
13
13
  return null;
14
14
  return _jsx(Text, { color: "yellow", children: " \u26A1 has pending draft changes" });
15
15
  }
16
- export function ServerPreviewView({ preview, spaceId, environmentId, allowDeletions, }) {
16
+ export function ServerPreviewView({ preview, spaceId, environmentId }) {
17
17
  const { components, tokens } = preview;
18
18
  const totalComponents = components.new.length + components.changed.length + components.unchanged.length + components.removed.length;
19
19
  const totalTokens = tokens.new.length + tokens.changed.length + tokens.unchanged.length + tokens.removed.length;
20
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Preview \u2014 ", environmentId, " @ ", spaceId] }), _jsx(Text, { children: " " }), totalComponents > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Component Types (", totalComponents, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", components.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", components.changed.length, " to update"] }), _jsxs(Text, { color: allowDeletions ? 'red' : 'yellow', children: [' ', allowDeletions ? '✗' : '⊘', " ", components.removed.length, " to ", allowDeletions ? 'delete' : 'skip'] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", components.unchanged.length, " unchanged"] }), components.changed.map((item, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: item.hasPendingDraftChanges })] }), _jsx(BreakingBadge, { item: item })] }, i))), _jsx(Text, { children: " " })] })), totalTokens > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Design Tokens (", totalTokens, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", tokens.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", tokens.changed.length, " to update"] }), _jsxs(Text, { color: allowDeletions ? 'red' : 'yellow', children: [' ', allowDeletions ? '✗' : '⊘', " ", tokens.removed.length, " to ", allowDeletions ? 'delete' : 'skip'] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", tokens.unchanged.length, " unchanged"] }), tokens.changed
20
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, children: [_jsxs(Text, { bold: true, children: ["Preview \u2014 ", environmentId, " @ ", spaceId] }), _jsx(Text, { children: " " }), totalComponents > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Component Types (", totalComponents, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", components.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", components.changed.length, " to update"] }), _jsxs(Text, { color: "red", children: [" \u2717 ", components.removed.length, " to remove"] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", components.unchanged.length, " unchanged"] }), components.changed.map((item, i) => (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: item.hasPendingDraftChanges })] }), _jsx(BreakingBadge, { item: item })] }, i))), _jsx(Text, { children: " " })] })), totalTokens > 0 && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [" Design Tokens (", totalTokens, " total)"] }), _jsxs(Text, { color: "green", children: [" \u2746 ", tokens.new.length, " to create"] }), _jsxs(Text, { color: "yellow", children: [" ~ ", tokens.changed.length, " to update"] }), _jsxs(Text, { color: "red", children: [" \u2717 ", tokens.removed.length, " to remove"] }), _jsxs(Text, { dimColor: true, children: [" \u00B7 ", tokens.unchanged.length, " unchanged"] }), tokens.changed
21
21
  .filter((t) => t.hasPendingDraftChanges)
22
- .map((item, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: true })] }, i))), _jsx(Text, { children: " " })] })), !allowDeletions && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: " \u2298 Deletions are hidden by default \u2014 rerun with --allow-deletions to see them." }) })), _jsx(Text, { dimColor: true, children: " Press Q to exit." })] }));
22
+ .map((item, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsxs(Text, { color: "yellow", children: [" ~ ", item.current.name] }), _jsx(DraftWarning, { hasDraft: true })] }, i))), _jsx(Text, { children: " " })] })), _jsx(Text, { dimColor: true, children: " Press Q to exit." })] }));
23
23
  }
@@ -1,8 +1,2 @@
1
1
  import type { Command } from 'commander';
2
- /**
3
- * Feature 8: render the warning banner shown when a custom skill prompt is
4
- * active. Always cites the bundled invariants that the override bypasses so
5
- * the operator cannot miss it.
6
- */
7
- export declare function formatCustomPromptBanner(skill: 'components' | 'select', path: string): string;
8
2
  export declare function registerGenerateCommand(program: Command): void;
@@ -4,10 +4,9 @@ import { access, readFile, readdir, stat } from 'node:fs/promises';
4
4
  import { join, resolve } from 'node:path';
5
5
  import { execFile } from 'node:child_process';
6
6
  import { promisify } from 'node:util';
7
- import { AGENT_NAMES, describeAgentFailure, isAgentName, parseToolCallLines, parseTokenToolCallLines, resolveBinary, runAgent, } from './agent-runner.js';
7
+ import { AGENT_NAMES, createLocalCliAgentInvoker, describeAgentFailure, formatCustomPromptBanner, formatGenerateProgressLine, isAgentName, parseToolCallLines, parseTokenToolCallLines, resolveBinary, resolveSkillPath, buildPrompt, } from '@contentful/experience-design-system-generation';
8
8
  import { OutputFormatter, c } from '../output/format.js';
9
- import { formatGenerateProgressLine } from './progress.js';
10
- import { buildPrompt, resolveSkillPath } from './prompt-builder.js';
9
+ import { getDebugLogger } from '../lib/debug-logger.js';
11
10
  import { GenerateView } from './tui/GenerateView.js';
12
11
  import { registerGenerateEditCommand } from './edit/command.js';
13
12
  import { openPipelineDb, loadRawComponents, applyToolCalls, applyTokenToolCalls, computeComponentInputHash, computeTokenInputHash, lookupCache, lookupCacheByEntity, storeCache, copyComponentFromCache, copyTokensFromCache, renameEmptySlots, } from '../session/db.js';
@@ -19,16 +18,9 @@ const execFileAsync = promisify(execFile);
19
18
  const DEFAULT_TIMEOUT_MS = Number(process.env.EDS_AGENT_TIMEOUT_MS ?? 3 * 60 * 1000);
20
19
  const DEFAULT_COMPONENT_CONCURRENCY = 10;
21
20
  const RETRY_BACKOFF_MS = Number(process.env.EDS_RETRY_BACKOFF_MS ?? 5_000);
22
- /**
23
- * Feature 8: render the warning banner shown when a custom skill prompt is
24
- * active. Always cites the bundled invariants that the override bypasses so
25
- * the operator cannot miss it.
26
- */
27
- export function formatCustomPromptBanner(skill, path) {
28
- return (`WARNING: Custom prompt active for ${skill}: ${path}\n` +
29
- ` Bundled invariants (utility-wrapper rejection, description content rules) do NOT apply.\n` +
30
- ` You are responsible for the prompt's correctness.\n`);
31
- }
21
+ const invoker = createLocalCliAgentInvoker({
22
+ onDebugEvent: (name, payload) => getDebugLogger().event('agent', name, payload),
23
+ });
32
24
  function die(message) {
33
25
  process.stderr.write(`${message}\n`);
34
26
  process.exit(1);
@@ -191,11 +183,10 @@ async function runOneComponent(agent, model, db, sessionId, component, tokensInl
191
183
  const formatter = new OutputFormatter(verbose, (s) => {
192
184
  outputBuf += s;
193
185
  });
194
- const result = await runAgent({
186
+ const result = await invoker.invoke({
195
187
  agent,
196
188
  model,
197
189
  prompt,
198
- interactive: false,
199
190
  timeoutMs: DEFAULT_TIMEOUT_MS,
200
191
  onOutput: (chunk) => formatter.push(chunk),
201
192
  });
@@ -512,11 +503,10 @@ async function runGenerateSkill(skill, opts, verbose = false) {
512
503
  tokenMapInline,
513
504
  outDir: process.cwd(),
514
505
  });
515
- const result = await runAgent({
506
+ const result = await invoker.invoke({
516
507
  agent,
517
508
  model,
518
509
  prompt,
519
- interactive: false,
520
510
  timeoutMs: DEFAULT_TIMEOUT_MS * 5,
521
511
  });
522
512
  if (result.timedOut) {
@@ -4,7 +4,7 @@ import { resolveAutoFilter } from './auto-filter-resolve.js';
4
4
  import { resolveAgent, resolveModel } from './agent-model-resolve.js';
5
5
  import { addAgentModelOptions } from '../lib/agent-model-options.js';
6
6
  import { resolveCompositionMode } from '../lib/composition-mode.js';
7
- import { addAllowDeletionsOption, addCompositionOptions } from '../lib/command-options.js';
7
+ import { addCompositionOptions } from '../lib/command-options.js';
8
8
  import { isConflictMode } from '../runs/save-path-resolver.js';
9
9
  import { readExperiencesCredentials } from '../credentials-store.js';
10
10
  import { DEFAULT_CONFIGURED_HOST, toConfiguredHost } from '../host-utils.js';
@@ -46,7 +46,6 @@ export function registerImportCommand(program) {
46
46
  .option('--print-prompt', 'Print the generate components prompt without invoking the agent. Replaces the legacy --dry-run prompt-print behaviour on this command.')
47
47
  .option('--auto-accept-scope', 'Accept all extracted components without prompting (for scripted/non-TTY callers)');
48
48
  addCompositionOptions(cmd);
49
- addAllowDeletionsOption(cmd);
50
49
  cmd
51
50
  .option('--composition-map <path>', 'Consume a hand-authored parent→children interchange map (implies --composite)')
52
51
  .option('--composition-agent', 'Opt into agentic mapping resolution when deterministic sources find no groups (implies --composite)')
@@ -137,7 +136,6 @@ export function registerImportCommand(program) {
137
136
  ...(opts.host ? { host: opts.host } : {}),
138
137
  interactive: interactiveTerminalSupported,
139
138
  ...(opts.force ? { force: true } : {}),
140
- ...(opts.allowDeletions ? { allowDeletions: true } : {}),
141
139
  });
142
140
  return;
143
141
  }
@@ -168,7 +166,6 @@ export function registerImportCommand(program) {
168
166
  ...(opts.saveAsNew ? { saveAsNew: true } : {}),
169
167
  ...(opts.outDir ? { outDir: opts.outDir } : {}),
170
168
  ...(opts.force ? { force: true } : {}),
171
- ...(opts.allowDeletions ? { allowDeletions: true } : {}),
172
169
  });
173
170
  return;
174
171
  }
@@ -300,7 +297,6 @@ export function registerImportCommand(program) {
300
297
  selectPromptPath: opts.selectPromptPath ?? creds.selectPromptPath,
301
298
  generatePromptPath: opts.generatePromptPath ?? creds.generatePromptPath,
302
299
  ...(opts.rawTokens ? { initialRawTokensPath: resolve(opts.rawTokens) } : {}),
303
- allowDeletions: opts.allowDeletions === true,
304
300
  ...pickerProps,
305
301
  }));
306
302
  unmountInk = unmount;
@@ -361,7 +357,6 @@ export function registerImportCommand(program) {
361
357
  dryRun: dryRunForward,
362
358
  selectPromptPath: opts.selectPromptPath,
363
359
  autoRejectCycles: opts.autoRejectCycles ?? false,
364
- allowDeletions: opts.allowDeletions ?? false,
365
360
  compositionMode: headlessCompositionMode,
366
361
  ...(opts.compositionMap ? { compositionMap: opts.compositionMap } : {}),
367
362
  ...(opts.compositionAgent ? { compositionAgent: true } : {}),
@@ -26,7 +26,6 @@ export interface PipelineOptions {
26
26
  selectPromptPath?: string;
27
27
  /** When true, auto-reject cycle participants and retry push instead of surfacing an error. */
28
28
  autoRejectCycles?: boolean;
29
- allowDeletions?: boolean;
30
29
  compositionMode?: CompositionMode;
31
30
  compositionMap?: string;
32
31
  compositionAgent?: boolean;
@@ -418,8 +418,6 @@ export async function runPipeline(opts, progressWriter, cliPathOverride) {
418
418
  pushArgs.push('--host', opts.host);
419
419
  if (opts.verbose)
420
420
  pushArgs.push('--verbose');
421
- if (opts.allowDeletions)
422
- pushArgs.push('--allow-deletions');
423
421
  pushArgs.push('--yes');
424
422
  const pushStepId = createStep(db, sessionId, 'apply push', {
425
423
  components: componentsPath,
@@ -66,6 +66,5 @@ export type WizardAppProps = {
66
66
  initialRawTokensPath?: string;
67
67
  initialRuns?: RunRecord[];
68
68
  onRunPicked?: (selection: RunPickerSelection) => void;
69
- allowDeletions?: boolean;
70
69
  };
71
- export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, initialAgent, initialModel, initialProjectPath, host, autoAcceptScope, autoRejectCycles, compositionMode, compositionMap, compositionAgent, compositionAgentMode, compositionRefresh, generateMap, promptOverrides, noCache, autoFilter, livePreview, noPush, noSave, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, allowDeletions, }?: WizardAppProps): React.ReactElement;
70
+ export declare function WizardApp({ initialSpaceId, initialEnvironmentId, initialCmaToken, initialHost, initialAgent, initialModel, initialProjectPath, host, autoAcceptScope, autoRejectCycles, compositionMode, compositionMap, compositionAgent, compositionAgentMode, compositionRefresh, generateMap, promptOverrides, noCache, autoFilter, livePreview, noPush, noSave, outDirOverride, onConflictMode, selectPromptPath, generatePromptPath, seedExtractSessionId, seedGenerateSessionId, seedTokenSessionId, seedTokensPath, initialStep, initialRawTokensPath, initialRuns, onRunPicked, }?: WizardAppProps): React.ReactElement;