@contentful/experience-design-system-cli 2.14.2 → 2.15.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 (193) hide show
  1. package/README.md +64 -2
  2. package/dist/package.json +3 -2
  3. package/dist/src/analyze/command.d.ts +11 -0
  4. package/dist/src/analyze/command.js +298 -3
  5. package/dist/src/analyze/composite-closure.d.ts +25 -0
  6. package/dist/src/analyze/composite-closure.js +0 -0
  7. package/dist/src/analyze/composition/agent-parser/author-prompt.d.ts +4 -0
  8. package/dist/src/analyze/composition/agent-parser/author-prompt.js +22 -0
  9. package/dist/src/analyze/composition/agent-parser/extract-parser.d.ts +8 -0
  10. package/dist/src/analyze/composition/agent-parser/extract-parser.js +44 -0
  11. package/dist/src/analyze/composition/agent-parser/load-prompt.d.ts +7 -0
  12. package/dist/src/analyze/composition/agent-parser/load-prompt.js +31 -0
  13. package/dist/src/analyze/composition/agent-parser/resolve-via-parser.d.ts +46 -0
  14. package/dist/src/analyze/composition/agent-parser/resolve-via-parser.js +88 -0
  15. package/dist/src/analyze/composition/agent-parser/sandbox.d.ts +45 -0
  16. package/dist/src/analyze/composition/agent-parser/sandbox.js +187 -0
  17. package/dist/src/analyze/composition/apply-mapping.d.ts +22 -0
  18. package/dist/src/analyze/composition/apply-mapping.js +67 -0
  19. package/dist/src/analyze/composition/candidate-critic-agent.d.ts +7 -0
  20. package/dist/src/analyze/composition/candidate-critic-agent.js +39 -0
  21. package/dist/src/analyze/composition/candidate-critic.d.ts +32 -0
  22. package/dist/src/analyze/composition/candidate-critic.js +63 -0
  23. package/dist/src/analyze/composition/candidate-files.d.ts +34 -0
  24. package/dist/src/analyze/composition/candidate-files.js +131 -0
  25. package/dist/src/analyze/composition/composition-cache-key.d.ts +19 -0
  26. package/dist/src/analyze/composition/composition-cache-key.js +19 -0
  27. package/dist/src/analyze/composition/interchange-schema.d.ts +37 -0
  28. package/dist/src/analyze/composition/interchange-schema.js +61 -0
  29. package/dist/src/analyze/composition/merge-edges.d.ts +19 -0
  30. package/dist/src/analyze/composition/merge-edges.js +50 -0
  31. package/dist/src/analyze/composition/parse-map-edges.d.ts +16 -0
  32. package/dist/src/analyze/composition/parse-map-edges.js +55 -0
  33. package/dist/src/analyze/composition/resolve-mapping-cli.d.ts +23 -0
  34. package/dist/src/analyze/composition/resolve-mapping-cli.js +29 -0
  35. package/dist/src/analyze/composition/resolve-mapping.d.ts +59 -0
  36. package/dist/src/analyze/composition/resolve-mapping.js +94 -0
  37. package/dist/src/analyze/cycle-detection.d.ts +27 -0
  38. package/dist/src/analyze/cycle-detection.js +234 -0
  39. package/dist/src/analyze/cycle-view.d.ts +7 -0
  40. package/dist/src/analyze/cycle-view.js +28 -0
  41. package/dist/src/analyze/fuzzy-search.d.ts +3 -0
  42. package/dist/src/analyze/fuzzy-search.js +55 -0
  43. package/dist/src/analyze/issue-inheritance.d.ts +12 -0
  44. package/dist/src/analyze/issue-inheritance.js +108 -0
  45. package/dist/src/analyze/lineage.d.ts +29 -0
  46. package/dist/src/analyze/lineage.js +0 -0
  47. package/dist/src/analyze/scope-gate-cascade.d.ts +11 -0
  48. package/dist/src/analyze/scope-gate-cascade.js +170 -0
  49. package/dist/src/analyze/search-neighborhood.d.ts +3 -0
  50. package/dist/src/analyze/search-neighborhood.js +56 -0
  51. package/dist/src/analyze/select/tui/App.js +5 -16
  52. package/dist/src/analyze/select/tui/components/ComponentRationalePanel.d.ts +0 -4
  53. package/dist/src/analyze/select/tui/components/ComponentRationalePanel.js +3 -15
  54. package/dist/src/analyze/select/tui/components/FieldEditor.d.ts +29 -38
  55. package/dist/src/analyze/select/tui/components/FieldEditor.js +309 -185
  56. package/dist/src/analyze/select/tui/components/FinalizeDialog.d.ts +13 -1
  57. package/dist/src/analyze/select/tui/components/FinalizeDialog.js +23 -2
  58. package/dist/src/analyze/select/tui/components/GotoBanner.d.ts +31 -0
  59. package/dist/src/analyze/select/tui/components/GotoBanner.js +30 -0
  60. package/dist/src/analyze/select/tui/components/GroupedSidebar.d.ts +91 -0
  61. package/dist/src/analyze/select/tui/components/GroupedSidebar.js +497 -0
  62. package/dist/src/analyze/select/tui/components/HelpOverlay.d.ts +13 -1
  63. package/dist/src/analyze/select/tui/components/HelpOverlay.js +9 -3
  64. package/dist/src/analyze/select/tui/components/JsonEditor.js +3 -3
  65. package/dist/src/analyze/select/tui/components/LineagePanel.d.ts +14 -0
  66. package/dist/src/analyze/select/tui/components/LineagePanel.js +22 -0
  67. package/dist/src/analyze/select/tui/components/RationalePanel.d.ts +0 -7
  68. package/dist/src/analyze/select/tui/components/RationalePanel.js +3 -15
  69. package/dist/src/analyze/select/tui/components/Sidebar.d.ts +0 -6
  70. package/dist/src/analyze/select/tui/components/Sidebar.js +11 -27
  71. package/dist/src/analyze/select/tui/components/StatusBar.js +2 -1
  72. package/dist/src/analyze/select/tui/components/removed-components-text.d.ts +7 -0
  73. package/dist/src/analyze/select/tui/components/removed-components-text.js +11 -0
  74. package/dist/src/analyze/select/tui/hooks/useImmediateInput.d.ts +1 -5
  75. package/dist/src/analyze/select/tui/hooks/useImmediateInput.js +8 -10
  76. package/dist/src/analyze/select/tui/theme.d.ts +15 -0
  77. package/dist/src/analyze/select/tui/theme.js +28 -0
  78. package/dist/src/analyze/selection-cascade.d.ts +3 -0
  79. package/dist/src/analyze/selection-cascade.js +19 -0
  80. package/dist/src/analyze/slot-graph.d.ts +11 -0
  81. package/dist/src/analyze/slot-graph.js +17 -0
  82. package/dist/src/apply/api-client.d.ts +2 -1
  83. package/dist/src/apply/api-client.js +40 -6
  84. package/dist/src/apply/command.d.ts +17 -1
  85. package/dist/src/apply/command.js +69 -11
  86. package/dist/src/apply/error-parser.d.ts +16 -0
  87. package/dist/src/apply/error-parser.js +117 -0
  88. package/dist/src/apply/tui/ServerPreviewView.js +3 -1
  89. package/dist/src/credentials-store.d.ts +6 -0
  90. package/dist/src/credentials-store.js +5 -1
  91. package/dist/src/generate/agent-runner.d.ts +6 -0
  92. package/dist/src/generate/agent-runner.js +24 -8
  93. package/dist/src/import/command.js +69 -31
  94. package/dist/src/import/cycle-auto-reject.d.ts +4 -0
  95. package/dist/src/import/cycle-auto-reject.js +14 -0
  96. package/dist/src/import/orchestrator.d.ts +20 -7
  97. package/dist/src/import/orchestrator.js +139 -35
  98. package/dist/src/import/strip-allowed-components.d.ts +18 -0
  99. package/dist/src/import/strip-allowed-components.js +22 -0
  100. package/dist/src/import/tui/CustomPromptBanner.d.ts +0 -5
  101. package/dist/src/import/tui/CustomPromptBanner.js +2 -1
  102. package/dist/src/import/tui/WizardApp.d.ts +10 -74
  103. package/dist/src/import/tui/WizardApp.js +234 -246
  104. package/dist/src/import/tui/ai-flag.d.ts +5 -0
  105. package/dist/src/import/tui/ai-flag.js +6 -0
  106. package/dist/src/import/tui/autocomplete.d.ts +5 -0
  107. package/dist/src/import/tui/autocomplete.js +32 -0
  108. package/dist/src/import/tui/components/AutoFilterBanner.d.ts +10 -0
  109. package/dist/src/import/tui/components/AutoFilterBanner.js +19 -0
  110. package/dist/src/import/tui/components/CounterStrip.d.ts +12 -0
  111. package/dist/src/import/tui/components/CounterStrip.js +12 -0
  112. package/dist/src/import/tui/components/LegendEntry.d.ts +2 -0
  113. package/dist/src/import/tui/components/LegendEntry.js +6 -0
  114. package/dist/src/import/tui/cycle-panel-scroll.d.ts +8 -0
  115. package/dist/src/import/tui/cycle-panel-scroll.js +33 -0
  116. package/dist/src/import/tui/final-review-host.d.ts +3 -7
  117. package/dist/src/import/tui/final-review-host.js +9 -4
  118. package/dist/src/import/tui/group-collapse.d.ts +2 -0
  119. package/dist/src/import/tui/group-collapse.js +11 -0
  120. package/dist/src/import/tui/history.d.ts +22 -0
  121. package/dist/src/import/tui/history.js +53 -0
  122. package/dist/src/import/tui/hooks/useLineage.d.ts +32 -0
  123. package/dist/src/import/tui/hooks/useLineage.js +50 -0
  124. package/dist/src/import/tui/hooks/useOverlayPanel.d.ts +13 -0
  125. package/dist/src/import/tui/hooks/useOverlayPanel.js +27 -0
  126. package/dist/src/import/tui/lineage-layout.d.ts +75 -0
  127. package/dist/src/import/tui/lineage-layout.js +82 -0
  128. package/dist/src/import/tui/runLivePreview.d.ts +11 -0
  129. package/dist/src/import/tui/runLivePreview.js +7 -1
  130. package/dist/src/import/tui/scope-gate-columns.d.ts +31 -0
  131. package/dist/src/import/tui/scope-gate-columns.js +94 -0
  132. package/dist/src/import/tui/scope-gate-host.d.ts +3 -1
  133. package/dist/src/import/tui/scope-gate-host.js +11 -3
  134. package/dist/src/import/tui/sidebar-width.d.ts +1 -0
  135. package/dist/src/import/tui/sidebar-width.js +3 -0
  136. package/dist/src/import/tui/step-filters.d.ts +19 -0
  137. package/dist/src/import/tui/step-filters.js +42 -0
  138. package/dist/src/import/tui/steps/AtomicGenerateReviewStep.d.ts +42 -0
  139. package/dist/src/import/tui/steps/AtomicGenerateReviewStep.js +726 -0
  140. package/dist/src/import/tui/steps/AtomicScopeGateStep.d.ts +3 -0
  141. package/dist/src/import/tui/steps/AtomicScopeGateStep.js +211 -0
  142. package/dist/src/import/tui/steps/CredentialsStep.js +5 -18
  143. package/dist/src/import/tui/steps/DoneStep.d.ts +0 -2
  144. package/dist/src/import/tui/steps/DoneStep.js +3 -2
  145. package/dist/src/import/tui/steps/ErrorStep.js +2 -1
  146. package/dist/src/import/tui/steps/GenerateReviewStep.d.ts +19 -25
  147. package/dist/src/import/tui/steps/GenerateReviewStep.js +1258 -229
  148. package/dist/src/import/tui/steps/PathValidationStep.js +3 -2
  149. package/dist/src/import/tui/steps/PreviewStep.js +2 -1
  150. package/dist/src/import/tui/steps/PushDecisionGateStep.d.ts +0 -7
  151. package/dist/src/import/tui/steps/PushDecisionGateStep.js +3 -6
  152. package/dist/src/import/tui/steps/PushingStep.js +2 -1
  153. package/dist/src/import/tui/steps/RunningStep.d.ts +4 -1
  154. package/dist/src/import/tui/steps/RunningStep.js +3 -2
  155. package/dist/src/import/tui/steps/ScopeGateStep.d.ts +24 -0
  156. package/dist/src/import/tui/steps/ScopeGateStep.js +850 -193
  157. package/dist/src/import/tui/steps/TokenInputStep.js +2 -1
  158. package/dist/src/import/tui/steps/WelcomeStep.js +2 -1
  159. package/dist/src/import/tui/steps/WizardPreviewStep.d.ts +6 -0
  160. package/dist/src/import/tui/steps/WizardPreviewStep.js +95 -56
  161. package/dist/src/import/tui/steps/auto-reject-decision.d.ts +7 -0
  162. package/dist/src/import/tui/steps/auto-reject-decision.js +9 -0
  163. package/dist/src/import/tui/steps/breaking-change-format.d.ts +2 -0
  164. package/dist/src/import/tui/steps/breaking-change-format.js +24 -0
  165. package/dist/src/import/tui/steps/enumerate-cycle-breaks.d.ts +14 -0
  166. package/dist/src/import/tui/steps/enumerate-cycle-breaks.js +0 -0
  167. package/dist/src/import/tui/steps/preview-diff.js +48 -5
  168. package/dist/src/import/tui/useFinalizePreview.d.ts +34 -0
  169. package/dist/src/import/tui/useFinalizePreview.js +0 -0
  170. package/dist/src/import/tui/useLivePreview.d.ts +3 -0
  171. package/dist/src/import/tui/useLivePreview.js +1 -0
  172. package/dist/src/import/tui/wizard-state-transitions.d.ts +5 -59
  173. package/dist/src/import/tui/wizard-state-transitions.js +5 -60
  174. package/dist/src/lib/composition-mode.d.ts +28 -0
  175. package/dist/src/lib/composition-mode.js +37 -0
  176. package/dist/src/lib/prompt-overrides.d.ts +38 -0
  177. package/dist/src/lib/prompt-overrides.js +69 -0
  178. package/dist/src/print/command.js +19 -1
  179. package/dist/src/runs/modify-launcher.d.ts +3 -0
  180. package/dist/src/runs/modify-launcher.js +2 -0
  181. package/dist/src/runs/replay-helpers.js +1 -0
  182. package/dist/src/runs/run-picker.js +2 -18
  183. package/dist/src/runs/store.d.ts +4 -0
  184. package/dist/src/session/db.d.ts +23 -40
  185. package/dist/src/session/db.js +110 -138
  186. package/dist/src/setup/composition-mode-prompt.d.ts +14 -0
  187. package/dist/src/setup/composition-mode-prompt.js +37 -0
  188. package/package.json +5 -4
  189. package/prompts/composition-dir-critic.md +7 -0
  190. package/prompts/composition-edges.md +8 -0
  191. package/prompts/composition-parser-repair-empty.md +1 -0
  192. package/prompts/composition-parser-repair-error.md +2 -0
  193. package/prompts/composition-parser.md +42 -0
