@design-ai/cli 4.55.0 → 4.57.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 (301) hide show
  1. package/.claude-plugin/plugin.json +109 -25
  2. package/CHANGELOG.md +75 -0
  3. package/README.ko.md +15 -32
  4. package/README.md +18 -31
  5. package/cli/bin/design-ai.mjs +1 -0
  6. package/cli/commands/help.mjs +7 -4
  7. package/cli/commands/index.mjs +292 -0
  8. package/cli/commands/learn-help.mjs +149 -0
  9. package/cli/commands/learn-print-profile.mjs +373 -0
  10. package/cli/commands/learn-print-restore.mjs +349 -0
  11. package/cli/commands/learn-print-signals.mjs +453 -0
  12. package/cli/commands/learn.mjs +60 -1281
  13. package/cli/commands/pack.mjs +7 -3
  14. package/cli/commands/prompt.mjs +7 -3
  15. package/cli/commands/search.mjs +94 -1
  16. package/cli/lib/dispatch.mjs +3 -0
  17. package/cli/lib/embedding-index.mjs +199 -0
  18. package/cli/lib/embedding-provider.mjs +121 -0
  19. package/cli/lib/embedding-rerank.mjs +52 -0
  20. package/cli/lib/learn-args.mjs +490 -0
  21. package/cli/lib/learn-backup.mjs +748 -0
  22. package/cli/lib/learn-curation.mjs +612 -0
  23. package/cli/lib/learn-eval.mjs +459 -0
  24. package/cli/lib/learn-profile.mjs +763 -0
  25. package/cli/lib/learn-select.mjs +223 -0
  26. package/cli/lib/learn-shared.mjs +158 -0
  27. package/cli/lib/learn-test-support.mjs +218 -0
  28. package/cli/lib/learn-usage.mjs +360 -0
  29. package/cli/lib/learn.mjs +83 -3694
  30. package/cli/lib/lexical.mjs +137 -0
  31. package/cli/lib/local-config.mjs +110 -0
  32. package/cli/lib/mcp-server.mjs +221 -13
  33. package/cli/lib/pack.mjs +31 -4
  34. package/cli/lib/prompt.mjs +42 -4
  35. package/cli/lib/recall.mjs +193 -0
  36. package/cli/lib/retrieval-index.mjs +0 -0
  37. package/cli/lib/search-ranked.mjs +170 -0
  38. package/cli/lib/search.mjs +11 -1
  39. package/cli/lib/signals-backlog-commands.mjs +673 -0
  40. package/cli/lib/signals-backlog.mjs +361 -0
  41. package/cli/lib/signals-eval.mjs +176 -0
  42. package/cli/lib/signals-readiness.mjs +247 -0
  43. package/cli/lib/signals-registry.mjs +377 -0
  44. package/cli/lib/signals-render.mjs +478 -0
  45. package/cli/lib/signals-shared.mjs +75 -0
  46. package/cli/lib/signals.mjs +16 -2337
  47. package/cli/lib/site-analysis.mjs +297 -0
  48. package/cli/lib/site-args.mjs +433 -0
  49. package/cli/lib/site-bundle-build.mjs +127 -0
  50. package/cli/lib/site-bundle-check.mjs +454 -0
  51. package/cli/lib/site-bundle-commands.mjs +95 -0
  52. package/cli/lib/site-bundle-compare.mjs +157 -0
  53. package/cli/lib/site-bundle-contract.mjs +79 -0
  54. package/cli/lib/site-bundle-files.mjs +87 -0
  55. package/cli/lib/site-bundle-handoff-expected.mjs +173 -0
  56. package/cli/lib/site-bundle-handoff-runbook-action-summary.mjs +332 -0
  57. package/cli/lib/site-bundle-handoff-runbook-actions.mjs +113 -0
  58. package/cli/lib/site-bundle-handoff-runbook-evidence-fields.mjs +164 -0
  59. package/cli/lib/site-bundle-handoff-runbook-evidence.mjs +334 -0
  60. package/cli/lib/site-bundle-handoff-runbook-format.mjs +31 -0
  61. package/cli/lib/site-bundle-handoff-runbook-human-lines.mjs +167 -0
  62. package/cli/lib/site-bundle-handoff-runbook-maps.mjs +238 -0
  63. package/cli/lib/site-bundle-handoff-runbook-next-step.mjs +278 -0
  64. package/cli/lib/site-bundle-handoff-runbook-stage-metadata.mjs +84 -0
  65. package/cli/lib/site-bundle-handoff-runbook.mjs +444 -0
  66. package/cli/lib/site-bundle-handoff-summary.mjs +183 -0
  67. package/cli/lib/site-bundle-handoff.mjs +271 -0
  68. package/cli/lib/site-bundle-readme.mjs +98 -0
  69. package/cli/lib/site-bundle-repair-report.mjs +143 -0
  70. package/cli/lib/site-bundle-repair.mjs +68 -0
  71. package/cli/lib/site-content.mjs +399 -0
  72. package/cli/lib/site-evidence.mjs +35 -0
  73. package/cli/lib/site-mcp-commands.mjs +28 -0
  74. package/cli/lib/site-mcp-probes.mjs +159 -0
  75. package/cli/lib/site-mcp-readiness.mjs +157 -0
  76. package/cli/lib/site-mcp-report.mjs +324 -0
  77. package/cli/lib/site-next-actions.mjs +333 -0
  78. package/cli/lib/site-options.mjs +104 -0
  79. package/cli/lib/site-prompts.mjs +332 -0
  80. package/cli/lib/site-starter.mjs +153 -0
  81. package/cli/lib/site-strings.mjs +23 -0
  82. package/cli/lib/site-tasks.mjs +93 -0
  83. package/cli/lib/site-test-support.mjs +68 -0
  84. package/cli/lib/site-workflow-graph.mjs +309 -0
  85. package/cli/lib/site-workspace.mjs +492 -0
  86. package/cli/lib/site.mjs +108 -6617
  87. package/cli/lib/skill-proposals-apply-commands.mjs +135 -0
  88. package/cli/lib/skill-proposals-apply-contract.mjs +750 -0
  89. package/cli/lib/skill-proposals-apply-plan.mjs +118 -0
  90. package/cli/lib/skill-proposals-generate.mjs +298 -0
  91. package/cli/lib/skill-proposals-render.mjs +532 -0
  92. package/cli/lib/skill-proposals-review.mjs +262 -0
  93. package/cli/lib/skill-proposals.mjs +15 -2046
  94. package/cli/lib/workspace-args.mjs +83 -0
  95. package/cli/lib/workspace-git.mjs +169 -0
  96. package/cli/lib/workspace-learning.mjs +483 -0
  97. package/cli/lib/workspace-repo.mjs +139 -0
  98. package/cli/lib/workspace-report.mjs +283 -0
  99. package/cli/lib/workspace-test-support.mjs +99 -0
  100. package/cli/lib/workspace.mjs +30 -1056
  101. package/docs/AI-LEARNING-PHASE2.md +215 -0
  102. package/docs/DISTRIBUTION.ko.md +35 -6
  103. package/docs/DISTRIBUTION.md +35 -8
  104. package/docs/NEXT-SURFACE-DECISION.md +125 -0
  105. package/docs/PRODUCT-READINESS.md +3 -3
  106. package/docs/RELEASE-CHECKLIST.md +20 -3
  107. package/docs/RELEASE-GATES.ko.md +35 -0
  108. package/docs/RELEASE-GATES.md +234 -0
  109. package/docs/ROADMAP.md +2232 -8
  110. package/docs/external-status.md +20 -7
  111. package/docs/inspection-20260630.md +169 -0
  112. package/docs/integrations/design-ai-mcp-server.md +35 -3
  113. package/docs/integrations/vscode-walkthrough.ko.md +3 -3
  114. package/docs/integrations/vscode-walkthrough.md +3 -3
  115. package/docs/reference/ant-design.md +413 -0
  116. package/docs/reference/awesome-design-md.md +439 -0
  117. package/docs/reference/mui.md +783 -0
  118. package/docs/reference/shadcn-ui.md +298 -0
  119. package/docs/site-overrides/main.html +1 -1
  120. package/examples/component-accordion-actions.md +5 -5
  121. package/examples/component-accordion-details.md +5 -5
  122. package/examples/component-accordion-summary.md +5 -5
  123. package/examples/component-accordion.md +3 -3
  124. package/examples/component-affix.md +1 -1
  125. package/examples/component-alert-dialog.md +1 -1
  126. package/examples/component-alert-title.md +1 -1
  127. package/examples/component-alert.md +3 -3
  128. package/examples/component-anchor.md +1 -1
  129. package/examples/component-app-bar.md +1 -1
  130. package/examples/component-aspect-ratio.md +1 -1
  131. package/examples/component-auto-complete.md +2 -2
  132. package/examples/component-avatar-group.md +5 -5
  133. package/examples/component-avatar.md +3 -3
  134. package/examples/component-back-top.md +1 -1
  135. package/examples/component-backdrop.md +1 -1
  136. package/examples/component-badge.md +3 -3
  137. package/examples/component-border-beam.md +3 -3
  138. package/examples/component-bottom-navigation.md +1 -1
  139. package/examples/component-box.md +1 -1
  140. package/examples/component-breadcrumb.md +3 -3
  141. package/examples/component-button-base.md +3 -3
  142. package/examples/component-button-group.md +2 -2
  143. package/examples/component-button.md +3 -3
  144. package/examples/component-calendar.md +2 -2
  145. package/examples/component-card-actions.md +1 -1
  146. package/examples/component-card-content.md +1 -1
  147. package/examples/component-card-header.md +1 -1
  148. package/examples/component-card-media.md +1 -1
  149. package/examples/component-card.md +3 -3
  150. package/examples/component-carousel.md +2 -2
  151. package/examples/component-cascader.md +1 -1
  152. package/examples/component-chart.md +1 -1
  153. package/examples/component-checkbox.md +3 -3
  154. package/examples/component-click-away-listener.md +1 -1
  155. package/examples/component-code.md +1 -1
  156. package/examples/component-collapsible.md +1 -1
  157. package/examples/component-color-picker.md +1 -1
  158. package/examples/component-combobox.md +1 -1
  159. package/examples/component-command.md +1 -1
  160. package/examples/component-config-provider.md +3 -3
  161. package/examples/component-context-menu.md +1 -1
  162. package/examples/component-css-baseline.md +3 -3
  163. package/examples/component-date-picker.md +2 -2
  164. package/examples/component-descriptions.md +1 -1
  165. package/examples/component-dialog-actions.md +1 -1
  166. package/examples/component-dialog-content-text.md +1 -1
  167. package/examples/component-dialog-content.md +1 -1
  168. package/examples/component-dialog-title.md +1 -1
  169. package/examples/component-dialog.md +2 -2
  170. package/examples/component-divider.md +3 -3
  171. package/examples/component-drawer.md +3 -3
  172. package/examples/component-dropdown.md +3 -3
  173. package/examples/component-empty.md +2 -2
  174. package/examples/component-fade.md +1 -1
  175. package/examples/component-field.md +1 -1
  176. package/examples/component-filled-input.md +1 -1
  177. package/examples/component-flex.md +1 -1
  178. package/examples/component-float-button.md +2 -2
  179. package/examples/component-form-control-label.md +1 -1
  180. package/examples/component-form-control.md +1 -1
  181. package/examples/component-form-controls.md +1 -1
  182. package/examples/component-form-group.md +1 -1
  183. package/examples/component-form-helper-text.md +1 -1
  184. package/examples/component-form-label.md +1 -1
  185. package/examples/component-form.md +2 -2
  186. package/examples/component-grid.md +2 -2
  187. package/examples/component-grow.md +1 -1
  188. package/examples/component-hover-card.md +1 -1
  189. package/examples/component-icon-button.md +1 -1
  190. package/examples/component-icon.md +1 -1
  191. package/examples/component-image-list.md +1 -1
  192. package/examples/component-image.md +1 -1
  193. package/examples/component-input-adornment.md +1 -1
  194. package/examples/component-input-base.md +1 -1
  195. package/examples/component-input-number.md +1 -1
  196. package/examples/component-input-otp.md +2 -2
  197. package/examples/component-input.md +3 -3
  198. package/examples/component-item.md +1 -1
  199. package/examples/component-label.md +1 -1
  200. package/examples/component-layout.md +1 -1
  201. package/examples/component-link.md +1 -1
  202. package/examples/component-list-item-avatar.md +1 -1
  203. package/examples/component-list-item-button.md +1 -1
  204. package/examples/component-list-item-icon.md +1 -1
  205. package/examples/component-list-item-text.md +1 -1
  206. package/examples/component-list-item.md +1 -1
  207. package/examples/component-list-subheader.md +1 -1
  208. package/examples/component-list.md +2 -2
  209. package/examples/component-masonry.md +1 -1
  210. package/examples/component-mentions.md +1 -1
  211. package/examples/component-menu-item.md +1 -1
  212. package/examples/component-menu-list.md +1 -1
  213. package/examples/component-menu.md +1 -1
  214. package/examples/component-menubar.md +1 -1
  215. package/examples/component-message.md +1 -1
  216. package/examples/component-mobile-stepper.md +1 -1
  217. package/examples/component-modal.md +5 -5
  218. package/examples/component-navigation-menu.md +1 -1
  219. package/examples/component-notification.md +1 -1
  220. package/examples/component-outlined-input.md +1 -1
  221. package/examples/component-pagination.md +3 -3
  222. package/examples/component-paper.md +1 -1
  223. package/examples/component-popconfirm.md +1 -1
  224. package/examples/component-popover.md +3 -3
  225. package/examples/component-popper.md +1 -1
  226. package/examples/component-progress.md +2 -2
  227. package/examples/component-qr-code.md +1 -1
  228. package/examples/component-radio.md +3 -3
  229. package/examples/component-rate.md +2 -2
  230. package/examples/component-resizable.md +1 -1
  231. package/examples/component-result.md +1 -1
  232. package/examples/component-scroll-area.md +1 -1
  233. package/examples/component-segmented.md +3 -3
  234. package/examples/component-select.md +3 -3
  235. package/examples/component-separator.md +1 -1
  236. package/examples/component-sheet.md +1 -1
  237. package/examples/component-sidebar.md +1 -1
  238. package/examples/component-skeleton.md +3 -3
  239. package/examples/component-slide.md +1 -1
  240. package/examples/component-slider.md +3 -3
  241. package/examples/component-snackbar-content.md +1 -1
  242. package/examples/component-snackbar.md +1 -1
  243. package/examples/component-sonner.md +1 -1
  244. package/examples/component-space.md +1 -1
  245. package/examples/component-speed-dial-action.md +1 -1
  246. package/examples/component-speed-dial.md +1 -1
  247. package/examples/component-spin.md +2 -2
  248. package/examples/component-spinner.md +1 -1
  249. package/examples/component-splitter.md +2 -2
  250. package/examples/component-stack.md +1 -1
  251. package/examples/component-statistic.md +1 -1
  252. package/examples/component-step-button.md +4 -4
  253. package/examples/component-step-connector.md +4 -4
  254. package/examples/component-step-content.md +1 -1
  255. package/examples/component-step-icon.md +1 -1
  256. package/examples/component-step-label.md +1 -1
  257. package/examples/component-step.md +2 -2
  258. package/examples/component-steps.md +2 -2
  259. package/examples/component-swipeable-drawer.md +1 -1
  260. package/examples/component-switch.md +3 -3
  261. package/examples/component-tab-scroll-button.md +5 -5
  262. package/examples/component-tab.md +1 -1
  263. package/examples/component-table-body.md +1 -1
  264. package/examples/component-table-cell.md +1 -1
  265. package/examples/component-table-container.md +1 -1
  266. package/examples/component-table-footer.md +1 -1
  267. package/examples/component-table-head.md +1 -1
  268. package/examples/component-table-pagination.md +1 -1
  269. package/examples/component-table-row.md +1 -1
  270. package/examples/component-table-sort-label.md +1 -1
  271. package/examples/component-table.md +2 -2
  272. package/examples/component-tabs.md +3 -3
  273. package/examples/component-tag-badge.md +2 -2
  274. package/examples/component-tag.md +1 -1
  275. package/examples/component-textarea-autosize.md +1 -1
  276. package/examples/component-textarea.md +1 -1
  277. package/examples/component-time-picker.md +1 -1
  278. package/examples/component-timeline.md +1 -1
  279. package/examples/component-toast.md +4 -4
  280. package/examples/component-toggle-button.md +1 -1
  281. package/examples/component-toggle.md +1 -1
  282. package/examples/component-toolbar.md +1 -1
  283. package/examples/component-tooltip.md +3 -3
  284. package/examples/component-tour.md +1 -1
  285. package/examples/component-transfer.md +1 -1
  286. package/examples/component-tree-select.md +1 -1
  287. package/examples/component-tree.md +1 -1
  288. package/examples/component-typography.md +2 -2
  289. package/examples/component-upload.md +1 -1
  290. package/examples/component-watermark.md +1 -1
  291. package/examples/component-zoom.md +1 -1
  292. package/knowledge/COVERAGE.md +4 -3
  293. package/knowledge/components/INDEX.md +202 -202
  294. package/knowledge/patterns/brand-references.md +72 -72
  295. package/package.json +1 -1
  296. package/tools/audit/local-ci.py +16 -1
  297. package/tools/audit/package-smoke.py +717 -9
  298. package/tools/audit/registry-smoke.py +912 -10
  299. package/tools/audit/release-metadata.py +47 -1
  300. package/tools/audit/smoke_assertions.py +998 -35
  301. package/tools/migrations/refs-links-to-reference-pages.py +179 -0
