@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,373 @@
1
+ // Human-readable output for learn profile list/audit/curation/stats/usage results.
2
+
3
+ import {
4
+ LEARNING_CATEGORIES,
5
+ loadLearningProfile,
6
+ selectLearningEntrySet,
7
+ } from "../lib/learn.mjs";
8
+ import { dim, header, info } from "../lib/log.mjs";
9
+
10
+ export function printLearningImportVerification(payload) {
11
+ header("design-ai learn", "Learning import verification");
12
+ info(`Source: ${payload.source}`);
13
+ info(`Importable: ${payload.importable ? "yes" : "no"}`);
14
+ info(`Entries: ${payload.count}`);
15
+ info(`Audit: ${payload.auditSummary.status} (${payload.auditSummary.failures} failure(s), ${payload.auditSummary.warnings} warning(s))`);
16
+ console.log();
17
+
18
+ if (payload.issues.length === 0) {
19
+ console.log("No learning import issues found. No changes made.");
20
+ return;
21
+ }
22
+
23
+ for (const issue of payload.issues) {
24
+ const entry = issue.entryId ? ` (${issue.entryId})` : "";
25
+ console.log(`${issue.level.toUpperCase()} ${issue.code}${entry}: ${issue.message}`);
26
+ }
27
+ console.log();
28
+ console.log("No changes made. Review warnings before importing or using learned context.");
29
+ }
30
+
31
+ export function learningFilter(parsed) {
32
+ return {
33
+ category: parsed.categorySpecified ? parsed.category : "",
34
+ limit: parsed.limit,
35
+ query: parsed.query,
36
+ };
37
+ }
38
+
39
+ export function listPayload(filePath, parsed) {
40
+ const profile = loadLearningProfile(filePath);
41
+ const filter = learningFilter(parsed);
42
+ const { entries, selection } = selectLearningEntrySet(profile, {
43
+ ...filter,
44
+ includeFallback: false,
45
+ });
46
+ const payload = {
47
+ file: filePath,
48
+ version: profile.version,
49
+ updatedAt: profile.updatedAt,
50
+ category: filter.category,
51
+ query: filter.query,
52
+ limit: filter.limit || null,
53
+ entries,
54
+ count: entries.length,
55
+ totalCount: profile.entries.length,
56
+ };
57
+ if (parsed.explain) {
58
+ payload.selection = selection;
59
+ }
60
+ return payload;
61
+ }
62
+
63
+ export function printList(payload) {
64
+ header("design-ai learn", "Local learning profile");
65
+ info(`File: ${payload.file}`);
66
+ info(`Entries: ${payload.count}/${payload.totalCount}`);
67
+ if (payload.category) info(`Category: ${payload.category}`);
68
+ if (payload.query) info(`Query: ${payload.query}`);
69
+ if (payload.limit) info(`Limit: ${payload.limit}`);
70
+ if (payload.selection) {
71
+ info("Explain: selection score, matched tokens, and reason");
72
+ }
73
+ console.log();
74
+
75
+ if (payload.entries.length === 0) {
76
+ console.log(payload.category || payload.query || payload.limit
77
+ ? "No local learning preferences match the current filters."
78
+ : "No local learning preferences are stored yet.");
79
+ return;
80
+ }
81
+
82
+ for (let i = 0; i < payload.entries.length; i += 1) {
83
+ const entry = payload.entries[i];
84
+ const selected = payload.selection?.selected?.[i];
85
+ console.log(`${i + 1}. [${entry.category}] ${entry.text}`);
86
+ console.log(` ${dim(`${entry.id} · ${entry.createdAt || "unknown time"}`)}`);
87
+ if (selected) {
88
+ const matched = selected.matchedTokens.length > 0
89
+ ? selected.matchedTokens.join(", ")
90
+ : "none";
91
+ console.log(` ${dim(`score ${selected.score} · matched ${matched} · reason ${selected.reason}`)}`);
92
+ }
93
+ }
94
+ }
95
+
96
+ function formatCategoryCounts(categoryCounts) {
97
+ return LEARNING_CATEGORIES
98
+ .filter((category) => categoryCounts[category])
99
+ .map((category) => `${category} ${categoryCounts[category]}`)
100
+ .join(", ");
101
+ }
102
+
103
+ function formatCounts(counts) {
104
+ return Object.entries(counts || {})
105
+ .sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
106
+ .map(([key, count]) => `${key} ${count}`)
107
+ .join(", ");
108
+ }
109
+
110
+ export function printAudit(payload) {
111
+ header("design-ai learn", "Local learning profile audit");
112
+ info(`File: ${payload.file}`);
113
+ info(`Exists: ${payload.exists ? "yes" : "no"}`);
114
+ info(`Entries: ${payload.count}`);
115
+ info(`Status: ${payload.summary.status}`);
116
+ info(`Issues: ${payload.summary.failures} failure(s), ${payload.summary.warnings} warning(s)`);
117
+
118
+ const categories = formatCategoryCounts(payload.categoryCounts);
119
+ if (categories) info(`Categories: ${categories}`);
120
+ console.log();
121
+
122
+ if (!payload.exists) {
123
+ console.log("No local learning profile file exists yet.");
124
+ return;
125
+ }
126
+
127
+ if (payload.issues.length === 0) {
128
+ console.log("No learning profile issues found.");
129
+ return;
130
+ }
131
+
132
+ for (const issue of payload.issues) {
133
+ const entry = issue.entryId ? ` (${issue.entryId})` : "";
134
+ console.log(`${issue.level.toUpperCase()} ${issue.code}${entry}: ${issue.message}`);
135
+ }
136
+
137
+ if (payload.suggestions.length > 0) {
138
+ console.log();
139
+ console.log("Suggested cleanup:");
140
+ for (const suggestion of payload.suggestions) {
141
+ const entry = suggestion.entryId ? ` (${suggestion.entryId})` : "";
142
+ console.log(`- ${suggestion.action}${entry}: ${suggestion.message}`);
143
+ if (suggestion.command) {
144
+ console.log(` ${dim(suggestion.command)}`);
145
+ }
146
+ }
147
+ }
148
+ }
149
+
150
+ export function printAuditFix(payload) {
151
+ header("design-ai learn", payload.dryRun ? "Learning audit cleanup dry run" : "Learning audit cleanup applied");
152
+ info(`File: ${payload.file}`);
153
+ info(`Before: ${payload.before.status} (${payload.before.failures} failure(s), ${payload.before.warnings} warning(s))`);
154
+ info(`Cleanup entries: ${payload.cleanupCount}`);
155
+ if (payload.after) {
156
+ info(`After: ${payload.after.status} (${payload.after.failures} failure(s), ${payload.after.warnings} warning(s))`);
157
+ }
158
+ console.log();
159
+
160
+ if (payload.cleanup.length === 0) {
161
+ console.log("No safe cleanup suggestions are available.");
162
+ } else {
163
+ console.log(payload.dryRun ? "Would remove:" : "Removed:");
164
+ for (const fix of payload.cleanup) {
165
+ console.log(`- ${fix.entryId}: ${fix.actions.join(", ")} (${fix.issueCodes.join(", ")})`);
166
+ if (fix.command) {
167
+ console.log(` ${dim(fix.command)}`);
168
+ }
169
+ }
170
+ }
171
+
172
+ if (payload.skipped.length > 0) {
173
+ console.log();
174
+ console.log("Skipped:");
175
+ for (const skipped of payload.skipped) {
176
+ const entry = skipped.entryId ? ` (${skipped.entryId})` : "";
177
+ console.log(`- ${skipped.reason}${entry}: ${skipped.message}`);
178
+ }
179
+ }
180
+
181
+ if (payload.dryRun) {
182
+ console.log();
183
+ console.log("No changes made. Re-run with `--yes` instead of `--dry-run` to apply safe cleanup.");
184
+ }
185
+ }
186
+
187
+ export function printCuration(payload) {
188
+ header("design-ai learn", payload.dryRun ? "Learning curation preview" : "Learning curation applied");
189
+ info(`File: ${payload.file}`);
190
+ info(`Archive: ${payload.archiveFile}`);
191
+ info(`Before: ${payload.before.status} (${payload.before.failures} failure(s), ${payload.before.warnings} warning(s))`);
192
+ info(`Proposals: ${payload.proposalCount}`);
193
+ info(`Archive candidates: ${payload.archiveCount}`);
194
+ info(`Manual review: ${payload.manualReviewCount}`);
195
+ if (payload.after) {
196
+ info(`After: ${payload.after.status} (${payload.after.failures} failure(s), ${payload.after.warnings} warning(s))`);
197
+ }
198
+ console.log();
199
+
200
+ if (payload.proposals.length === 0) {
201
+ console.log("No learning curation candidates found.");
202
+ } else {
203
+ const archiveCandidates = payload.proposals.filter((proposal) => proposal.action === "archive");
204
+ const manualCandidates = payload.proposals.filter((proposal) => proposal.action === "manual-review");
205
+ if (archiveCandidates.length > 0) {
206
+ console.log(payload.dryRun ? "Would archive:" : "Archived:");
207
+ for (const proposal of archiveCandidates) {
208
+ console.log(`- ${proposal.entryId}: ${proposal.reason} (${proposal.issueCodes.join(", ")})`);
209
+ if (proposal.textPreview) console.log(` ${dim(proposal.textPreview)}`);
210
+ }
211
+ console.log();
212
+ }
213
+ if (manualCandidates.length > 0) {
214
+ console.log("Needs manual review:");
215
+ for (const proposal of manualCandidates) {
216
+ const label = proposal.entryId || "profile";
217
+ console.log(`- ${label}: ${proposal.reason} (${proposal.issueCodes.join(", ")})`);
218
+ if (proposal.textPreview) console.log(` ${dim(proposal.textPreview)}`);
219
+ }
220
+ console.log();
221
+ }
222
+ }
223
+
224
+ if (payload.skipped.length > 0) {
225
+ console.log("Skipped:");
226
+ for (const skipped of payload.skipped) {
227
+ const entry = skipped.entryId ? `${skipped.entryId}: ` : "";
228
+ console.log(`- ${entry}${skipped.reason}`);
229
+ }
230
+ console.log();
231
+ }
232
+
233
+ if (payload.usage?.exists || payload.usage?.error) {
234
+ console.log("Usage review:");
235
+ console.log(`- sidecar: ${payload.usage.usageFile}`);
236
+ console.log(`- events: ${payload.usage.eventCount}`);
237
+ console.log(`- review items: ${payload.usage.reviewCount}`);
238
+ if (payload.usage.error) {
239
+ console.log(`- error: ${payload.usage.error}`);
240
+ } else if (payload.usage.reviews.length === 0) {
241
+ console.log("- no usage-based curation review items found");
242
+ } else {
243
+ for (const review of payload.usage.reviews) {
244
+ const label = review.entryId || "usage";
245
+ console.log(`- ${label}: ${review.reason} (${review.action})`);
246
+ if (review.textPreview) console.log(` ${dim(review.textPreview)}`);
247
+ }
248
+ }
249
+ console.log(dim("Usage review is advisory; --curate only archives duplicate/sensitive audit candidates."));
250
+ console.log();
251
+ }
252
+
253
+ if (payload.dryRun) {
254
+ console.log("No changes made. Re-run with `--yes` to move archive candidates into the learning archive.");
255
+ }
256
+ }
257
+
258
+ export function printStats(payload) {
259
+ header("design-ai learn", "Local learning profile stats");
260
+ info(`File: ${payload.file}`);
261
+ info(`Exists: ${payload.exists ? "yes" : "no"}`);
262
+ info(`Entries: ${payload.count}`);
263
+ info(`Updated: ${payload.updatedAt || "unknown"}`);
264
+ info(`Audit: ${payload.auditSummary.status} (${payload.auditSummary.failures} failure(s), ${payload.auditSummary.warnings} warning(s))`);
265
+
266
+ const categories = formatCategoryCounts(payload.categoryCounts);
267
+ if (categories) info(`Categories: ${categories}`);
268
+
269
+ const sources = Object.entries(payload.sourceCounts)
270
+ .map(([source, count]) => `${source} ${count}`)
271
+ .join(", ");
272
+ if (sources) info(`Sources: ${sources}`);
273
+ console.log();
274
+
275
+ if (!payload.exists) {
276
+ console.log("No local learning profile file exists yet.");
277
+ return;
278
+ }
279
+
280
+ if (payload.auditSummary.failures > 0) {
281
+ console.log("Profile stats are limited because the profile has audit failures. Run `design-ai learn --audit` for details.");
282
+ return;
283
+ }
284
+
285
+ if (payload.count === 0) {
286
+ console.log("No local learning preferences are stored yet.");
287
+ return;
288
+ }
289
+
290
+ if (!payload.latestEntry) {
291
+ console.log("No parseable learning entry timestamps found. Run `design-ai learn --audit` for details.");
292
+ return;
293
+ }
294
+
295
+ if (payload.latestEntry) {
296
+ console.log(`Latest: [${payload.latestEntry.category}] ${payload.latestEntry.textPreview}`);
297
+ console.log(` ${dim(`${payload.latestEntry.id} · ${payload.latestEntry.createdAt}`)}`);
298
+ }
299
+ if (payload.oldestEntry && payload.oldestEntry.id !== payload.latestEntry?.id) {
300
+ console.log(`Oldest: [${payload.oldestEntry.category}] ${payload.oldestEntry.textPreview}`);
301
+ console.log(` ${dim(`${payload.oldestEntry.id} · ${payload.oldestEntry.createdAt}`)}`);
302
+ }
303
+ }
304
+
305
+ export function printUsage(payload) {
306
+ header("design-ai learn", "Local learning usage report");
307
+ info(`File: ${payload.file}`);
308
+ info(`Usage sidecar: ${payload.usageFile}`);
309
+ info(`Usage exists: ${payload.exists ? "yes" : "no"}`);
310
+ info(`Events: ${payload.eventCount}`);
311
+ info(`Profile entries: ${payload.profileEntryCount}`);
312
+ info(`Used entries: ${payload.usedEntryCount}`);
313
+ info(`Unused entries: ${payload.unusedEntryCount}`);
314
+ info(`Stale selected ids: ${payload.staleSelectedEntryCount}`);
315
+ info(`Updated: ${payload.updatedAt || "unknown"}`);
316
+
317
+ const commands = formatCounts(payload.commandCounts);
318
+ if (commands) info(`Commands: ${commands}`);
319
+ const routes = formatCounts(payload.routeCounts);
320
+ if (routes) info(`Routes: ${routes}`);
321
+ const categories = formatCounts(payload.categoryCounts);
322
+ if (categories) info(`Categories: ${categories}`);
323
+ console.log();
324
+
325
+ if (!payload.exists) {
326
+ console.log("No local learning usage sidecar exists yet.");
327
+ console.log("Run `design-ai prompt \"...\" --with-learning` or `design-ai pack \"...\" --with-learning` to record usage metadata.");
328
+ return;
329
+ }
330
+
331
+ if (payload.eventCount === 0) {
332
+ console.log("No local learning usage events are stored yet.");
333
+ return;
334
+ }
335
+
336
+ if (payload.topSelectedEntries.length > 0) {
337
+ console.log("Top selected entries:");
338
+ for (const entry of payload.topSelectedEntries) {
339
+ console.log(`- ${entry.id}: ${entry.usageCount} use(s) [${entry.category}] ${entry.textPreview}`);
340
+ if (entry.latestUsedAt) console.log(` ${dim(`latest ${entry.latestUsedAt}`)}`);
341
+ }
342
+ console.log();
343
+ }
344
+
345
+ if (payload.recentEvents.length > 0) {
346
+ console.log("Recent events:");
347
+ for (const event of payload.recentEvents) {
348
+ const route = event.routeId || "unrouted";
349
+ const ids = event.selectedEntryIds.length > 0 ? event.selectedEntryIds.join(", ") : "none";
350
+ console.log(`- ${event.createdAt}: ${event.command} / ${route} selected ${ids}`);
351
+ console.log(` ${dim(`briefHash ${event.briefHash || "none"} · matched ${event.matchedCount}/${event.candidateCount} · fallback ${event.fallbackCount}`)}`);
352
+ }
353
+ console.log();
354
+ }
355
+
356
+ if (payload.unusedEntryIds.length > 0) {
357
+ console.log(`Unused active entry ids: ${payload.unusedEntryIds.join(", ")}`);
358
+ }
359
+ if (payload.staleSelectedEntryIds.length > 0) {
360
+ console.log(`Stale selected entry ids: ${payload.staleSelectedEntryIds.join(", ")}`);
361
+ }
362
+
363
+ if (payload.recommendations.length > 0) {
364
+ console.log();
365
+ console.log("Recommendations:");
366
+ for (const recommendation of payload.recommendations) {
367
+ console.log(`- ${recommendation.level}: ${recommendation.text}`);
368
+ }
369
+ }
370
+
371
+ console.log();
372
+ console.log("Privacy: usage events store selected entry ids and a short brief hash, not raw brief text.");
373
+ }