@@ -1,10 +1,6 @@
1
1
  import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PALETTE } from '../theme.js';
2
3
  import { Box, Text } from 'ink';
3
- /**
4
- * Word-wrap a single rationale paragraph to fit within `innerWidth` columns.
5
- * Returns an array of wrapped lines. Empty input → single empty line so the
6
- * row still occupies vertical space.
7
- */
8
4
  function wrapText(text, innerWidth) {
9
5
  if (!text)
10
6
  return [''];
@@ -16,7 +12,6 @@ function wrapText(text, innerWidth) {
16
12
  let current = '';
17
13
  for (const w of words) {
18
14
  if (current.length === 0) {
19
- // Word longer than width — hard-break.
20
15
  if (w.length > width) {
21
16
  let rest = w;
22
17
  while (rest.length > width) {
@@ -52,13 +47,6 @@ function wrapText(text, innerWidth) {
52
47
  lines.push(current);
53
48
  return lines.length > 0 ? lines : [''];
54
49
  }
55
- /**
56
- * Flatten `rows` to a list of rendered lines so scrollOffset slicing matches
57
- * what the operator sees. Layout per row:
58
- * <name> ← bold
59
- * <wrapped text> ← one or more lines
60
- * <blank> ← spacer (omitted after the final row)
61
- */
62
50
  export function renderRationaleLines(rows, innerWidth) {
63
51
  const out = [];
64
52
  rows.forEach((row, idx) => {
@@ -80,12 +68,12 @@ export function RationalePanel({ componentName, rows, scrollOffset, width, heigh
80
68
  const overflowed = totalLines > height;
81
69
  const visibleStart = totalLines === 0 ? 0 : scrollOffset + 1;
82
70
  const visibleEnd = Math.min(totalLines, scrollOffset + height);
83
- return (_jsxs(Box, { flexDirection: "column", width: width, height: height + 2, borderStyle: "single", borderColor: active ? 'white' : undefined, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, dimColor: !active, children: `RATIONALE — ${componentName}` }), overflowed && (_jsxs(_Fragment, { children: [_jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: !active, children: `↕ ${visibleStart}-${visibleEnd}/${totalLines}` })] }))] }), visible.map((line, i) => {
71
+ return (_jsxs(Box, { flexDirection: "column", width: width, height: height + 2, borderStyle: "single", borderColor: active ? PALETTE.inverse : undefined, children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, dimColor: !active, children: `RATIONALE — ${componentName}` }), overflowed && (_jsxs(_Fragment, { children: [_jsx(Box, { flexGrow: 1 }), _jsx(Text, { dimColor: !active, children: `↕ ${visibleStart}-${visibleEnd}/${totalLines}` })] }))] }), visible.map((line, i) => {
84
72
  if (line.kind === 'blank') {
85
73
  return (_jsx(Box, { children: _jsx(Text, { children: " " }) }, i));
86
74
  }
87
75
  if (line.kind === 'name') {
88
- return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: "cyan", dimColor: !active, children: line.text }), line.isSlot && _jsx(Text, { dimColor: true, children: " (slot)" })] }, i));
76
+ return (_jsxs(Box, { children: [_jsx(Text, { bold: true, color: PALETTE.info, dimColor: !active, children: line.text }), line.isSlot && _jsx(Text, { dimColor: true, children: " (slot)" })] }, i));
89
77
  }
90
78
  return (_jsx(Box, { children: _jsx(Text, { dimColor: !active, children: line.text }) }, i));
91
79
  })] }));