@@ -0,0 +1,334 @@
1
+ // Evidence capture helpers for Website Improvement bundle handoff runbooks.
2
+
3
+ export function getEvidenceCaptureFieldValueShape(field) {
4
+ return {
5
+ textarea: "long-text",
6
+ text: "short-text",
7
+ "file-path": "file-path",
8
+ list: "string-list",
9
+ }[field.inputType] || "text";
10
+ }
11
+
12
+ export function getEvidenceCaptureFieldEmptyValue(field) {
13
+ return field.inputType === "list" ? [] : "";
14
+ }
15
+
16
+ export function getEvidenceCaptureFieldRequirementLabel(field) {
17
+ return field.required ? "Required" : "Optional";
18
+ }
19
+
20
+ export function getEvidenceCaptureFieldAriaLabel(field) {
21
+ return `${field.label} evidence (${field.required ? "required" : "optional"})`;
22
+ }
23
+
24
+ export function getEvidenceCaptureFieldHelpText(field) {
25
+ return field.validationHint || field.placeholder || "";
26
+ }
27
+
28
+ export function getEvidenceCaptureFieldSectionKey(field) {
29
+ return {
30
+ strictBundleCheckOutput: "source-bundle-verification",
31
+ bundleDigest: "source-bundle-verification",
32
+ handoffJsonSnapshot: "handoff-snapshot",
33
+ promptOutputFile: "handoff-prompt-output",
34
+ selectedTaskId: "handoff-prompt-output",
35
+ targetRepoChangedFiles: "target-repo-changes",
36
+ targetRepoVerificationResults: "target-repo-verification",
37
+ viewportAccessibilityNotes: "viewport-accessibility-qa",
38
+ finalEvidenceRecord: "final-handoff-evidence",
39
+ remainingRisks: "risk-record",
40
+ }[field.key] || "general-evidence";
41
+ }
42
+
43
+ export function getEvidenceCaptureFieldSectionLabel(field) {
44
+ return {
45
+ "source-bundle-verification": "Source bundle verification",
46
+ "handoff-snapshot": "Handoff snapshot",
47
+ "handoff-prompt-output": "Handoff prompt output",
48
+ "target-repo-changes": "Target repo changes",
49
+ "target-repo-verification": "Target repo verification",
50
+ "viewport-accessibility-qa": "Viewport and accessibility QA",
51
+ "final-handoff-evidence": "Final handoff evidence",
52
+ "risk-record": "Risk record",
53
+ "general-evidence": "General evidence",
54
+ }[getEvidenceCaptureFieldSectionKey(field)];
55
+ }
56
+
57
+ export function getEvidenceCaptureFieldPayloadNamespace(field) {
58
+ return {
59
+ strictBundleCheckOutput: "sourceBundle",
60
+ bundleDigest: "sourceBundle",
61
+ handoffJsonSnapshot: "handoffSnapshot",
62
+ promptOutputFile: "handoffPrompt",
63
+ selectedTaskId: "handoffPrompt",
64
+ targetRepoChangedFiles: "targetRepo",
65
+ targetRepoVerificationResults: "targetRepo",
66
+ viewportAccessibilityNotes: "targetRepo",
67
+ finalEvidenceRecord: "handoffEvidence",
68
+ remainingRisks: "handoffEvidence",
69
+ }[field.key] || "evidence";
70
+ }
71
+
72
+ export function getEvidenceCaptureFieldPayloadPath(field) {
73
+ return {
74
+ strictBundleCheckOutput: "sourceBundle.verification.strictBundleCheckOutput",
75
+ bundleDigest: "sourceBundle.verification.bundleDigest",
76
+ handoffJsonSnapshot: "handoffSnapshot.strictJson",
77
+ promptOutputFile: "handoffPrompt.outputFile",
78
+ selectedTaskId: "handoffPrompt.selectedTaskId",
79
+ targetRepoChangedFiles: "targetRepo.changedFiles",
80
+ targetRepoVerificationResults: "targetRepo.verificationResults",
81
+ viewportAccessibilityNotes: "targetRepo.viewportAccessibilityNotes",
82
+ finalEvidenceRecord: "handoffEvidence.finalRecord",
83
+ remainingRisks: "handoffEvidence.remainingRisks",
84
+ }[field.key] || `evidence.${field.key || "unknown"}`;
85
+ }
86
+
87
+ export function uniqueValues(values) {
88
+ return Array.from(new Set(values));
89
+ }
90
+
91
+ export function cloneEvidenceCaptureValue(value) {
92
+ return Array.isArray(value) ? [...value] : value;
93
+ }
94
+
95
+ function setPayloadTemplateValue(target, payloadPath, value) {
96
+ const pathParts = String(payloadPath || "").split(".").filter(Boolean);
97
+ if (pathParts.length === 0) {
98
+ return target;
99
+ }
100
+ let cursor = target;
101
+ pathParts.slice(0, -1).forEach((part) => {
102
+ if (!cursor[part] || typeof cursor[part] !== "object" || Array.isArray(cursor[part])) {
103
+ cursor[part] = {};
104
+ }
105
+ cursor = cursor[part];
106
+ });
107
+ cursor[pathParts[pathParts.length - 1]] = cloneEvidenceCaptureValue(value);
108
+ return target;
109
+ }
110
+
111
+ export function buildEvidenceCapturePayloadTemplate(fields) {
112
+ return fields.reduce(
113
+ (template, field) => setPayloadTemplateValue(template, field.payloadPath, field.emptyValue),
114
+ {},
115
+ );
116
+ }
117
+
118
+ export function buildEvidenceCapturePayloadFlatTemplate(fields) {
119
+ return Object.fromEntries(
120
+ fields.map((field) => [field.payloadPath, cloneEvidenceCaptureValue(field.emptyValue)]),
121
+ );
122
+ }
123
+
124
+ export function buildEvidenceCapturePayloadBindings(fields) {
125
+ return fields.map((field) => ({
126
+ key: field.key,
127
+ label: field.label,
128
+ payloadNamespace: field.payloadNamespace,
129
+ payloadPath: field.payloadPath,
130
+ inputType: field.inputType,
131
+ valueShape: field.valueShape,
132
+ acceptsMultiple: field.acceptsMultiple,
133
+ required: field.required,
134
+ requirementLabel: field.requirementLabel,
135
+ emptyValue: cloneEvidenceCaptureValue(field.emptyValue),
136
+ validationRule: field.validationRule,
137
+ minLength: field.minLength,
138
+ sectionKey: field.sectionKey,
139
+ sectionLabel: field.sectionLabel,
140
+ ariaLabel: field.ariaLabel,
141
+ }));
142
+ }
143
+
144
+ function getEvidenceCaptureValidationFailureMessage(field) {
145
+ return field.required
146
+ ? `Provide ${field.label.toLowerCase()} before marking this action complete.`
147
+ : `Optional: provide ${field.label.toLowerCase()} when available.`;
148
+ }
149
+
150
+ export function buildEvidenceCaptureValidationSpecs(fields) {
151
+ return fields.map((field) => ({
152
+ key: field.key,
153
+ label: field.label,
154
+ rule: field.validationRule,
155
+ severity: field.required ? "error" : "info",
156
+ required: field.required,
157
+ allowsEmpty: !field.required,
158
+ minLength: field.minLength,
159
+ valueShape: field.valueShape,
160
+ acceptsMultiple: field.acceptsMultiple,
161
+ emptyValue: cloneEvidenceCaptureValue(field.emptyValue),
162
+ message: field.validationHint,
163
+ failureMessage: getEvidenceCaptureValidationFailureMessage(field),
164
+ }));
165
+ }
166
+
167
+ function getEvidenceCaptureInitialValidationDisplay(status) {
168
+ return {
169
+ "missing-required": {
170
+ statusLabel: "Missing required",
171
+ statusTone: "danger",
172
+ iconName: "alert-circle",
173
+ actionLabel: "Provide evidence",
174
+ helperText: "Required before completion",
175
+ },
176
+ "optional-empty": {
177
+ statusLabel: "Optional empty",
178
+ statusTone: "info",
179
+ iconName: "info",
180
+ actionLabel: "Add optional evidence",
181
+ helperText: "Can remain empty",
182
+ },
183
+ }[status] || {
184
+ statusLabel: "Unknown",
185
+ statusTone: "neutral",
186
+ iconName: "circle",
187
+ actionLabel: "Review",
188
+ helperText: "Review this field",
189
+ };
190
+ }
191
+
192
+ export function buildEvidenceCaptureInitialValidationStates(fields) {
193
+ return fields.map((field) => {
194
+ const status = field.required ? "missing-required" : "optional-empty";
195
+ const display = getEvidenceCaptureInitialValidationDisplay(status);
196
+ return {
197
+ key: field.key,
198
+ label: field.label,
199
+ rule: field.validationRule,
200
+ status,
201
+ statusLabel: display.statusLabel,
202
+ statusTone: display.statusTone,
203
+ iconName: display.iconName,
204
+ actionLabel: display.actionLabel,
205
+ helperText: display.helperText,
206
+ valid: !field.required,
207
+ blocking: field.required,
208
+ severity: field.required ? "error" : "info",
209
+ required: field.required,
210
+ allowsEmpty: !field.required,
211
+ touched: false,
212
+ dirty: false,
213
+ valuePresent: false,
214
+ valueLength: 0,
215
+ minLength: field.minLength,
216
+ valueShape: field.valueShape,
217
+ acceptsMultiple: field.acceptsMultiple,
218
+ emptyValue: cloneEvidenceCaptureValue(field.emptyValue),
219
+ payloadPath: field.payloadPath,
220
+ message: field.required
221
+ ? getEvidenceCaptureValidationFailureMessage(field)
222
+ : field.validationHint,
223
+ };
224
+ });
225
+ }
226
+
227
+ export function buildEvidenceCaptureInitialValidationDisplayMetadata(fields) {
228
+ return buildEvidenceCaptureInitialValidationStates(fields).map((state) => ({
229
+ key: state.key,
230
+ label: state.label,
231
+ status: state.status,
232
+ statusLabel: state.statusLabel,
233
+ statusTone: state.statusTone,
234
+ iconName: state.iconName,
235
+ actionLabel: state.actionLabel,
236
+ helperText: state.helperText,
237
+ blocking: state.blocking,
238
+ required: state.required,
239
+ message: state.message,
240
+ }));
241
+ }
242
+
243
+ export function buildEvidenceCaptureInitialValidationChecklist(fields) {
244
+ return buildEvidenceCaptureInitialValidationStates(fields).map((state) => ({
245
+ key: state.key,
246
+ label: state.label,
247
+ status: state.status,
248
+ statusLabel: state.statusLabel,
249
+ statusTone: state.statusTone,
250
+ iconName: state.iconName,
251
+ actionLabel: state.actionLabel,
252
+ helperText: state.helperText,
253
+ required: state.required,
254
+ blocking: state.blocking,
255
+ completionBlocking: state.blocking,
256
+ checkedInitially: state.valid,
257
+ disabled: false,
258
+ message: state.message,
259
+ payloadPath: state.payloadPath,
260
+ }));
261
+ }
262
+
263
+ export function buildEvidenceCaptureInitialValidationChecklistSummary(fields) {
264
+ const checklist = buildEvidenceCaptureInitialValidationChecklist(fields);
265
+ const checkedItems = checklist.filter((item) => item.checkedInitially);
266
+ const uncheckedItems = checklist.filter((item) => !item.checkedInitially);
267
+ const blockingItems = checklist.filter((item) => item.completionBlocking);
268
+ const blockingUncheckedItems = checklist.filter((item) => item.completionBlocking && !item.checkedInitially);
269
+ const firstUncheckedItem = uncheckedItems[0];
270
+ const status = blockingUncheckedItems.length > 0 ? "blocked" : "ready";
271
+ const completionPercent = checklist.length > 0
272
+ ? Math.round((checkedItems.length / checklist.length) * 100)
273
+ : 100;
274
+ return {
275
+ status,
276
+ statusLabel: status === "blocked" ? "Checklist blocked" : "Checklist ready",
277
+ statusTone: status === "blocked" ? "danger" : "success",
278
+ iconName: status === "blocked" ? "list-x" : "list-checks",
279
+ actionLabel: status === "blocked" ? "Complete required evidence" : "Continue",
280
+ helperText: status === "blocked"
281
+ ? `${blockingUncheckedItems.length} required checklist item(s) need evidence before completion.`
282
+ : "No required checklist items are unchecked on first render.",
283
+ itemCount: checklist.length,
284
+ checkedCount: checkedItems.length,
285
+ uncheckedCount: uncheckedItems.length,
286
+ requiredCount: checklist.filter((item) => item.required).length,
287
+ optionalCount: checklist.filter((item) => !item.required).length,
288
+ blockingCount: blockingItems.length,
289
+ blockingUncheckedCount: blockingUncheckedItems.length,
290
+ nonBlockingCount: checklist.filter((item) => !item.completionBlocking).length,
291
+ completionPercent,
292
+ progressLabel: `${checkedItems.length}/${checklist.length} complete`,
293
+ allCheckedInitially: uncheckedItems.length === 0,
294
+ hasUncheckedItems: uncheckedItems.length > 0,
295
+ hasBlockingUncheckedItems: blockingUncheckedItems.length > 0,
296
+ canCompleteInitially: blockingUncheckedItems.length === 0,
297
+ firstUncheckedItemKey: firstUncheckedItem?.key || "",
298
+ firstUncheckedItemLabel: firstUncheckedItem?.label || "",
299
+ firstUncheckedItemMessage: firstUncheckedItem?.message || "",
300
+ };
301
+ }
302
+
303
+ export function buildEvidenceCaptureInitialValidationSummary(fields) {
304
+ const states = buildEvidenceCaptureInitialValidationStates(fields);
305
+ const blockingStates = states.filter((state) => state.blocking);
306
+ const firstBlockingState = blockingStates[0];
307
+ const status = blockingStates.length > 0 ? "blocked" : "ready";
308
+ return {
309
+ status,
310
+ statusLabel: status === "blocked" ? "Blocked by required evidence" : "Ready for completion",
311
+ statusTone: status === "blocked" ? "danger" : "success",
312
+ iconName: status === "blocked" ? "alert-circle" : "check-circle",
313
+ actionLabel: status === "blocked" ? "Provide required evidence" : "Continue",
314
+ helperText: status === "blocked"
315
+ ? `${blockingStates.length} required evidence field(s) need input before completion.`
316
+ : "No required evidence is missing on first render.",
317
+ fieldCount: states.length,
318
+ requiredCount: states.filter((state) => state.required).length,
319
+ optionalCount: states.filter((state) => !state.required).length,
320
+ validCount: states.filter((state) => state.valid).length,
321
+ invalidCount: states.filter((state) => !state.valid).length,
322
+ blockingCount: blockingStates.length,
323
+ nonBlockingCount: states.filter((state) => !state.blocking).length,
324
+ missingRequiredCount: states.filter((state) => state.status === "missing-required").length,
325
+ optionalEmptyCount: states.filter((state) => state.status === "optional-empty").length,
326
+ dangerDisplayCount: states.filter((state) => state.statusTone === "danger").length,
327
+ infoDisplayCount: states.filter((state) => state.statusTone === "info").length,
328
+ allFieldsPristine: states.every((state) => !state.dirty && !state.touched),
329
+ canCompleteInitially: blockingStates.length === 0,
330
+ firstBlockingFieldKey: firstBlockingState?.key || "",
331
+ firstBlockingFieldLabel: firstBlockingState?.label || "",
332
+ firstBlockingMessage: firstBlockingState?.message || "",
333
+ };
334
+ }
@@ -0,0 +1,31 @@
1
+ // Human-readable formatting for Website Improvement bundle handoff runbooks.
2
+
3
+ export function formatBundleHandoffOperatorRunbookLines(operatorRunbook) {
4
+ if (!operatorRunbook || !Array.isArray(operatorRunbook.stages) || operatorRunbook.stages.length === 0) {
5
+ return ["- No operator runbook is available."];
6
+ }
7
+ if (Array.isArray(operatorRunbook.stageHumanLines) && operatorRunbook.stageHumanLines.length === operatorRunbook.stages.length) {
8
+ return operatorRunbook.stageHumanLines;
9
+ }
10
+ return operatorRunbook.stages.map((stage) => {
11
+ const checklistSummary = stage.actionEvidenceCaptureInitialValidationChecklistSummary
12
+ || operatorRunbook.stageActionEvidenceCaptureInitialValidationChecklistSummaryByKey?.[stage.key];
13
+ return formatBundleHandoffOperatorRunbookStageLine(stage, checklistSummary);
14
+ });
15
+ }
16
+
17
+ export function formatBundleHandoffOperatorRunbookStageLine(stage, checklistSummary = null) {
18
+ const commands = Array.isArray(stage.commands) ? stage.commands : [];
19
+ const outputFiles = Array.isArray(stage.outputFiles) ? stage.outputFiles : [];
20
+ const required = stage.required ? "required" : "optional";
21
+ const commandText = commands.length
22
+ ? ` command: \`${commands[0].command}\``
23
+ : " command: manual";
24
+ const outputText = outputFiles.length ? ` output: ${outputFiles.join(", ")}` : "";
25
+ const evidenceText = checklistSummary?.itemCount > 0
26
+ ? ` evidence: ${checklistSummary.progressLabel}, ${checklistSummary.statusLabel}${
27
+ checklistSummary.firstUncheckedItemLabel ? `; next: ${checklistSummary.firstUncheckedItemLabel}` : ""
28
+ }`
29
+ : "";
30
+ return `- ${stage.step}. ${stage.key} (${required}, ${stage.runPolicy || stage.kind}): ${stage.label}.${commandText}${outputText}${evidenceText}`;
31
+ }
@@ -0,0 +1,167 @@
1
+ // Human-readable stage line rows and summaries for the bundle-handoff operator runbook.
2
+
3
+ import {
4
+ formatBundleHandoffOperatorRunbookStageLine,
5
+ } from "./site-bundle-handoff-runbook-format.mjs";
6
+ import {
7
+ byKey,
8
+ hasCommands,
9
+ isManualStage,
10
+ isOptionalStage,
11
+ isRequiredStage,
12
+ } from "./site-bundle-handoff-runbook-maps.mjs";
13
+
14
+ export function buildStageHumanLineMaps(
15
+ stages,
16
+ stageActionRows,
17
+ stageActionEvidenceCaptureInitialValidationChecklistSummaryByKey,
18
+ ) {
19
+ const stageHumanLines = stages.map((stage) => formatBundleHandoffOperatorRunbookStageLine(
20
+ stage,
21
+ stageActionEvidenceCaptureInitialValidationChecklistSummaryByKey[stage.key],
22
+ ));
23
+ const stageHumanLineByKey = byKey(stages, (_stage, index) => stageHumanLines[index]);
24
+ const stageHumanLineDisplayRows = stages.map((stage, index) => {
25
+ const actionRow = stageActionRows[index];
26
+ const evidenceProgress = actionRow.actionEvidenceCaptureInitialValidationChecklistSummary;
27
+ const displayRowIdentity = {
28
+ step: stage.step,
29
+ key: stage.key,
30
+ label: stage.label,
31
+ line: stageHumanLines[index],
32
+ };
33
+ const displayRowExecutionContext = {
34
+ required: stage.required,
35
+ manual: isManualStage(stage),
36
+ commandCount: stage.commandCount,
37
+ };
38
+ const displayRowActionStatus = {
39
+ actionType: actionRow.actionType,
40
+ actionLabel: actionRow.actionLabel,
41
+ actionStatus: actionRow.actionStatus,
42
+ actionStatusLabel: actionRow.actionStatusLabel,
43
+ actionStatusTone: actionRow.actionStatusTone,
44
+ };
45
+ const displayRowEvidenceProgress = {
46
+ hasEvidenceProgress: evidenceProgress.itemCount > 0,
47
+ evidenceProgressStatus: evidenceProgress.status || "",
48
+ evidenceProgressStatusLabel: evidenceProgress.statusLabel || "",
49
+ evidenceProgressStatusTone: evidenceProgress.statusTone || "",
50
+ evidenceProgressIconName: evidenceProgress.iconName || "",
51
+ evidenceProgressLabel: evidenceProgress.progressLabel || "",
52
+ evidenceCompletionPercent: evidenceProgress.completionPercent ?? 0,
53
+ firstUncheckedEvidenceItemLabel: evidenceProgress.firstUncheckedItemLabel || "",
54
+ };
55
+ return {
56
+ ...displayRowIdentity,
57
+ ...displayRowExecutionContext,
58
+ ...displayRowActionStatus,
59
+ ...displayRowEvidenceProgress,
60
+ };
61
+ });
62
+ const stageHumanLineDisplayRowByKey = byKey(stageHumanLineDisplayRows, (row) => row);
63
+ const countBy = (predicate) => stages.filter(predicate).length;
64
+ const stageKeysBy = (predicate) => stages.filter(predicate).map((stage) => stage.key);
65
+ const countActions = (predicate) => stageActionRows.filter(predicate).length;
66
+ const firstActionKey = (predicate) => stageActionRows.find(predicate)?.key || "";
67
+ const displayRowKeysBy = (predicate) => stageHumanLineDisplayRows.filter(predicate).map((row) => row.key);
68
+ const countDisplayRows = (predicate) => stageHumanLineDisplayRows.filter(predicate).length;
69
+ const firstDisplayRowKey = (predicate) => stageHumanLineDisplayRows.find(predicate)?.key || "";
70
+ const isRequiredDisplayRow = (row) => row.required;
71
+ const isOptionalDisplayRow = (row) => !row.required;
72
+ const hasCommandDisplayRow = (row) => row.commandCount > 0;
73
+ const isManualDisplayRow = (row) => row.manual;
74
+ const hasReadyActionStatus = (row) => row.actionStatus === "ready";
75
+ const hasOptionalActionStatus = (row) => row.actionStatus === "optional";
76
+ const hasManualActionStatus = (row) => row.actionStatus === "manual";
77
+ const hasBlockedActionStatus = (row) => row.actionStatus === "blocked";
78
+ const hasDisplayRowEvidenceProgress = (row) => row.hasEvidenceProgress;
79
+ const hasBlockedDisplayRowEvidenceProgress = (row) => row.evidenceProgressStatus === "blocked";
80
+ const hasReadyDisplayRowEvidenceProgress = (row) => row.evidenceProgressStatus === "ready";
81
+ const hasEvidenceProgress = (stage) => (
82
+ stage.actionEvidenceCaptureInitialValidationChecklistSummary.itemCount > 0
83
+ );
84
+ const hasBlockedEvidenceProgress = (stage) => (
85
+ stage.actionEvidenceCaptureInitialValidationChecklistSummary.status === "blocked"
86
+ );
87
+ const hasReadyEvidenceProgress = (stage) => (
88
+ stage.actionEvidenceCaptureInitialValidationChecklistSummary.status === "ready"
89
+ );
90
+ const stageHumanLineDisplayRowKeysByActionStatus = {
91
+ ready: displayRowKeysBy(hasReadyActionStatus),
92
+ optional: displayRowKeysBy(hasOptionalActionStatus),
93
+ manual: displayRowKeysBy(hasManualActionStatus),
94
+ blocked: displayRowKeysBy(hasBlockedActionStatus),
95
+ };
96
+ const stageHumanLineDisplayRowKeysByEvidenceProgressStatus = {
97
+ blocked: displayRowKeysBy(hasBlockedDisplayRowEvidenceProgress),
98
+ ready: displayRowKeysBy(hasReadyDisplayRowEvidenceProgress),
99
+ };
100
+ const stageHumanLineDisplayRowCountSummary = {
101
+ count: stageHumanLineDisplayRows.length,
102
+ byKeyCount: Object.keys(stageHumanLineDisplayRowByKey).length,
103
+ requiredCount: countDisplayRows(isRequiredDisplayRow),
104
+ optionalCount: countDisplayRows(isOptionalDisplayRow),
105
+ commandCount: countDisplayRows(hasCommandDisplayRow),
106
+ manualCount: countDisplayRows(isManualDisplayRow),
107
+ };
108
+ const stageHumanLineDisplayRowActionStatusSummary = {
109
+ readyActionStatusCount: countDisplayRows(hasReadyActionStatus),
110
+ optionalActionStatusCount: countDisplayRows(hasOptionalActionStatus),
111
+ manualActionStatusCount: countDisplayRows(hasManualActionStatus),
112
+ blockedActionStatusCount: countDisplayRows(hasBlockedActionStatus),
113
+ };
114
+ const stageHumanLineDisplayRowEvidenceProgressSummary = {
115
+ evidenceProgressCount: countDisplayRows(hasDisplayRowEvidenceProgress),
116
+ blockedEvidenceProgressCount: countDisplayRows(hasBlockedDisplayRowEvidenceProgress),
117
+ readyEvidenceProgressCount: countDisplayRows(hasReadyDisplayRowEvidenceProgress),
118
+ };
119
+ const stageHumanLineDisplayRowFirstKeySummary = {
120
+ firstRowKey: stageHumanLineDisplayRows[0]?.key || "",
121
+ firstReadyActionRowKey: firstDisplayRowKey(hasReadyActionStatus),
122
+ firstOptionalActionRowKey: firstDisplayRowKey(hasOptionalActionStatus),
123
+ firstManualActionRowKey: firstDisplayRowKey(hasManualActionStatus),
124
+ firstBlockedEvidenceProgressRowKey: firstDisplayRowKey(hasBlockedDisplayRowEvidenceProgress),
125
+ firstReadyEvidenceProgressRowKey: firstDisplayRowKey(hasReadyDisplayRowEvidenceProgress),
126
+ };
127
+ const stageHumanLineDisplayRowSummary = {
128
+ ...stageHumanLineDisplayRowCountSummary,
129
+ ...stageHumanLineDisplayRowActionStatusSummary,
130
+ ...stageHumanLineDisplayRowEvidenceProgressSummary,
131
+ ...stageHumanLineDisplayRowFirstKeySummary,
132
+ };
133
+ const stageHumanLineCountSummary = {
134
+ count: stageHumanLines.length,
135
+ byKeyCount: Object.keys(stageHumanLineByKey).length,
136
+ requiredCount: countBy(isRequiredStage),
137
+ optionalCount: countBy(isOptionalStage),
138
+ commandCount: countBy(hasCommands),
139
+ manualCount: countBy(isManualStage),
140
+ };
141
+ const stageHumanLineEvidenceProgressSummary = {
142
+ evidenceProgressCount: countActions(hasEvidenceProgress),
143
+ blockedEvidenceProgressCount: countActions(hasBlockedEvidenceProgress),
144
+ readyEvidenceProgressCount: countActions(hasReadyEvidenceProgress),
145
+ };
146
+ const stageHumanLineFirstValueSummary = {
147
+ firstStageKey: stages[0]?.key || "",
148
+ firstLine: stageHumanLines[0] || "",
149
+ firstEvidenceProgressStageKey: firstActionKey(hasEvidenceProgress),
150
+ firstBlockedEvidenceProgressStageKey: firstActionKey(hasBlockedEvidenceProgress),
151
+ };
152
+ const stageHumanLineSummary = {
153
+ ...stageHumanLineCountSummary,
154
+ ...stageHumanLineEvidenceProgressSummary,
155
+ ...stageHumanLineFirstValueSummary,
156
+ };
157
+ return {
158
+ stageHumanLines,
159
+ stageHumanLineByKey,
160
+ stageHumanLineDisplayRows,
161
+ stageHumanLineDisplayRowByKey,
162
+ stageHumanLineDisplayRowKeysByActionStatus,
163
+ stageHumanLineDisplayRowKeysByEvidenceProgressStatus,
164
+ stageHumanLineDisplayRowSummary,
165
+ stageHumanLineSummary,
166
+ };
167
+ }