@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
@@ -0,0 +1,56 @@
1
+ export function computeDirectNeighborhood(matches, graph) {
2
+ const matchSet = new Set(matches);
3
+ const out = new Set(matchSet);
4
+ if (matchSet.size === 0)
5
+ return out;
6
+ for (const node of graph) {
7
+ const targets = [];
8
+ for (const slot of node.slots) {
9
+ for (const t of slot.allowedComponents ?? [])
10
+ targets.push(t);
11
+ }
12
+ if (matchSet.has(node.name)) {
13
+ for (const t of targets)
14
+ out.add(t);
15
+ }
16
+ for (const t of targets) {
17
+ if (matchSet.has(t)) {
18
+ out.add(node.name);
19
+ break;
20
+ }
21
+ }
22
+ }
23
+ return out;
24
+ }
25
+ export function findAllAncestors(target, graph) {
26
+ const out = new Set([target]);
27
+ const parentsOf = new Map();
28
+ for (const node of graph) {
29
+ for (const slot of node.slots) {
30
+ for (const child of slot.allowedComponents ?? []) {
31
+ const list = parentsOf.get(child);
32
+ if (list) {
33
+ if (!list.includes(node.name))
34
+ list.push(node.name);
35
+ }
36
+ else {
37
+ parentsOf.set(child, [node.name]);
38
+ }
39
+ }
40
+ }
41
+ }
42
+ const queue = [target];
43
+ while (queue.length > 0) {
44
+ const cur = queue.shift();
45
+ const parents = parentsOf.get(cur);
46
+ if (!parents)
47
+ continue;
48
+ for (const p of parents) {
49
+ if (out.has(p))
50
+ continue;
51
+ out.add(p);
52
+ queue.push(p);
53
+ }
54
+ }
55
+ return out;
56
+ }
@@ -1,5 +1,6 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
3
+ import { PALETTE } from './theme.js';
3
4
  import { Box, Text, useStdout } from 'ink';
4
5
  import { readFile } from 'node:fs/promises';
5
6
  import { createReviewSessionDetail } from '../types.js';
@@ -139,7 +140,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
139
140
  clearTimeout(previewDebounceRef.current);
140
141
  };
141
142
  }, []);
142
- // Sync loaded session into local state
143
143
  useEffect(() => {
144
144
  if (loadedSession && !session) {
145
145
  setSession(loadedSession);
@@ -173,7 +173,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
173
173
  }
174
174
  }
175
175
  }, [session]);
176
- // Lazy source code loading
177
176
  useEffect(() => {
178
177
  if (!session || !selectedId)
179
178
  return;
@@ -195,7 +194,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
195
194
  // Leave as null; SourcePanel shows [No source available]
196
195
  });
197
196
  }, [selectedId]);
