@design-ai/cli 4.56.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 (264) hide show
  1. package/.claude-plugin/plugin.json +109 -25
  2. package/CHANGELOG.md +36 -0
  3. package/README.ko.md +9 -26
  4. package/README.md +10 -23
  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 +13 -3
  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-bundle-handoff-expected.mjs +173 -0
  48. package/cli/lib/site-bundle-handoff-runbook-action-summary.mjs +332 -0
  49. package/cli/lib/site-bundle-handoff-runbook-human-lines.mjs +167 -0
  50. package/cli/lib/site-bundle-handoff-runbook-maps.mjs +238 -0
  51. package/cli/lib/site-bundle-handoff-runbook-next-step.mjs +278 -0
  52. package/cli/lib/site-bundle-handoff-runbook.mjs +44 -931
  53. package/cli/lib/site-test-support.mjs +68 -0
  54. package/cli/lib/skill-proposals-apply-commands.mjs +135 -0
  55. package/cli/lib/skill-proposals-apply-contract.mjs +750 -0
  56. package/cli/lib/skill-proposals-apply-plan.mjs +118 -0
  57. package/cli/lib/skill-proposals-generate.mjs +298 -0
  58. package/cli/lib/skill-proposals-render.mjs +532 -0
  59. package/cli/lib/skill-proposals-review.mjs +262 -0
  60. package/cli/lib/skill-proposals.mjs +15 -2046
  61. package/cli/lib/workspace-args.mjs +83 -0
  62. package/cli/lib/workspace-git.mjs +169 -0
  63. package/cli/lib/workspace-learning.mjs +483 -0
  64. package/cli/lib/workspace-repo.mjs +139 -0
  65. package/cli/lib/workspace-report.mjs +283 -0
  66. package/cli/lib/workspace-test-support.mjs +99 -0
  67. package/cli/lib/workspace.mjs +30 -1056
  68. package/docs/AI-LEARNING-PHASE2.md +215 -0
  69. package/docs/DISTRIBUTION.ko.md +2 -2
  70. package/docs/DISTRIBUTION.md +2 -2
  71. package/docs/NEXT-SURFACE-DECISION.md +125 -0
  72. package/docs/PRODUCT-READINESS.md +3 -3
  73. package/docs/RELEASE-GATES.ko.md +35 -0
  74. package/docs/RELEASE-GATES.md +234 -0
  75. package/docs/ROADMAP.md +53 -8
  76. package/docs/external-status.md +6 -8
  77. package/docs/inspection-20260630.md +169 -0
  78. package/docs/integrations/design-ai-mcp-server.md +4 -4
  79. package/docs/reference/ant-design.md +413 -0
  80. package/docs/reference/awesome-design-md.md +439 -0
  81. package/docs/reference/mui.md +783 -0
  82. package/docs/reference/shadcn-ui.md +298 -0
  83. package/examples/component-accordion-actions.md +5 -5
  84. package/examples/component-accordion-details.md +5 -5
  85. package/examples/component-accordion-summary.md +5 -5
  86. package/examples/component-accordion.md +3 -3
  87. package/examples/component-affix.md +1 -1
  88. package/examples/component-alert-dialog.md +1 -1
  89. package/examples/component-alert-title.md +1 -1
  90. package/examples/component-alert.md +3 -3
  91. package/examples/component-anchor.md +1 -1
  92. package/examples/component-app-bar.md +1 -1
  93. package/examples/component-aspect-ratio.md +1 -1
  94. package/examples/component-auto-complete.md +2 -2
  95. package/examples/component-avatar-group.md +5 -5
  96. package/examples/component-avatar.md +3 -3
  97. package/examples/component-back-top.md +1 -1
  98. package/examples/component-backdrop.md +1 -1
  99. package/examples/component-badge.md +3 -3
  100. package/examples/component-border-beam.md +3 -3
  101. package/examples/component-bottom-navigation.md +1 -1
  102. package/examples/component-box.md +1 -1
  103. package/examples/component-breadcrumb.md +3 -3
  104. package/examples/component-button-base.md +3 -3
  105. package/examples/component-button-group.md +2 -2
  106. package/examples/component-button.md +3 -3
  107. package/examples/component-calendar.md +2 -2
  108. package/examples/component-card-actions.md +1 -1
  109. package/examples/component-card-content.md +1 -1
  110. package/examples/component-card-header.md +1 -1
  111. package/examples/component-card-media.md +1 -1
  112. package/examples/component-card.md +3 -3
  113. package/examples/component-carousel.md +2 -2
  114. package/examples/component-cascader.md +1 -1
  115. package/examples/component-chart.md +1 -1
  116. package/examples/component-checkbox.md +3 -3
  117. package/examples/component-click-away-listener.md +1 -1
  118. package/examples/component-code.md +1 -1
  119. package/examples/component-collapsible.md +1 -1
  120. package/examples/component-color-picker.md +1 -1
  121. package/examples/component-combobox.md +1 -1
  122. package/examples/component-command.md +1 -1
  123. package/examples/component-config-provider.md +3 -3
  124. package/examples/component-context-menu.md +1 -1
  125. package/examples/component-css-baseline.md +3 -3
  126. package/examples/component-date-picker.md +2 -2
  127. package/examples/component-descriptions.md +1 -1
  128. package/examples/component-dialog-actions.md +1 -1
  129. package/examples/component-dialog-content-text.md +1 -1
  130. package/examples/component-dialog-content.md +1 -1
  131. package/examples/component-dialog-title.md +1 -1
  132. package/examples/component-dialog.md +2 -2
  133. package/examples/component-divider.md +3 -3
  134. package/examples/component-drawer.md +3 -3
  135. package/examples/component-dropdown.md +3 -3
  136. package/examples/component-empty.md +2 -2
  137. package/examples/component-fade.md +1 -1
  138. package/examples/component-field.md +1 -1
  139. package/examples/component-filled-input.md +1 -1
  140. package/examples/component-flex.md +1 -1
  141. package/examples/component-float-button.md +2 -2
  142. package/examples/component-form-control-label.md +1 -1
  143. package/examples/component-form-control.md +1 -1
  144. package/examples/component-form-controls.md +1 -1
  145. package/examples/component-form-group.md +1 -1
  146. package/examples/component-form-helper-text.md +1 -1
  147. package/examples/component-form-label.md +1 -1
  148. package/examples/component-form.md +2 -2
  149. package/examples/component-grid.md +2 -2
  150. package/examples/component-grow.md +1 -1
  151. package/examples/component-hover-card.md +1 -1
  152. package/examples/component-icon-button.md +1 -1
  153. package/examples/component-icon.md +1 -1
  154. package/examples/component-image-list.md +1 -1
  155. package/examples/component-image.md +1 -1
  156. package/examples/component-input-adornment.md +1 -1
  157. package/examples/component-input-base.md +1 -1
  158. package/examples/component-input-number.md +1 -1
  159. package/examples/component-input-otp.md +2 -2
  160. package/examples/component-input.md +3 -3
  161. package/examples/component-item.md +1 -1
  162. package/examples/component-label.md +1 -1
  163. package/examples/component-layout.md +1 -1
  164. package/examples/component-link.md +1 -1
  165. package/examples/component-list-item-avatar.md +1 -1
  166. package/examples/component-list-item-button.md +1 -1
  167. package/examples/component-list-item-icon.md +1 -1
  168. package/examples/component-list-item-text.md +1 -1
  169. package/examples/component-list-item.md +1 -1
  170. package/examples/component-list-subheader.md +1 -1
  171. package/examples/component-list.md +2 -2
  172. package/examples/component-masonry.md +1 -1
  173. package/examples/component-mentions.md +1 -1
  174. package/examples/component-menu-item.md +1 -1
  175. package/examples/component-menu-list.md +1 -1
  176. package/examples/component-menu.md +1 -1
  177. package/examples/component-menubar.md +1 -1
  178. package/examples/component-message.md +1 -1
  179. package/examples/component-mobile-stepper.md +1 -1
  180. package/examples/component-modal.md +5 -5
  181. package/examples/component-navigation-menu.md +1 -1
  182. package/examples/component-notification.md +1 -1
  183. package/examples/component-outlined-input.md +1 -1
  184. package/examples/component-pagination.md +3 -3
  185. package/examples/component-paper.md +1 -1
  186. package/examples/component-popconfirm.md +1 -1
  187. package/examples/component-popover.md +3 -3
  188. package/examples/component-popper.md +1 -1
  189. package/examples/component-progress.md +2 -2
  190. package/examples/component-qr-code.md +1 -1
  191. package/examples/component-radio.md +3 -3
  192. package/examples/component-rate.md +2 -2
  193. package/examples/component-resizable.md +1 -1
  194. package/examples/component-result.md +1 -1
  195. package/examples/component-scroll-area.md +1 -1
  196. package/examples/component-segmented.md +3 -3
  197. package/examples/component-select.md +3 -3
  198. package/examples/component-separator.md +1 -1
  199. package/examples/component-sheet.md +1 -1
  200. package/examples/component-sidebar.md +1 -1
  201. package/examples/component-skeleton.md +3 -3
  202. package/examples/component-slide.md +1 -1
  203. package/examples/component-slider.md +3 -3
  204. package/examples/component-snackbar-content.md +1 -1
  205. package/examples/component-snackbar.md +1 -1
  206. package/examples/component-sonner.md +1 -1
  207. package/examples/component-space.md +1 -1
  208. package/examples/component-speed-dial-action.md +1 -1
  209. package/examples/component-speed-dial.md +1 -1
  210. package/examples/component-spin.md +2 -2
  211. package/examples/component-spinner.md +1 -1
  212. package/examples/component-splitter.md +2 -2
  213. package/examples/component-stack.md +1 -1
  214. package/examples/component-statistic.md +1 -1
  215. package/examples/component-step-button.md +4 -4
  216. package/examples/component-step-connector.md +4 -4
  217. package/examples/component-step-content.md +1 -1
  218. package/examples/component-step-icon.md +1 -1
  219. package/examples/component-step-label.md +1 -1
  220. package/examples/component-step.md +2 -2
  221. package/examples/component-steps.md +2 -2
  222. package/examples/component-swipeable-drawer.md +1 -1
  223. package/examples/component-switch.md +3 -3
  224. package/examples/component-tab-scroll-button.md +5 -5
  225. package/examples/component-tab.md +1 -1
  226. package/examples/component-table-body.md +1 -1
  227. package/examples/component-table-cell.md +1 -1
  228. package/examples/component-table-container.md +1 -1
  229. package/examples/component-table-footer.md +1 -1
  230. package/examples/component-table-head.md +1 -1
  231. package/examples/component-table-pagination.md +1 -1
  232. package/examples/component-table-row.md +1 -1
  233. package/examples/component-table-sort-label.md +1 -1
  234. package/examples/component-table.md +2 -2
  235. package/examples/component-tabs.md +3 -3
  236. package/examples/component-tag-badge.md +2 -2
  237. package/examples/component-tag.md +1 -1
  238. package/examples/component-textarea-autosize.md +1 -1
  239. package/examples/component-textarea.md +1 -1
  240. package/examples/component-time-picker.md +1 -1
  241. package/examples/component-timeline.md +1 -1
  242. package/examples/component-toast.md +4 -4
  243. package/examples/component-toggle-button.md +1 -1
  244. package/examples/component-toggle.md +1 -1
  245. package/examples/component-toolbar.md +1 -1
  246. package/examples/component-tooltip.md +3 -3
  247. package/examples/component-tour.md +1 -1
  248. package/examples/component-transfer.md +1 -1
  249. package/examples/component-tree-select.md +1 -1
  250. package/examples/component-tree.md +1 -1
  251. package/examples/component-typography.md +2 -2
  252. package/examples/component-upload.md +1 -1
  253. package/examples/component-watermark.md +1 -1
  254. package/examples/component-zoom.md +1 -1
  255. package/knowledge/COVERAGE.md +4 -3
  256. package/knowledge/components/INDEX.md +202 -202
  257. package/knowledge/patterns/brand-references.md +72 -72
  258. package/package.json +1 -1
  259. package/tools/audit/local-ci.py +16 -1
  260. package/tools/audit/package-smoke.py +611 -9
  261. package/tools/audit/registry-smoke.py +534 -0
  262. package/tools/audit/release-metadata.py +47 -1
  263. package/tools/audit/smoke_assertions.py +915 -13
  264. package/tools/migrations/refs-links-to-reference-pages.py +179 -0