@@ -1,11 +1,5 @@
1
1
  import React from 'react';
2
2
  import type { PreviewAnnotation, ReviewComponentSummary, ReviewComponentStatus } from '../../types.js';
3
- /**
4
- * Pilot-2026-06-23 R2: render a single-character preview-diff badge directly
5
- * before each component name in the sidebar so operators can scan the diff
6
- * shape at a glance without opening each row. Returns null for unannotated
7
- * rows so the row falls back to its existing single-space layout.
8
- */
9
3
  export declare function previewBadge(annotation: PreviewAnnotation | undefined): {
10
4
  char: string;
11
5
  color: string;
@@ -1,21 +1,16 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
- /**
4
- * Pilot-2026-06-23 R2: render a single-character preview-diff badge directly
5
- * before each component name in the sidebar so operators can scan the diff
6
- * shape at a glance without opening each row. Returns null for unannotated
7
- * rows so the row falls back to its existing single-space layout.
8
- */
3
+ import { PALETTE } from '../theme.js';
9
4
  export function previewBadge(annotation) {
10
5
  switch (annotation) {
11
6
  case 'new':
12
- return { char: '+', color: 'green' };
7
+ return { char: '+', color: PALETTE.success };
13
8
  case 'changed':
14
- return { char: '~', color: 'yellow' };
9
+ return { char: '~', color: PALETTE.warning };
15
10
  case 'removed':
16
- return { char: '-', color: 'red', dim: true };
11
+ return { char: '-', color: PALETTE.error, dim: true };
17
12
  case 'breaking':
18
- return { char: '!', color: 'red', bold: true };
13
+ return { char: '!', color: PALETTE.error, bold: true };
19
14
  default:
20
15
  return null;
21
16
  }
@@ -25,7 +20,6 @@ export function statusIcon(status, validationErrorCount,
25
20
  // decision must remain visible. Color is already yellow via statusColor for
26
21
  // warning-only components, so the warning cue is preserved.
27
22
  _validationWarningCount) {
28
- // Errors override — a structurally broken component should never render as ✓/✗.
29
23
  if (validationErrorCount > 0)
30
24
  return '⚠';
31
25
  switch (status) {
@@ -41,18 +35,18 @@ _validationWarningCount) {
41
35
  }
42
36
  export function statusColor(status, validationErrorCount, validationWarningCount) {
43
37
  if (validationErrorCount > 0)
44
- return 'red';
38
+ return PALETTE.error;
45
39
  if (validationWarningCount > 0)
46
- return 'yellow';
40
+ return PALETTE.warning;
47
41
  switch (status) {
48
42
  case 'accepted':
49
- return 'green';
43
+ return PALETTE.success;
50
44
  case 'rejected':
51
- return 'red';
45
+ return PALETTE.error;
52
46
  case 'reviewed':
53
- return 'yellow';
47
+ return PALETTE.warning;
54
48
  case 'needs-review':
55
- return 'white';
49
+ return PALETTE.muted;
56
50
  }
57
51
  }
58
52
  function truncateName(name, maxLen) {
@@ -60,13 +54,6 @@ function truncateName(name, maxLen) {
60
54
  return name;
61
55
  return name.slice(0, maxLen) + '…';
62
56
  }
63
- /**
64
- * Sort key for stable secondary ordering within a validation tier:
65
- * needs-review components first, then by extractionConfidence ascending
66
- * (lowest confidence first, null treated as highest = last). Used to be
67
- * applied inline in App.tsx, then this helper sorted again — pulled in
68
- * here so the sort happens once.
69
- */
70
57
  function withinTierComparator(a, b) {
71
58
  const aPending = a.needsReview && a.status === 'needs-review' ? 0 : 1;
72
59
  const bPending = b.needsReview && b.status === 'needs-review' ? 0 : 1;
@@ -95,9 +82,6 @@ export function Sidebar({ components, selectedId, focused, scrollOffset, visible
95
82
  const icon = statusIcon(component.status, component.validationErrorCount, component.validationWarningCount);
96
83
  const color = statusColor(component.status, component.validationErrorCount, component.validationWarningCount);
97
84
  const badge = previewBadge(component.previewAnnotation);
98
- // Reserve one column for the preview badge regardless of whether this
99
- // row has one — keeps name truncation stable across rows so the column
100
- // width doesn't jitter as annotations flip in/out.
101
85
  const maxNameLen = Math.max(1, width - 5);
102
86
  const name = truncateName(component.name, maxNameLen);
103
87
  if (collapsed) {
@@ -1,6 +1,7 @@
1
1
  import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
2
  import { Box, Text } from 'ink';
3
+ import { PALETTE } from '../theme.js';
3
4
  export function StatusBar({ accepted, rejected, reviewed, needsReview }) {
4
5
  const allResolved = needsReview === 0;
5
- return (_jsxs(Box, { gap: 2, flexWrap: "wrap", children: [_jsxs(Text, { color: "green", children: [accepted, " accepted"] }), _jsxs(Text, { color: "red", children: [rejected, " rejected"] }), _jsxs(Text, { dimColor: true, children: [needsReview, " pending"] }), _jsxs(Text, { color: "cyan", children: [reviewed, " reviewed"] }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { dimColor: true, children: "[A] accept all" }), _jsx(Text, { bold: allResolved, color: allResolved ? 'green' : 'white', children: "[F] finalize" })] }));
6
+ return (_jsxs(Box, { gap: 2, flexWrap: "wrap", children: [_jsxs(Text, { color: PALETTE.success, children: [accepted, " accepted"] }), _jsxs(Text, { color: PALETTE.error, children: [rejected, " rejected"] }), _jsxs(Text, { dimColor: true, children: [needsReview, " pending"] }), _jsxs(Text, { color: PALETTE.info, children: [reviewed, " reviewed"] }), _jsx(Text, { dimColor: true, children: "\u00B7" }), _jsx(Text, { dimColor: true, children: "[A] accept all" }), _jsx(Text, { bold: allResolved, color: allResolved ? PALETTE.success : PALETTE.fg, children: "[F] finalize" })] }));
6
7
  }
@@ -0,0 +1,7 @@
1
+ import type { ComponentTypeSummary } from '@contentful/experience-design-system-types';
2
+ /** Header line for the removed-components surfaces (deletion panel + finalize
3
+ * dialog). Kept in one place so both read identically. `withExpandHint` adds
4
+ * the panel's "[d] to expand/collapse" affordance, which the dialog omits. */
5
+ export declare function removedComponentsHeader(count: number, withExpandHint: boolean): string;
6
+ /** One bullet line per removed component, matching the deletion panel. */
7
+ export declare function removedComponentLine(rc: ComponentTypeSummary): string;
@@ -0,0 +1,11 @@
1
+ /** Header line for the removed-components surfaces (deletion panel + finalize
2
+ * dialog). Kept in one place so both read identically. `withExpandHint` adds
3
+ * the panel's "[d] to expand/collapse" affordance, which the dialog omits. */
4
+ export function removedComponentsHeader(count, withExpandHint) {
5
+ const hint = withExpandHint ? ' (press [d] to expand/collapse)' : '';
6
+ return `Removed components (${count}) — will be DELETED from target space${hint}`;
7
+ }
8
+ /** One bullet line per removed component, matching the deletion panel. */
9
+ export function removedComponentLine(rc) {
10
+ return `- ${rc.name}${rc.id ? ` (${rc.id})` : ''}`;
11
+ }
@@ -10,15 +10,11 @@ type Key = {
10
10
  ctrl: boolean;
11
11
  shift: boolean;
12
12
  tab: boolean;
13
+ shiftTab: boolean;
13
14
  backspace: boolean;
14
15
  delete: boolean;
15
16
  meta: boolean;
16
17
  };
17
18
  type InputHandler = (input: string, key: Key) => void;
18
- /**
19
- * Like Ink's useInput, but uses useLayoutEffect so the listener is registered
20
- * synchronously after render. This allows stdin.write() calls in tests to work
21
- * immediately after render() without awaiting effects.
22
- */
23
19
  export declare function useImmediateInput(handler: InputHandler): void;
24
20
  export {};
@@ -1,6 +1,10 @@
1
1
  import { useLayoutEffect, useRef } from 'react';
2
2
  import { useStdin } from 'ink';
3
3
  function parseInput(data) {
4
+ // Shift-Tab in most terminals emits CSI Z (\x1b[Z). We surface it as both
5
+ // `tab` and `shiftTab` so callers that already branch on `tab` still fire,
6
+ // and new callers can distinguish direction via `shiftTab`.
7
+ const isShiftTab = data === '\x1b[Z';
4
8
  const key = {
5
9
  upArrow: data === '\x1b[A',
6
10
  downArrow: data === '\x1b[B',
@@ -11,22 +15,21 @@ function parseInput(data) {
11
15
  return: data === '\r' || data === '\n',
12
16
  escape: data === '\x1b',
13
17
  ctrl: false,
14
- shift: false,
15
- tab: data === '\t',
18
+ shift: isShiftTab,
19
+ tab: data === '\t' || isShiftTab,
20
+ shiftTab: isShiftTab,
16
21
  backspace: data === '\x7f' || data === '\b',
17
22
  delete: data === '\x1b[3~',
18
23
  meta: data === '\x1b',
19
24
  };
20
25
  let input = data;
21
- // Ctrl+letter: data is \x01-\x1a (Ctrl+A through Ctrl+Z)
22
26
  if (data.length === 1) {
23
27
  const code = data.charCodeAt(0);
24
28
  if (code >= 1 && code <= 26) {
25
29
  key.ctrl = true;
26
- input = String.fromCharCode(code + 96); // convert to lowercase letter
30
+ input = String.fromCharCode(code + 96);
27
31
  }
28
32
  }
29
- // Arrow keys and special keys produce no printable input
30
33
  if (key.upArrow ||
31
34
  key.downArrow ||
32
35
  key.leftArrow ||
@@ -43,11 +46,6 @@ function parseInput(data) {
43
46
  }
44
47
  return { input, key };
45
48
  }
46
- /**
47
- * Like Ink's useInput, but uses useLayoutEffect so the listener is registered
48
- * synchronously after render. This allows stdin.write() calls in tests to work
49
- * immediately after render() without awaiting effects.
50
- */
51
49
  export function useImmediateInput(handler) {
52
50
  const { stdin, setRawMode } = useStdin();
53
51
  const handlerRef = useRef(handler);
@@ -0,0 +1,15 @@
1
+ import type { ReviewComponentStatus } from '../types.js';
2
+ export declare const PALETTE: {
3
+ readonly info: "#1773EB";
4
+ readonly success: "#00C459";
5
+ readonly error: "#E44F20";
6
+ readonly warning: "#FFDA00";
7
+ readonly fg: "#000000";
8
+ readonly inverse: "#FFFFFF";
9
+ readonly muted: "#A6B5C7";
10
+ readonly border: "#C4D1DE";
11
+ readonly subtle: "#DDE5EC";
12
+ readonly bg: "#EFF2F6";
13
+ };
14
+ export type PaletteRole = keyof typeof PALETTE;
15
+ export declare function statusPaletteColor(status: ReviewComponentStatus, validationErrorCount: number, validationWarningCount: number): string;
@@ -0,0 +1,28 @@
1
+ export const PALETTE = {
2
+ info: '#1773EB',
3
+ success: '#00C459',
4
+ error: '#E44F20',
5
+ warning: '#FFDA00',
6
+ fg: '#000000',
7
+ inverse: '#FFFFFF',
8
+ muted: '#A6B5C7',
9
+ border: '#C4D1DE',
10
+ subtle: '#DDE5EC',
11
+ bg: '#EFF2F6',
12
+ };
13
+ export function statusPaletteColor(status, validationErrorCount, validationWarningCount) {
14
+ if (validationErrorCount > 0)
15
+ return PALETTE.error;
16
+ if (validationWarningCount > 0)
17
+ return PALETTE.warning;
18
+ switch (status) {
19
+ case 'accepted':
20
+ return PALETTE.success;
21
+ case 'rejected':
22
+ return PALETTE.error;
23
+ case 'reviewed':
24
+ return PALETTE.warning;
25
+ case 'needs-review':
26
+ return PALETTE.muted;
27
+ }
28
+ }
@@ -0,0 +1,3 @@
1
+ import type { ComponentGraphNode } from './composite-closure.js';
2
+ export declare function computeRejectCascade(target: string, components: ComponentGraphNode[]): Set<string>;
3
+ export declare function computeAcceptCascade(target: string, components: ComponentGraphNode[]): Set<string>;
@@ -0,0 +1,19 @@
1
+ import { computeClosure } from './composite-closure.js';
2
+ import { findAllAncestors } from './lineage.js';
3
+ export function computeRejectCascade(target, components) {
4
+ const out = new Set();
5
+ out.add(target);
6
+ for (const ancestor of findAllAncestors(target, components)) {
7
+ out.add(ancestor);
8
+ }
9
+ return out;
10
+ }
11
+ export function computeAcceptCascade(target, components) {
12
+ const out = new Set();
13
+ out.add(target);
14
+ const closure = computeClosure(target, components);
15
+ for (const node of closure.nodes) {
16
+ out.add(node.name);
17
+ }
18
+ return out;
19
+ }
@@ -0,0 +1,11 @@
1
+ import type { CDFComponentEntry } from '@contentful/experience-design-system-types';
2
+ import type { ComponentGraphNode } from './composite-closure.js';
3
+ export interface SlotGraphInput {
4
+ key: string;
5
+ entry: CDFComponentEntry;
6
+ status?: string;
7
+ }
8
+ export interface BuildOptions {
9
+ stripRejectedEdges?: boolean;
10
+ }
11
+ export declare function buildComponentGraph(components: SlotGraphInput[], opts?: BuildOptions): ComponentGraphNode[];
@@ -0,0 +1,17 @@
1
+ const REJECTED_STATUSES = new Set(['error', 'rejected']);
2
+ export function buildComponentGraph(components, opts) {
3
+ const stripRejectedEdges = opts?.stripRejectedEdges === true;
4
+ return components.map((row) => {
5
+ if (stripRejectedEdges && row.status !== undefined && REJECTED_STATUSES.has(row.status)) {
6
+ return { name: row.key, slots: [] };
7
+ }
8
+ const slotDefs = row.entry.$slots ?? {};
9
+ const slots = Object.entries(slotDefs).map(([slotName, slotDef]) => ({
10
+ name: slotName,
11
+ allowedComponents: Array.isArray(slotDef?.$allowedComponents)
12
+ ? slotDef.$allowedComponents.filter((v) => typeof v === 'string')
13
+ : [],
14
+ }));
15
+ return { name: row.key, slots };
16
+ });
17
+ }
@@ -1,4 +1,4 @@
1
- import type { ManifestPayload, ServerPreviewResponse, ApplyOperationResponse } from '@contentful/experience-design-system-types';
1
+ import type { ManifestPayload, ServerPreviewResponse, ApplyOperationResponse, BreakingChange } from '@contentful/experience-design-system-types';
2
2
  export declare const DEFAULT_HOST = "https://api.contentful.com";
3
3
  export declare const PREVIEW_ERROR_PREFIX = "preview failed:";
4
4
  export declare const APPLY_ERROR_PREFIX = "apply failed:";
@@ -31,6 +31,7 @@ export interface PreviewValidationError {
31
31
  * the field-level detail.
32
32
  */
33
33
  export declare function parsePreviewValidationErrors(body: string): PreviewValidationError[];
34
+ export declare function sanitizeBreakingChanges(raw: unknown): BreakingChange[];
34
35
  export declare class ImportApiClient {
35
36
  private host;
36
37
  private token;
@@ -2,8 +2,6 @@ import { DEFAULT_API_HOST, toApiHost } from '../host-utils.js';
2
2
  import { getDebugLogger } from '../lib/debug-logger.js';
3
3
  import { buildUserAgent } from '../lib/user-agent.js';
4
4
  export const DEFAULT_HOST = DEFAULT_API_HOST;
5
- // Phase-prefix constants used at the two ApiError throw sites below and
6
- // imported by orchestrator.ts to identify preview-phase 422s for retry.
7
5
  export const PREVIEW_ERROR_PREFIX = 'preview failed:';
8
6
  export const APPLY_ERROR_PREFIX = 'apply failed:';
9
7
  // Substring match the orchestrator uses to distinguish a parseable
@@ -31,9 +29,6 @@ export class ApiError extends Error {
31
29
  this.status = status;
32
30
  this.body = body;
33
31
  if (body) {
34
- // Append a (possibly trimmed) version of the response body so callers
35
- // that only log e.message don't silently swallow the server's error
36
- // detail.
37
32
  const trimmed = body.length > ERROR_BODY_LOG_CAP ? body.slice(0, ERROR_BODY_LOG_CAP) + '…' : body;
38
33
  this.message = `${message}\n${trimmed}`;
39
34
  }
@@ -83,6 +78,45 @@ export function parsePreviewValidationErrors(body) {
83
78
  }
84
79
  return out;
85
80
  }
81
+ const PROPERTY_BREAKING_REASONS = new Set([
82
+ 'removed',
83
+ 'added_required_no_default',
84
+ 'type_changed',
85
+ 'validation_narrowed',
86
+ ]);
87
+ const SLOT_BREAKING_REASONS = new Set(['slot_removed', 'slot_allowed_components_narrowed']);
88
+ export function sanitizeBreakingChanges(raw) {
89
+ if (!Array.isArray(raw))
90
+ return [];
91
+ const out = [];
92
+ for (const bc of raw) {
93
+ if (typeof bc !== 'object' || bc === null)
94
+ continue;
95
+ const reason = bc.reason;
96
+ if (typeof reason !== 'string')
97
+ continue;
98
+ if ('propertyId' in bc && typeof bc.propertyId === 'string') {
99
+ if (PROPERTY_BREAKING_REASONS.has(reason))
100
+ out.push(bc);
101
+ continue;
102
+ }
103
+ if ('slotId' in bc && typeof bc.slotId === 'string') {
104
+ if (SLOT_BREAKING_REASONS.has(reason))
105
+ out.push(bc);
106
+ continue;
107
+ }
108
+ }
109
+ return out;
110
+ }
111
+ function sanitizePreviewResponse(res) {
112
+ for (const item of res.components?.changed ?? []) {
113
+ const cc = item.changeClassification;
114
+ if (cc && Array.isArray(cc.breakingChanges)) {
115
+ cc.breakingChanges = sanitizeBreakingChanges(cc.breakingChanges);
116
+ }
117
+ }
118
+ return res;
119
+ }
86
120
  async function request(url, options) {
87
121
  const headers = {
88
122
  Authorization: `Bearer ${options.token}`,
@@ -155,7 +189,7 @@ export class ImportApiClient {
155
189
  }
156
190
  const parsed = (await res.json());
157
191
  debug.event('apply', 'preview.ok', { status: res.status, durationMs: Date.now() - startedAt });
158
- return parsed;
192
+ return sanitizePreviewResponse(parsed);
159
193
  }
160
194
  async applyImport(manifest, acknowledgeBreakingChanges) {
161
195
  const url = `${this.base()}/design_systems/imports/apply`;
@@ -1,6 +1,22 @@
1
1
  import type { Command } from 'commander';
2
- import type { DTCGTokenEntry } from '@contentful/experience-design-system-types';
2
+ import type { CDFComponentEntry, DTCGTokenEntry } from '@contentful/experience-design-system-types';
3
+ import { findSlotCycles } from '../analyze/cycle-detection.js';
3
4
  import type { ServerPreviewResponse } from '@contentful/experience-design-system-types';
4
5
  export declare function readTokensFromPath(flag: string, p: string): Promise<DTCGTokenEntry[]>;
6
+ export declare function detectSlotCycles(components: Array<{
7
+ key: string;
8
+ entry: CDFComponentEntry;
9
+ }>): ReturnType<typeof findSlotCycles>;
10
+ export declare function formatSlotCycleReport(cycles: ReturnType<typeof findSlotCycles>): string[];
11
+ export declare function assertNoSlotCycles(components: Array<{
12
+ key: string;
13
+ entry: CDFComponentEntry;
14
+ }>): void;
15
+ export declare function extractComponentsFromManifest(manifest: {
16
+ componentsManifest?: Record<string, unknown>;
17
+ } | null | undefined): Array<{
18
+ key: string;
19
+ entry: CDFComponentEntry;
20
+ }>;
5
21
  export declare function hasBreakingChangesWithImpact(preview: ServerPreviewResponse): boolean;
6
22
  export declare function registerApplyCommand(program: Command): void;