198
- // SIGINT handler
199
197
  useEffect(() => {
200
198
  const handler = () => {
201
199
  if (Object.keys(draftsByComponentId).length > 0) {
@@ -316,7 +314,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
316
314
  },
317
315
  onToggleHelp: () => setShowHelp((prev) => !prev),
318
316
  });
319
- // Must be before early returns — Rules of Hooks
320
317
  const sessionSummary = useMemo(() => sortComponentsForSidebar((session?.components ?? []).map((c) => {
321
318
  const counts = countValidationIssues(c.originalProposal);
322
319
  return {
@@ -334,10 +331,10 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
334
331
  return _jsx(Text, { children: "Loading session..." });
335
332
  }
336
333
  if (sessionError) {
337
- return (_jsxs(Text, { color: "red", children: [sessionError, '\nPress q to exit.'] }));
334
+ return (_jsxs(Text, { color: PALETTE.error, children: [sessionError, '\nPress q to exit.'] }));
338
335
  }
339
336
  if (!session || !paths) {
340
- return _jsx(Text, { color: "red", children: "Session unavailable." });
337
+ return _jsx(Text, { color: PALETTE.error, children: "Session unavailable." });
341
338
  }
342
339
  if (finalizedResult) {
343
340
  return _jsx(FinalizedScreen, { result: finalizedResult });
@@ -363,10 +360,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
363
360
  const collapsed = terminalWidth < 80;
364
361
  const visibleCount = 20;
365
362
  const longestName = session.components.reduce((max, c) => Math.max(max, c.name.length), 0);
366
- // icon + badge + space + name + 2 border chars; min 14, max 22.
367
- // The badge column is reserved (pilot R2) even when no annotation is set —
368
- // see Sidebar.previewBadge — so the column width is stable as live-preview
369
- // annotations flip in/out.
370
363
  const sidebarWidth = collapsed ? 3 : Math.min(Math.max(longestName + 5, 14), 22);
371
364
  const handleDraftSave = async (draft) => {
372
365
  if (!selectedId || !session || !paths)
@@ -397,7 +390,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
397
390
  type: 'draft_saved',
398
391
  payload: { componentId: selectedId },
399
392
  });
400
- // Sync all edited proposals to pipeline DB (keep 'generated' for preview)
401
393
  const allEdited = updatedSession.components.map((c) => c.editedProposal);
402
394
  const syncDb = openPipelineDb();
403
395
  try {
@@ -434,9 +426,6 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
434
426
  excluded: needsReviewCount,
435
427
  },
436
428
  });
437
- // Write all components back to DB, marking accepted as 'generated'
438
- // so loadCDFComponents (push/preview) only picks up accepted ones,
439
- // but loadRawComponents (editor re-entry) still finds all of them
440
429
  const acceptedNames = new Set(session.components.filter((c) => c.status === 'accepted').map((c) => c.name));
441
430
  const db = openPipelineDb();
442
431
  try {
@@ -479,7 +468,7 @@ export function App({ sessionId, artifactsRoot, reviewRoot }) {
479
468
  }, onCancel: () => setShowQuitDialog(false) })), !showHelp && !showFinalizeDialog && !showQuitDialog && (_jsxs(Box, { flexGrow: 1, children: [_jsx(Sidebar, { components: sessionSummary, selectedId: selectedId, focused: sidebarFocused, scrollOffset: sidebarScrollOffset, visibleCount: visibleCount, onSelect: (id) => {
480
469
  setSelectedId(id);
481
470
  setJsonScrollOffset(0);
482
- }, onScrollChange: setSidebarScrollOffset, collapsed: collapsed, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: selectedDetail ? (_jsx(ComponentDetail, { component: selectedDetail, sourceCode: selectedRecord?.sourceCode ?? null, draftValue: selectedId ? (draftsByComponentId[selectedId] ?? '') : '', editMode: editMode, sourceVisible: sourceVisible, jsonScrollOffset: jsonScrollOffset, sourceScrollX: 0, sourceScrollY: 0, terminalWidth: terminalWidth, previewAnnotation: selectedRecord ? previewAnnotations[selectedRecord.name] : undefined, onSaveDraft: (value) => void handleDraftSave(value), onDiscardDraft: handleDraftDiscard, onScrollChange: setJsonScrollOffset })) : (_jsx(Text, { dimColor: true, children: "No component selected" })) })] })), _jsx(PreviewSummaryBar, { preview: previewResponse, loading: previewLoading }), previewError && _jsx(Text, { color: "yellow", children: '⚠ Preview: ' + previewError }), saveError && _jsx(Text, { color: "red", children: '⚠ ' + saveError }), !dialogOpen && (_jsx(StatusBar, { accepted: acceptedCount, rejected: rejectedCount, reviewed: reviewedCount, needsReview: needsReviewCount, onApproveAll: () => { }, onFinalize: () => setShowFinalizeDialog(true) }))] }));
471
+ }, onScrollChange: setSidebarScrollOffset, collapsed: collapsed, width: sidebarWidth }), _jsx(Box, { flexGrow: 1, paddingLeft: 1, children: selectedDetail ? (_jsx(ComponentDetail, { component: selectedDetail, sourceCode: selectedRecord?.sourceCode ?? null, draftValue: selectedId ? (draftsByComponentId[selectedId] ?? '') : '', editMode: editMode, sourceVisible: sourceVisible, jsonScrollOffset: jsonScrollOffset, sourceScrollX: 0, sourceScrollY: 0, terminalWidth: terminalWidth, previewAnnotation: selectedRecord ? previewAnnotations[selectedRecord.name] : undefined, onSaveDraft: (value) => void handleDraftSave(value), onDiscardDraft: handleDraftDiscard, onScrollChange: setJsonScrollOffset })) : (_jsx(Text, { dimColor: true, children: "No component selected" })) })] })), _jsx(PreviewSummaryBar, { preview: previewResponse, loading: previewLoading }), previewError && _jsx(Text, { color: PALETTE.warning, children: '⚠ Preview: ' + previewError }), saveError && _jsx(Text, { color: PALETTE.error, children: '⚠ ' + saveError }), !dialogOpen && (_jsx(StatusBar, { accepted: acceptedCount, rejected: rejectedCount, reviewed: reviewedCount, needsReview: needsReviewCount, onApproveAll: () => { }, onFinalize: () => setShowFinalizeDialog(true) }))] }));
483
472
  }