@@ -0,0 +1,532 @@
1
+ // Markdown/report rendering for skill proposal reviews, apply plans, and patches.
2
+
3
+ import { existsSync, readFileSync } from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import { PACKAGE_ROOT } from "./paths.mjs";
7
+
8
+ function listItem(label, value) {
9
+ return `- ${label}: ${value}`;
10
+ }
11
+
12
+ function yesNo(value) {
13
+ return value ? "yes" : "no";
14
+ }
15
+
16
+ function normalizePatchPath(filePath) {
17
+ return String(filePath || "").replace(/\\/g, "/");
18
+ }
19
+
20
+ function readSkillLines(skillPath, sourceRoot) {
21
+ const absolutePath = path.resolve(sourceRoot, skillPath);
22
+ if (!existsSync(absolutePath)) {
23
+ return {
24
+ exists: false,
25
+ absolutePath,
26
+ lines: [],
27
+ };
28
+ }
29
+ const text = readFileSync(absolutePath, "utf8");
30
+ return {
31
+ exists: true,
32
+ absolutePath,
33
+ lines: text.split(/\r?\n/),
34
+ };
35
+ }
36
+
37
+ function patchSectionForProposal(proposal) {
38
+ const routes = Array.isArray(proposal.routeIds) && proposal.routeIds.length > 0
39
+ ? proposal.routeIds.join(", ")
40
+ : "artifact";
41
+ return [
42
+ "",
43
+ `## Local Learning Proposal: ${proposal.id}`,
44
+ "",
45
+ "<!-- Generated by design-ai learn --propose-skills --patch. Review manually before applying. -->",
46
+ "",
47
+ `- Category: ${proposal.category}`,
48
+ `- Routes: ${routes}`,
49
+ `- Risk: ${proposal.riskLevel}`,
50
+ `- Review status: ${proposal.reviewStatus || "pending"}`,
51
+ `- Evidence count: ${proposal.sourceIssueCount}`,
52
+ `- Proposed instruction: ${proposal.proposedInstructionDelta}`,
53
+ `- Verification: \`${proposal.verificationCommand}\``,
54
+ ];
55
+ }
56
+
57
+ function patchForSkillFile(skillPath, proposals, { sourceRoot }) {
58
+ const normalizedSkillPath = normalizePatchPath(skillPath);
59
+ const file = readSkillLines(normalizedSkillPath, sourceRoot);
60
+ const currentLines = file.lines.slice();
61
+ if (currentLines.length > 0 && currentLines[currentLines.length - 1] === "") {
62
+ currentLines.pop();
63
+ }
64
+ const addedLines = proposals.flatMap(patchSectionForProposal);
65
+ const fromPath = `a/${normalizedSkillPath}`;
66
+ const toPath = `b/${normalizedSkillPath}`;
67
+ const lines = [
68
+ `diff --git ${fromPath} ${toPath}`,
69
+ `--- ${file.exists ? fromPath : "/dev/null"}`,
70
+ `+++ ${toPath}`,
71
+ ];
72
+
73
+ if (currentLines.length === 0) {
74
+ lines.push(`@@ -0,0 +1,${addedLines.length} @@`);
75
+ for (const addedLine of addedLines) {
76
+ lines.push(`+${addedLine}`);
77
+ }
78
+ return lines;
79
+ }
80
+
81
+ const contextLine = currentLines[currentLines.length - 1];
82
+ lines.push(`@@ -${currentLines.length},1 +${currentLines.length},${addedLines.length + 1} @@`);
83
+ lines.push(` ${contextLine}`);
84
+ for (const addedLine of addedLines) {
85
+ lines.push(`+${addedLine}`);
86
+ }
87
+ return lines;
88
+ }
89
+
90
+ export function renderSkillEvolutionProposalPatch(payload, {
91
+ sourceRoot = PACKAGE_ROOT,
92
+ } = {}) {
93
+ const proposals = Array.isArray(payload.proposals)
94
+ ? payload.proposals.filter((proposal) => !proposal.reviewClearsStrict)
95
+ : [];
96
+ const lines = [
97
+ "# design-ai skill proposal patch preview",
98
+ "# Preview-only output from `design-ai learn --propose-skills --patch`.",
99
+ "# Review manually before applying. This command does not edit skill files.",
100
+ ];
101
+
102
+ if (proposals.length === 0) {
103
+ lines.push((payload.proposalCount || 0) > 0
104
+ ? "# No pending skill proposal deltas remain after review-file decisions."
105
+ : "# No repeated check-capture groups crossed the proposal threshold.");
106
+ return `${lines.join("\n")}\n`;
107
+ }
108
+
109
+ const proposalsBySkill = new Map();
110
+ for (const proposal of proposals) {
111
+ const skillPath = proposal.candidateSkillPath || "skills/unknown/SKILL.md";
112
+ if (!proposalsBySkill.has(skillPath)) proposalsBySkill.set(skillPath, []);
113
+ proposalsBySkill.get(skillPath).push(proposal);
114
+ }
115
+
116
+ for (const [skillPath, skillProposals] of [...proposalsBySkill.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
117
+ lines.push("");
118
+ lines.push(...patchForSkillFile(skillPath, skillProposals, { sourceRoot }));
119
+ }
120
+
121
+ return `${lines.join("\n")}\n`;
122
+ }
123
+
124
+ export function renderSkillProposalReviewTemplate(payload, {
125
+ generatedAt = new Date(),
126
+ } = {}) {
127
+ const generatedAtText = generatedAt instanceof Date ? generatedAt.toISOString() : String(generatedAt || "");
128
+ const proposals = Array.isArray(payload.proposals)
129
+ ? payload.proposals.filter((proposal) => !proposal.reviewClearsStrict)
130
+ : [];
131
+ const decisions = proposals.map((proposal) => ({
132
+ proposalId: proposal.id,
133
+ status: proposal.reviewStatus && proposal.reviewStatus !== "pending" ? proposal.reviewStatus : "deferred",
134
+ reviewedAt: "",
135
+ reviewer: "",
136
+ note: `Review ${proposal.candidateSkillPath}: ${proposal.title}`,
137
+ }));
138
+
139
+ return `${JSON.stringify({
140
+ version: 1,
141
+ generatedAt: generatedAtText,
142
+ source: "design-ai learn --propose-skills --review-template",
143
+ proposalFile: payload.file || "",
144
+ usageFile: payload.usageFile || "",
145
+ signalSource: payload.signalSource || "",
146
+ reviewFile: payload.reviewFile || "",
147
+ reviewPolicy: {
148
+ clearsStrict: ["applied", "rejected"],
149
+ remainsPending: ["accepted", "deferred"],
150
+ },
151
+ summary: {
152
+ proposalCount: payload.proposalCount || 0,
153
+ pendingReviewCount: payload.pendingReviewCount ?? proposals.length,
154
+ reviewedCount: payload.reviewedCount || 0,
155
+ templateDecisionCount: decisions.length,
156
+ },
157
+ decisions,
158
+ }, null, 2)}\n`;
159
+ }
160
+
161
+ export function renderSkillEvolutionProposalReport(payload, {
162
+ generatedAt = new Date(),
163
+ } = {}) {
164
+ const generatedAtText = generatedAt instanceof Date ? generatedAt.toISOString() : String(generatedAt || "");
165
+ const lines = [
166
+ "# Skill Evolution Proposal Report",
167
+ "",
168
+ listItem("Generated", generatedAtText),
169
+ listItem("File", payload.file),
170
+ listItem("Usage sidecar", payload.usageFile),
171
+ listItem("Signal source", payload.signalSource),
172
+ listItem("Status", payload.status),
173
+ listItem("Signal status", payload.signalStatus),
174
+ listItem("Minimum evidence", payload.minEvidenceCount),
175
+ listItem("Check capture entries", payload.checkCaptureCount),
176
+ listItem("Candidate groups", payload.candidateCount),
177
+ listItem("Proposal count", payload.proposalCount),
178
+ listItem("Skipped groups", payload.skippedCount),
179
+ listItem("Pending review", payload.pendingReviewCount ?? payload.proposalCount ?? 0),
180
+ listItem("Reviewed", payload.reviewedCount ?? 0),
181
+ listItem("Dry run", yesNo(Boolean(payload.dryRun))),
182
+ listItem("Applied", yesNo(Boolean(payload.applied))),
183
+ "",
184
+ "## Proposed Skill Deltas",
185
+ "",
186
+ ];
187
+
188
+ if (!Array.isArray(payload.proposals) || payload.proposals.length === 0) {
189
+ lines.push("No repeated check-capture groups crossed the proposal threshold.");
190
+ } else {
191
+ for (const proposal of payload.proposals) {
192
+ const routes = Array.isArray(proposal.routeIds) && proposal.routeIds.length > 0
193
+ ? proposal.routeIds.join(", ")
194
+ : "artifact";
195
+ lines.push(`### ${proposal.title}`);
196
+ lines.push("");
197
+ lines.push(listItem("Proposal id", proposal.id));
198
+ lines.push(listItem("Candidate skill", proposal.candidateSkillPath));
199
+ lines.push(listItem("Category", proposal.category));
200
+ lines.push(listItem("Routes", routes));
201
+ lines.push(listItem("Risk", proposal.riskLevel));
202
+ lines.push(listItem("Review status", proposal.reviewStatus || "pending"));
203
+ if (proposal.reviewDecision?.reviewedAt) lines.push(listItem("Reviewed at", proposal.reviewDecision.reviewedAt));
204
+ if (proposal.reviewDecision?.reviewer) lines.push(listItem("Reviewer", proposal.reviewDecision.reviewer));
205
+ if (proposal.reviewDecision?.note) lines.push(listItem("Review note", proposal.reviewDecision.note));
206
+ lines.push(listItem("Source issues", proposal.sourceIssueCount));
207
+ lines.push(listItem("Rationale", proposal.rationale));
208
+ lines.push("");
209
+ lines.push("Proposed instruction delta:");
210
+ lines.push("");
211
+ lines.push(`> ${proposal.proposedInstructionDelta}`);
212
+ lines.push("");
213
+ lines.push("Verification:");
214
+ lines.push("");
215
+ lines.push(`\`\`\`bash`);
216
+ lines.push(proposal.verificationCommand);
217
+ lines.push(`\`\`\``);
218
+ lines.push("");
219
+ lines.push("Evidence:");
220
+ const evidenceSources = Array.isArray(proposal.evidenceSources) ? proposal.evidenceSources : [];
221
+ for (const evidence of evidenceSources) {
222
+ const title = evidence.title ? ` / ${evidence.title}` : "";
223
+ lines.push(`- \`${evidence.entryId}\` [${evidence.category}] ${evidence.source}${title}`);
224
+ if (evidence.createdAt) lines.push(` - Captured: ${evidence.createdAt}`);
225
+ if (evidence.textPreview) lines.push(` - Preview: ${evidence.textPreview}`);
226
+ }
227
+ lines.push("");
228
+ }
229
+ }
230
+
231
+ const review = payload.review || {};
232
+ lines.push("## Review File", "");
233
+ if (review.file) {
234
+ lines.push(listItem("File", review.file));
235
+ lines.push(listItem("Exists", yesNo(Boolean(review.exists))));
236
+ lines.push(listItem("Status", review.status || "unknown"));
237
+ lines.push(listItem("Decisions", review.decisionCount || 0));
238
+ lines.push(listItem("Matched", review.matchedCount || 0));
239
+ lines.push(listItem("Stale", review.staleCount || 0));
240
+ lines.push(listItem("Pending", review.pendingCount ?? 0));
241
+ lines.push(listItem("Applied", review.appliedCount || 0));
242
+ lines.push(listItem("Rejected", review.rejectedCount || 0));
243
+ lines.push(listItem("Accepted", review.acceptedCount || 0));
244
+ lines.push(listItem("Deferred", review.deferredCount || 0));
245
+ if (Array.isArray(review.warnings) && review.warnings.length > 0) {
246
+ lines.push("");
247
+ lines.push("Warnings:");
248
+ for (const warning of review.warnings) lines.push(`- ${warning}`);
249
+ }
250
+ } else {
251
+ lines.push("No review file was provided.");
252
+ }
253
+ lines.push("");
254
+
255
+ lines.push("## Skipped Groups", "");
256
+ if (!Array.isArray(payload.skipped) || payload.skipped.length === 0) {
257
+ lines.push("No candidate groups were skipped.");
258
+ } else {
259
+ for (const item of payload.skipped) {
260
+ lines.push(`- \`${item.candidateSkillPath}\` [${item.category}]: ${item.reason}`);
261
+ const evidenceSources = Array.isArray(item.evidenceSources) ? item.evidenceSources : [];
262
+ for (const evidence of evidenceSources) {
263
+ lines.push(` - Evidence: \`${evidence.entryId}\` ${evidence.source}`);
264
+ }
265
+ }
266
+ }
267
+
268
+ lines.push("", "## Recommendations", "");
269
+ if (!Array.isArray(payload.recommendations) || payload.recommendations.length === 0) {
270
+ lines.push("No recommendations emitted.");
271
+ } else {
272
+ for (const recommendation of payload.recommendations) {
273
+ lines.push(`- ${recommendation.level}: ${recommendation.text}`);
274
+ }
275
+ }
276
+
277
+ const privacy = payload.privacy || {};
278
+ lines.push("", "## Privacy And Boundaries", "");
279
+ lines.push(listItem("Mutates learning profile", yesNo(Boolean(privacy.mutatesProfile))));
280
+ lines.push(listItem("Mutates skill files", yesNo(Boolean(privacy.mutatesSkillFiles))));
281
+ lines.push(listItem("Calls external AI APIs", yesNo(Boolean(privacy.callsExternalAiApis))));
282
+ lines.push(listItem("Stores raw brief text", yesNo(Boolean(privacy.storesRawBriefText))));
283
+ lines.push(listItem("Includes entry text preview", yesNo(Boolean(privacy.exposesEntryTextPreview))));
284
+
285
+ lines.push("", "## Next Steps", "");
286
+ if ((payload.proposalCount || 0) > 0) {
287
+ lines.push("- Review each proposed instruction delta manually before editing any skill file.");
288
+ lines.push("- Mark resolved proposals as `applied` or `rejected` in the review file to clear the strict proposal gate.");
289
+ lines.push("- Run the proposal verification command after applying an accepted skill edit.");
290
+ lines.push("- Re-run `design-ai learn --propose-skills --strict --json` to confirm no pending proposal gate remains.");
291
+ } else {
292
+ lines.push("- Keep capturing explicit `design-ai check --learn --yes` warning/failure results until repeated evidence exists.");
293
+ lines.push("- Re-run this report after new check-capture entries are added.");
294
+ }
295
+ lines.push("- This report is preview-only evidence; it does not apply changes.");
296
+
297
+ return `${lines.join("\n")}\n`;
298
+ }
299
+
300
+ export function renderSkillProposalApplyPlanReport(payload, {
301
+ generatedAt = new Date(),
302
+ } = {}) {
303
+ const generatedAtText = generatedAt instanceof Date ? generatedAt.toISOString() : String(generatedAt || "");
304
+ const lines = [
305
+ "# Skill Proposal Apply Plan",
306
+ "",
307
+ listItem("Generated", generatedAtText),
308
+ listItem("Status", payload.status),
309
+ listItem("Proposal status", payload.proposalStatus),
310
+ listItem("Signal status", payload.signalStatus),
311
+ listItem("File", payload.file),
312
+ listItem("Usage sidecar", payload.usageFile),
313
+ listItem("Signal source", payload.signalSource),
314
+ listItem("Review file", payload.reviewFile || "not configured"),
315
+ listItem("Accepted proposals", payload.acceptedCount || 0),
316
+ listItem("Pending review", payload.pendingReviewCount || 0),
317
+ listItem("Reviewed", payload.reviewedCount || 0),
318
+ "",
319
+ "## Manual Apply Tasks",
320
+ "",
321
+ ];
322
+
323
+ if (!Array.isArray(payload.tasks) || payload.tasks.length === 0) {
324
+ lines.push("No accepted skill proposals are ready for manual apply.");
325
+ } else {
326
+ for (const task of payload.tasks) {
327
+ lines.push(`### ${task.title}`);
328
+ lines.push("");
329
+ lines.push(listItem("Proposal id", task.proposalId));
330
+ lines.push(listItem("Candidate skill", task.candidateSkillPath));
331
+ lines.push(listItem("Category", task.category));
332
+ lines.push(listItem("Routes", (task.routeIds || []).join(", ") || "artifact"));
333
+ lines.push(listItem("Risk", task.riskLevel));
334
+ lines.push(listItem("Source issues", task.sourceIssueCount));
335
+ if (task.reviewDecision?.reviewer) lines.push(listItem("Reviewer", task.reviewDecision.reviewer));
336
+ if (task.reviewDecision?.reviewedAt) lines.push(listItem("Reviewed at", task.reviewDecision.reviewedAt));
337
+ if (task.reviewDecision?.note) lines.push(listItem("Review note", task.reviewDecision.note));
338
+ if (task.rationale) lines.push(listItem("Rationale", task.rationale));
339
+ lines.push("");
340
+ lines.push("Proposed instruction delta:");
341
+ lines.push("");
342
+ lines.push(`> ${task.proposedInstructionDelta}`);
343
+ lines.push("");
344
+ lines.push("Manual steps:");
345
+ for (const step of task.manualSteps || []) lines.push(`- ${step}`);
346
+ lines.push("");
347
+ lines.push("Verification:");
348
+ lines.push("");
349
+ lines.push("```bash");
350
+ lines.push(task.verificationCommand);
351
+ lines.push("```");
352
+ lines.push("");
353
+ lines.push("Safety checklist:");
354
+ for (const item of task.safetyChecklist || []) lines.push(`- ${item}`);
355
+ lines.push("");
356
+ lines.push("Evidence:");
357
+ for (const evidence of task.evidenceSources || []) {
358
+ const title = evidence.title ? ` / ${evidence.title}` : "";
359
+ lines.push(`- \`${evidence.entryId}\` [${evidence.category}] ${evidence.source}${title}`);
360
+ if (evidence.textPreview) lines.push(` - Preview: ${evidence.textPreview}`);
361
+ }
362
+ lines.push("");
363
+ }
364
+ }
365
+
366
+ lines.push("## Follow-up Commands", "");
367
+ const commands = payload.commands || {};
368
+ for (const [label, command] of Object.entries(commands)) {
369
+ lines.push(listItem(label, `\`${command}\``));
370
+ }
371
+
372
+ const commandContract = payload.commandContract || {};
373
+ lines.push("", "## Command Contract", "");
374
+ lines.push(listItem("Valid", yesNo(Boolean(commandContract.valid))));
375
+ lines.push(listItem("Status", commandContract.status || "unknown"));
376
+ lines.push(listItem("Command count", commandContract.commandCount || 0));
377
+ lines.push(listItem("Check count", commandContract.checkCount || 0));
378
+ lines.push(listItem("Pass count", commandContract.passCount || 0));
379
+ lines.push(listItem("Warning count", commandContract.warningCount || 0));
380
+ lines.push(listItem("Required keys", (commandContract.requiredKeys || []).join(", ") || "none"));
381
+ lines.push(listItem("Review file required", yesNo(Boolean(commandContract.reviewFileRequired))));
382
+ lines.push(listItem("Forbidden flags", (commandContract.forbiddenFlags || []).join(", ") || "none"));
383
+ lines.push(listItem("Failure count", commandContract.failureCount || 0));
384
+ lines.push(listItem("Failed checks", (commandContract.failedCheckIds || []).join(", ") || "none"));
385
+ lines.push(listItem("Next command key", commandContract.nextCommandKey || "none"));
386
+ lines.push(listItem("Next command policy", commandContract.nextCommandRunPolicy || "none"));
387
+ if (commandContract.nextCommandSafety?.level) lines.push(listItem("Next command safety", commandContract.nextCommandSafety.level));
388
+ if (commandContract.nextCommand) lines.push(listItem("Next command", `\`${commandContract.nextCommand}\``));
389
+ lines.push(listItem("Command sequence count", commandContract.commandSequenceCount || 0));
390
+ lines.push(listItem("Command sequence keys", (commandContract.commandSequenceKeys || []).join(", ") || "none"));
391
+ const sequenceSummary = commandContract.commandSequenceSummary || {};
392
+ lines.push(listItem("Command sequence policy", sequenceSummary.runPolicy || "none"));
393
+ lines.push(listItem("Command sequence executable", yesNo(Boolean(sequenceSummary.executable))));
394
+ lines.push(listItem("Command sequence local outputs", sequenceSummary.localOutputStepCount || 0));
395
+ lines.push(listItem("Command sequence mutates profile", yesNo(Boolean(sequenceSummary.mutatesProfile))));
396
+ lines.push(listItem("Command sequence mutates review file", yesNo(Boolean(sequenceSummary.mutatesReviewFile))));
397
+ lines.push(listItem("Command sequence mutates skill files", yesNo(Boolean(sequenceSummary.mutatesSkillFiles))));
398
+ lines.push(listItem("Command sequence calls external AI APIs", yesNo(Boolean(sequenceSummary.callsExternalAiApis))));
399
+ const operatorRunbook = commandContract.operatorRunbook || {};
400
+ lines.push(listItem("Operator runbook stages", operatorRunbook.stageCount || 0));
401
+ lines.push(listItem("Operator runbook keys", (operatorRunbook.stageKeys || []).join(", ") || "none"));
402
+ lines.push(listItem("Operator runbook required stages", operatorRunbook.requiredStageCount || 0));
403
+ lines.push(listItem("Operator runbook next stage", operatorRunbook.nextStageKey || "none"));
404
+ lines.push(listItem("Operator runbook next required stage", operatorRunbook.nextRequiredStageKey || "none"));
405
+ lines.push(listItem("Operator runbook next required command stage", operatorRunbook.nextRequiredCommandStageKey || "none"));
406
+ if (operatorRunbook.stageSelection?.strategy) {
407
+ lines.push(listItem("Operator runbook stage selection", operatorRunbook.stageSelection.strategy));
408
+ if (operatorRunbook.stageSelection.decision?.action) {
409
+ lines.push(listItem("Operator runbook decision", operatorRunbook.stageSelection.decision.action));
410
+ if (operatorRunbook.stageSelection.decision.safety?.level) {
411
+ lines.push(listItem("Operator runbook decision safety", operatorRunbook.stageSelection.decision.safety.level));
412
+ }
413
+ if (Array.isArray(operatorRunbook.stageSelection.decision.commands)) {
414
+ lines.push(listItem("Operator runbook decision commands", operatorRunbook.stageSelection.decision.commands.map((command) => command.key).join(", ") || "none"));
415
+ }
416
+ if (operatorRunbook.stageSelection.decision.nextCommandKey) {
417
+ lines.push(listItem("Operator runbook decision next command", operatorRunbook.stageSelection.decision.nextCommandKey));
418
+ }
419
+ }
420
+ if (operatorRunbook.stageSelection.nextStage?.key) {
421
+ const nextStageLabel = operatorRunbook.stageSelection.nextStage.required ? "required" : "optional";
422
+ lines.push(listItem("Operator runbook selected stage", `${operatorRunbook.stageSelection.nextStage.key} (${nextStageLabel}, ${operatorRunbook.stageSelection.nextStage.kind})`));
423
+ }
424
+ }
425
+ const commandSequence = Array.isArray(commandContract.commandSequence) ? commandContract.commandSequence : [];
426
+ if (commandSequence.length > 0) {
427
+ lines.push("");
428
+ lines.push("Command sequence:");
429
+ for (const item of commandSequence) {
430
+ const safetyLevel = item.safety?.level || "unknown";
431
+ lines.push(`- ${item.step}. ${item.key} (${item.runPolicy || "unknown"} / ${safetyLevel}): \`${item.command}\``);
432
+ }
433
+ }
434
+ const runbookStages = Array.isArray(operatorRunbook.stages) ? operatorRunbook.stages : [];
435
+ if (runbookStages.length > 0) {
436
+ lines.push("");
437
+ lines.push("Operator runbook:");
438
+ for (const stage of runbookStages) {
439
+ const required = stage.required ? "required" : "optional";
440
+ const commandKeys = Array.isArray(stage.commandKeys) && stage.commandKeys.length > 0
441
+ ? stage.commandKeys.join(", ")
442
+ : "manual";
443
+ lines.push(`- ${stage.step}. ${stage.key} (${required} / ${stage.kind || "unknown"}): ${commandKeys}`);
444
+ }
445
+ }
446
+ if (commandContract.nextAction) lines.push(listItem("Next action", commandContract.nextAction));
447
+ const missingCommandKeys = Array.isArray(commandContract.missingCommandKeys) ? commandContract.missingCommandKeys : [];
448
+ const unexpectedCommandKeys = Array.isArray(commandContract.unexpectedCommandKeys) ? commandContract.unexpectedCommandKeys : [];
449
+ if (missingCommandKeys.length > 0) lines.push(listItem("Missing command keys", missingCommandKeys.join(", ")));
450
+ if (unexpectedCommandKeys.length > 0) lines.push(listItem("Unexpected command keys", unexpectedCommandKeys.join(", ")));
451
+ const failedChecks = Array.isArray(commandContract.failedChecks) ? commandContract.failedChecks : [];
452
+ if (failedChecks.length > 0) {
453
+ lines.push("");
454
+ lines.push("Failed command checks:");
455
+ for (const check of failedChecks) {
456
+ lines.push(`- ${check.id}: ${check.message}`);
457
+ }
458
+ }
459
+
460
+ lines.push("", "## Recommendations", "");
461
+ for (const recommendation of payload.recommendations || []) {
462
+ lines.push(`- ${recommendation.level}: ${recommendation.text}`);
463
+ }
464
+
465
+ const privacy = payload.privacy || {};
466
+ lines.push("", "## Privacy And Boundaries", "");
467
+ lines.push(listItem("Mutates learning profile", yesNo(Boolean(privacy.mutatesProfile))));
468
+ lines.push(listItem("Mutates review file", yesNo(Boolean(privacy.mutatesReviewFile))));
469
+ lines.push(listItem("Mutates skill files", yesNo(Boolean(privacy.mutatesSkillFiles))));
470
+ lines.push(listItem("Calls external AI APIs", yesNo(Boolean(privacy.callsExternalAiApis))));
471
+ lines.push(listItem("Stores raw brief text", yesNo(Boolean(privacy.storesRawBriefText))));
472
+ lines.push(listItem("Includes entry text preview", yesNo(Boolean(privacy.exposesEntryTextPreview))));
473
+
474
+ return `${lines.join("\n")}\n`;
475
+ }
476
+
477
+ export function renderSkillProposalReviewCheckReport(payload, {
478
+ generatedAt = new Date(),
479
+ } = {}) {
480
+ const generatedAtText = generatedAt instanceof Date ? generatedAt.toISOString() : String(generatedAt || "");
481
+ const lines = [
482
+ "# Skill Proposal Review Check",
483
+ "",
484
+ listItem("Generated", generatedAtText),
485
+ listItem("Status", payload.status),
486
+ listItem("Proposal status", payload.proposalStatus),
487
+ listItem("Signal status", payload.signalStatus),
488
+ listItem("File", payload.file),
489
+ listItem("Usage sidecar", payload.usageFile),
490
+ listItem("Signal source", payload.signalSource),
491
+ listItem("Review file", payload.reviewFile || "not configured"),
492
+ listItem("Proposals", payload.proposalCount),
493
+ listItem("Pending review", payload.pendingReviewCount),
494
+ listItem("Reviewed", payload.reviewedCount),
495
+ "",
496
+ "## Checks",
497
+ "",
498
+ ];
499
+
500
+ for (const check of payload.checks || []) {
501
+ lines.push(`- ${check.level}: ${check.id} - ${check.message}`);
502
+ }
503
+
504
+ const review = payload.review || {};
505
+ lines.push("", "## Review Summary", "");
506
+ lines.push(listItem("Exists", yesNo(Boolean(review.exists))));
507
+ lines.push(listItem("Status", review.status || "unknown"));
508
+ lines.push(listItem("Decisions", review.decisionCount || 0));
509
+ lines.push(listItem("Matched", review.matchedCount || 0));
510
+ lines.push(listItem("Stale", review.staleCount || 0));
511
+ lines.push(listItem("Applied", review.appliedCount || 0));
512
+ lines.push(listItem("Rejected", review.rejectedCount || 0));
513
+ lines.push(listItem("Accepted", review.acceptedCount || 0));
514
+ lines.push(listItem("Deferred", review.deferredCount || 0));
515
+ if (Array.isArray(review.warnings) && review.warnings.length > 0) {
516
+ lines.push("", "Warnings:");
517
+ for (const warning of review.warnings) lines.push(`- ${warning}`);
518
+ }
519
+
520
+ lines.push("", "## Recommendations", "");
521
+ for (const recommendation of payload.recommendations || []) {
522
+ lines.push(`- ${recommendation.level}: ${recommendation.text}`);
523
+ }
524
+
525
+ lines.push("", "## Privacy And Boundaries", "");
526
+ lines.push(listItem("Mutates learning profile", yesNo(Boolean(payload.privacy?.mutatesProfile))));
527
+ lines.push(listItem("Mutates skill files", yesNo(Boolean(payload.privacy?.mutatesSkillFiles))));
528
+ lines.push(listItem("Calls external AI APIs", yesNo(Boolean(payload.privacy?.callsExternalAiApis))));
529
+ lines.push(listItem("Stores raw brief text", yesNo(Boolean(payload.privacy?.storesRawBriefText))));
530
+
531
+ return `${lines.join("\n")}\n`;
532
+ }