484
473
  function FinalizedScreen({ result, }) {
485
474
  useImmediateInput((_input, key) => {
@@ -487,5 +476,5 @@ function FinalizedScreen({ result, }) {
487
476
  process.exit(0);
488
477
  }
489
478
  });
490
- return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, gap: 1, children: [_jsx(Text, { bold: true, color: "green", children: "\u2713 Finalized" }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "green", children: "\u2713" }), _jsxs(Text, { children: [result.accepted, " accepted"] })] }), result.rejected > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: "red", children: "\u2717" }), _jsxs(Text, { children: [result.rejected, " rejected"] })] })), result.excluded > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "\u00B7" }), _jsxs(Text, { dimColor: true, children: [result.excluded, " excluded (unresolved)"] })] }))] }), _jsx(Text, { dimColor: true, children: "Decisions saved. Ready for the next step." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[Enter / q] Exit" }) })] }));
479
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, paddingY: 1, gap: 1, children: [_jsx(Text, { bold: true, color: PALETTE.success, children: "\u2713 Finalized" }), _jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.success, children: "\u2713" }), _jsxs(Text, { children: [result.accepted, " accepted"] })] }), result.rejected > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { color: PALETTE.error, children: "\u2717" }), _jsxs(Text, { children: [result.rejected, " rejected"] })] })), result.excluded > 0 && (_jsxs(Box, { gap: 1, children: [_jsx(Text, { dimColor: true, children: "\u00B7" }), _jsxs(Text, { dimColor: true, children: [result.excluded, " excluded (unresolved)"] })] }))] }), _jsx(Text, { dimColor: true, children: "Decisions saved. Ready for the next step." }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[Enter / q] Exit" }) })] }));
491
480
  }
@@ -21,10 +21,6 @@ type RenderedLine = {
21
21
  } | {
22
22
  kind: 'blank';
23
23
  };
24
- /**
25
- * Flatten the component rationale into a list of rendered lines so scrollOffset
26
- * slicing matches what the operator sees.
27
- */
28
24
  export declare function renderComponentRationaleLines(data: ComponentRationale, innerWidth: number): RenderedLine[];
29
25
  export declare function ComponentRationalePanel({ data, scrollOffset, width, height, active, }: ComponentRationalePanelProps): React.ReactElement;
30
26
  export {};
@@ -1,10 +1,7 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { PALETTE } from '../theme.js';
2
3
  import { Box, Text } from 'ink';
3
4
  const PLACEHOLDER = '(no rationale captured)';
4
- /**
5
- * Word-wrap a paragraph to fit `innerWidth` columns. Mirrors the helper in
6
- * RationalePanel.tsx so wrapping is identical across both panels.
7
- */
8
5
  function wrapText(text, innerWidth) {
9
6
  if (!text)
10
7
  return [''];
@@ -51,10 +48,6 @@ function wrapText(text, innerWidth) {
51
48
  lines.push(current);
52
49
  return lines.length > 0 ? lines : [''];
53
50
  }
54
- /**
55
- * Flatten the component rationale into a list of rendered lines so scrollOffset
56
- * slicing matches what the operator sees.
57
- */
58
51
  export function renderComponentRationaleLines(data, innerWidth) {
59
52
  const out = [];
60
53
  const pushSection = (heading, body) => {
@@ -65,10 +58,6 @@ export function renderComponentRationaleLines(data, innerWidth) {
65
58
  }
66
59
  out.push({ kind: 'blank' });
67
60
  };
68
- // "Description" section shows the human-facing component description.
69
- // The `descriptionRationale` (WHY the component was classified this way)
70
- // is appended as a small "why" line so the operator can see the LLM's
71
- // reasoning without leaving the panel.
72
61
  out.push({ kind: 'heading', text: 'Description' });
73
62
  const descBody = data.description && data.description.trim().length > 0 ? data.description : PLACEHOLDER;
74
63
  for (const ln of wrapText(descBody, Math.max(1, innerWidth - 2))) {
@@ -116,18 +105,17 @@ export function ComponentRationalePanel({ data, scrollOffset, width, height, act
116
105
  const innerWidth = Math.max(1, width - 2);
117
106
  const all = renderComponentRationaleLines(data, innerWidth);
118
107
  const totalLines = all.length;
119
- // Reserve one line at the bottom for the legend.
120
108
  const contentHeight = Math.max(1, height - 1);
121
109
  const visible = all.slice(scrollOffset, scrollOffset + contentHeight);
122
110
  const overflowed = totalLines > contentHeight;
123
111
  const visibleStart = totalLines === 0 ? 0 : scrollOffset + 1;
124
112
  const visibleEnd = Math.min(totalLines, scrollOffset + contentHeight);
125
- return (_jsxs(Box, { flexDirection: "column", width: width, height: height + 2, borderStyle: "single", borderColor: active ? 'white' : undefined, children: [_jsx(Box, { children: _jsx(Text, { bold: true, dimColor: !active, children: `Component rationale: ${data.name}` }) }), visible.map((line, i) => {
113
+ return (_jsxs(Box, { flexDirection: "column", width: width, height: height + 2, borderStyle: "single", borderColor: active ? PALETTE.inverse : undefined, children: [_jsx(Box, { children: _jsx(Text, { bold: true, dimColor: !active, children: `Component rationale: ${data.name}` }) }), visible.map((line, i) => {
126
114
  if (line.kind === 'blank') {
127
115
  return (_jsx(Box, { children: _jsx(Text, { children: " " }) }, i));
128
116
  }
129
117
  if (line.kind === 'heading') {
130
- return (_jsx(Box, { children: _jsx(Text, { bold: true, color: "cyan", dimColor: !active, children: line.text }) }, i));
118
+ return (_jsx(Box, { children: _jsx(Text, { bold: true, color: PALETTE.info, dimColor: !active, children: line.text }) }, i));
131
119
  }
132
120
  if (line.kind === 'list-name') {
133
121
  return (_jsxs(Box, { children: [_jsx(Text, { children: ' - ' }), _jsx(Text, { bold: true, dimColor: !active, children: line.text }), line.sublabel ? _jsx(Text, { dimColor: true, children: ' ' + line.sublabel }) : null] }, i));
@@ -1,7 +1,5 @@
1
1
  import React from 'react';
2
- /** Per-prop / per-component metadata captured by the extractor + generate phase.
3
- * Optional; when omitted, the FieldEditor renders without rationale/source
4
- * affordances (backwards-compatible with mounts that pre-date Feature 1). */
2
+ import { findSlotCycles, type ComponentSlotInfo } from '../../../cycle-detection.js';
5
3
  export type PropMetadata = {
6
4
  rationale?: string | null;
7
5
  sourceStartLine?: number | null;
@@ -19,50 +17,43 @@ type FieldEditorProps = {
19
17
  value: string;
20
18
  width: number;
21
19
  height: number;
22
- /**
23
- * When false, the editor is mounted but does not consume keystrokes.
24
- * Used by callers (e.g. GenerateReviewStep) that toggle focus between
25
- * a sidebar and this editor — the editor stays visible while the sidebar
26
- * has the keyboard.
27
- */
28
20
  active?: boolean;
29
21
  onChange: (value: string) => void;
30
22
  onSave: () => void;
31
23
  onDiscard: () => void;
32
- /**
33
- * Called when the user requests to exit the panel from row-level (Esc).
34
- * Distinct from onDiscard, which drops pending edits without changing focus.
35
- * Callers that embed FieldEditor inside a sidebar+panel layout wire this to
36
- * return focus to the sidebar (e.g. setSidebarFocused(true)). When omitted,
37
- * row-level Esc falls back to onDiscard for backward compatibility.
38
- */
39
24
  onExit?: () => void;
40
- /** Feature 1: source code + LLM rationale metadata. Optional. */
41
25
  metadata?: FieldEditorMetadata;
42
- /**
43
- * When provided, the parent owns the prop-rationale panel: pressing `i`
44
- * inside FieldEditor calls this callback (subject to text-entry gating)
45
- * instead of toggling internal state. The panel is rendered by the parent
46
- * in place of FieldEditor — FieldEditor no longer renders its own panel.
47
- */
48
26
  onTogglePropRationale?: () => void;
49
- /**
50
- * When provided, the parent owns the component-rationale panel: pressing
51
- * `I` (uppercase) calls this callback (subject to text-entry gating).
52
- */
27
+ propRationaleKey?: string;
53
28
  onToggleComponentRationale?: () => void;
54
- /**
55
- * When provided, the parent owns the source-view panel: pressing `s`
56
- * calls this callback (subject to text-entry gating). FieldEditor will not
57
- * render its own source panel in that case.
58
- */
29
+ componentRationaleKey?: string;
59
30
  onToggleSourceExternal?: () => void;
60
- /**
61
- * Reports text-entry-active state changes to the parent so the parent can
62
- * gate top-level keybinds (i/I/s) against literal keystrokes inside
63
- * description editors, string default editors, and value-list text entry.
64
- */
65
31
  onTextEntryActiveChange?: (active: boolean) => void;
32
+ projectSlotGraph?: ComponentSlotInfo[];
33
+ currentComponentName?: string;
34
+ onDirtyChange?: (isDirty: boolean) => void;
35
+ discardTrigger?: number;
36
+ initialFocusTarget?: {
37
+ kind: 'prop' | 'slot';
38
+ name: string;
39
+ };
66
40
  };
67
- export declare function FieldEditor({ value, width, height, active, onChange, onSave, onDiscard, onExit, metadata, onTogglePropRationale, onToggleComponentRationale, onToggleSourceExternal, onTextEntryActiveChange, }: FieldEditorProps): React.ReactElement;
41
+ export declare function simulateGraphWithCandidate(projectSlotGraph: ComponentSlotInfo[], selfName: string, currentSlots: {
42
+ name: string;
43
+ allowedComponents: string[];
44
+ }[], targetSlotName: string, candidate: string): ComponentSlotInfo[];
45
+ export declare function introducesNewCycle(before: ReturnType<typeof findSlotCycles>, next: ReturnType<typeof findSlotCycles>): boolean;
46
+ export declare function computeAllowedComponentCandidates(projectSlotGraph: ComponentSlotInfo[], selfName: string, currentSlots: {
47
+ name: string;
48
+ allowedComponents: string[];
49
+ }[], targetSlotName: string): string[];
50
+ export declare function simulateGraphWithReplacement(projectSlotGraph: ComponentSlotInfo[], selfName: string, currentSlots: {
51
+ name: string;
52
+ allowedComponents: string[];
53
+ }[], targetSlotName: string, replaceIndex: number, candidate: string): ComponentSlotInfo[];
54
+ export declare function computeAllowedComponentReplacementCandidates(projectSlotGraph: ComponentSlotInfo[], selfName: string, currentSlots: {
55
+ name: string;
56
+ allowedComponents: string[];
57
+ }[], targetSlotName: string, replaceIndex: number): string[];
58
+ export declare function FieldEditor({ value, width, height, active, onChange, onSave, onDiscard, onExit, metadata, onTogglePropRationale, propRationaleKey, onToggleComponentRationale, componentRationaleKey, onToggleSourceExternal, onTextEntryActiveChange, projectSlotGraph, currentComponentName, onDirtyChange, discardTrigger, initialFocusTarget, }: FieldEditorProps): React.ReactElement;
68
59
  export {};