@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,283 @@
1
+ // Aggregate workspace readiness report and next actions for `design-ai workspace`.
2
+
3
+ import { existsSync } from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import {
7
+ defaultLearningFile,
8
+ learningEvalReport,
9
+ learningStats,
10
+ learningUsageStats,
11
+ listLearningRestoreBackups,
12
+ } from "./learn.mjs";
13
+ import { DESIGN_AI_HOME } from "./paths.mjs";
14
+ import { collectGitReport, runGitCommand } from "./workspace-git.mjs";
15
+ import {
16
+ action,
17
+ assessLearningEvalFreshness,
18
+ assessLearningRestoreBackupReadiness,
19
+ assessLearningUsageReadiness,
20
+ collectLearningEvalReport,
21
+ collectLearningReport,
22
+ collectLearningRestoreBackupsReport,
23
+ collectLearningUsageReport,
24
+ collectRetrievalIndexReport,
25
+ defaultLearningCurationReportPath,
26
+ defaultLearningEvalPath,
27
+ defaultLearningUsagePath,
28
+ learningCurationCommand,
29
+ learningCurationReportCommand,
30
+ learningRestoreBackupPruneCommand,
31
+ learningRestoreBackupsCommand,
32
+ pushUniqueAction,
33
+ quoteShellArg,
34
+ } from "./workspace-learning.mjs";
35
+ import {
36
+ collectReleaseScriptReport,
37
+ collectRepositoryReport,
38
+ } from "./workspace-repo.mjs";
39
+
40
+ export function buildWorkspaceNextActions({
41
+ git,
42
+ repository,
43
+ learning,
44
+ learningUsage,
45
+ learningEval,
46
+ learningRestoreBackups,
47
+ retrievalIndex = null,
48
+ release,
49
+ }) {
50
+ const actions = [];
51
+
52
+ if (retrievalIndex && !retrievalIndex.fresh) {
53
+ actions.push(action(
54
+ "warn",
55
+ "Retrieval index is stale or unreadable for the current corpus/learning sources. Rebuild it before relying on ranked retrieval.",
56
+ retrievalIndex.buildCommand || "design-ai index --build",
57
+ ));
58
+ }
59
+
60
+ if (!git.isRepo) {
61
+ actions.push(action("warn", "Open design-ai from a git workspace before preparing shared changes."));
62
+ } else {
63
+ if (!git.clean) {
64
+ actions.push(action("warn", "Review local changes before committing or pushing.", "git status --short"));
65
+ } else if (git.hasIgnoredLocalArtifacts) {
66
+ actions.push(action("info", "Ignored local portfolio/evidence artifacts are present and excluded from workspace readiness.", "git status --short"));
67
+ } else if (!git.upstream && git.branch) {
68
+ actions.push(action("warn", "Set an upstream branch before sharing dogfood work.", `git push -u origin ${git.branch}`));
69
+ } else if (git.behind > 0) {
70
+ actions.push(action("warn", "Rebase or merge remote changes before continuing release work.", "git pull --rebase"));
71
+ } else if (git.ahead > 0) {
72
+ actions.push(action("info", "Push committed local work when the current phase is ready.", "git push"));
73
+ } else {
74
+ actions.push(action("pass", "Git workspace is clean and synced."));
75
+ }
76
+ }
77
+
78
+ if (repository && !repository.metadataAligned) {
79
+ actions.push(action("fail", "Fix package/plugin repository metadata before preparing shared dogfood builds.", "npm run release:metadata"));
80
+ } else if (repository?.remoteAligned === false) {
81
+ actions.push(action("warn", "Verify git remote points at the canonical design-ai repository before pushing.", "git remote -v"));
82
+ }
83
+
84
+ if (release.missing.length > 0) {
85
+ actions.push(action("fail", `Restore required release script(s): ${release.missing.join(", ")}`));
86
+ }
87
+
88
+ if (learning.error) {
89
+ actions.push(action("fail", "Repair the local learning profile before relying on personalized prompt context.", "design-ai learn --audit"));
90
+ } else if (learning.auditSummary.status !== "pass") {
91
+ pushUniqueAction(actions, action(
92
+ "warn",
93
+ "Preview archive-first learning curation before dogfooding prompts.",
94
+ learningCurationCommand(learning, learningUsage),
95
+ ));
96
+ pushUniqueAction(actions, action(
97
+ "info",
98
+ "Save a Markdown learning curation report before applying archive actions.",
99
+ learningCurationReportCommand(learning, learningUsage),
100
+ ));
101
+ } else if (learning.count === 0) {
102
+ actions.push(action("info", "Capture reviewed feedback after checks to make dogfood runs improve over time.", "design-ai check artifact.md --learn --yes"));
103
+ } else if (!learningEval) {
104
+ actions.push(action(
105
+ "info",
106
+ "Generate a local learning eval checkpoint before relying on personalized prompt context.",
107
+ `design-ai learn --eval-template --file ${quoteShellArg(learning.file)} --out ${quoteShellArg(defaultLearningEvalPath(learning.file))}`,
108
+ ));
109
+ }
110
+
111
+ if (learningUsage) {
112
+ const usageCommand = `design-ai learn --usage --file ${quoteShellArg(learningUsage.file)} --usage-file ${quoteShellArg(learningUsage.usageFile)}`;
113
+ if (learningUsage.error) {
114
+ actions.push(action("fail", "Repair the local learning usage sidecar before trusting prompt/pack usage analytics.", usageCommand));
115
+ } else if (learningUsage.readiness?.status === "warn") {
116
+ pushUniqueAction(actions, action(
117
+ "warn",
118
+ "Preview usage-aware learning curation before curating learning entries.",
119
+ learningCurationCommand(learning, learningUsage),
120
+ ));
121
+ pushUniqueAction(actions, action(
122
+ "info",
123
+ "Save a Markdown usage-aware learning curation report before applying archive actions.",
124
+ learningCurationReportCommand(learning, learningUsage),
125
+ ));
126
+ } else if (learningUsage.readiness?.status === "pass") {
127
+ actions.push(action("pass", "Learning usage sidecar is aligned with the active profile.", usageCommand));
128
+ } else {
129
+ actions.push(action("info", "Record prompt/pack --with-learning usage before judging which learning entries are useful.", usageCommand));
130
+ }
131
+ } else if (!learning.error && learning.auditSummary.status === "pass" && learning.count > 0) {
132
+ actions.push(action(
133
+ "info",
134
+ "Run prompt or pack with --with-learning to create a local learning usage sidecar.",
135
+ "design-ai prompt \"your brief\" --with-learning",
136
+ ));
137
+ }
138
+
139
+ if (learningEval) {
140
+ const evalCommand = `design-ai learn --eval --from-file ${quoteShellArg(learningEval.source)} --file ${quoteShellArg(learningEval.file)} --strict`;
141
+ const regenerateCommand = `design-ai learn --eval-template --file ${quoteShellArg(learningEval.file)} --out ${quoteShellArg(learningEval.source)} --force`;
142
+ if (learningEval.error) {
143
+ actions.push(action("fail", "Fix the local learning eval checkpoint before using workspace readiness as a gate.", evalCommand));
144
+ } else if (learningEval.status === "fail") {
145
+ actions.push(action("fail", "Review failed local learning eval checkpoint cases before trusting prompt/pack selection.", evalCommand));
146
+ } else if (learningEval.status === "warn") {
147
+ actions.push(action("warn", "Review local learning eval checkpoint warnings before relying on personalized prompt context.", evalCommand));
148
+ } else if (learningEval.freshness?.status === "warn") {
149
+ actions.push(action("warn", "Regenerate the local learning eval checkpoint because the learning profile changed after it was created.", regenerateCommand));
150
+ } else {
151
+ actions.push(action("pass", "Learning eval checkpoints pass.", evalCommand));
152
+ }
153
+ }
154
+
155
+ if (learningRestoreBackups) {
156
+ const readiness = learningRestoreBackups.readiness || {};
157
+ if (learningRestoreBackups.error) {
158
+ actions.push(action("fail", "Repair the learning restore rollback backup inventory before relying on restore readiness.", learningRestoreBackupsCommand(learning)));
159
+ } else if (readiness.pruneCandidateCount > 0) {
160
+ actions.push(action(
161
+ "info",
162
+ "Preview pruning older learning restore rollback backups after keeping the newest recovery points.",
163
+ learningRestoreBackupPruneCommand(learning, readiness.keep || DEFAULT_RESTORE_BACKUP_KEEP),
164
+ ));
165
+ } else if (learningRestoreBackups.totalCount > 0) {
166
+ actions.push(action("pass", "Learning restore rollback backups are available for rollback review.", learningRestoreBackupsCommand(learning)));
167
+ }
168
+ }
169
+
170
+ if (release.available.includes("test")) {
171
+ actions.push(action("info", "Run CLI unit tests before handing this phase off.", "npm test"));
172
+ }
173
+ if (release.available.includes("audit:strict")) {
174
+ actions.push(action("info", "Run the strict repository audit before repo cleanup or team distribution.", "npm run audit:strict"));
175
+ }
176
+ if (release.available.includes("package:smoke")) {
177
+ actions.push(action("info", "Use package smoke before publishing or testing the packed install path.", "npm run package:smoke"));
178
+ }
179
+
180
+ return actions;
181
+ }
182
+
183
+ export function hasWorkspaceStrictIssues(report) {
184
+ return (report?.nextActions || []).some((item) => item.level === "fail" || item.level === "warn");
185
+ }
186
+
187
+ export function collectWorkspaceReport({
188
+ root = process.cwd(),
189
+ sourceRoot = DESIGN_AI_HOME,
190
+ learningFilePath = defaultLearningFile(),
191
+ learningUsagePath = "",
192
+ learningEvalPath = "",
193
+ gitRunner = runGitCommand,
194
+ learningStatsProvider = learningStats,
195
+ learningUsageStatsProvider = learningUsageStats,
196
+ learningEvalReportProvider = learningEvalReport,
197
+ learningRestoreBackupsProvider = listLearningRestoreBackups,
198
+ } = {}) {
199
+ const resolvedRoot = path.resolve(root);
200
+ const resolvedSourceRoot = path.resolve(sourceRoot);
201
+ const resolvedLearningFile = path.resolve(learningFilePath);
202
+ const resolvedLearningUsagePath = learningUsagePath || (
203
+ existsSync(defaultLearningUsagePath(resolvedLearningFile)) ? defaultLearningUsagePath(resolvedLearningFile) : ""
204
+ );
205
+ const resolvedLearningEvalPath = learningEvalPath || (
206
+ existsSync(defaultLearningEvalPath(resolvedLearningFile)) ? defaultLearningEvalPath(resolvedLearningFile) : ""
207
+ );
208
+ const git = collectGitReport({ root: resolvedRoot, gitRunner });
209
+ const learning = collectLearningReport({
210
+ filePath: resolvedLearningFile,
211
+ learningStatsProvider,
212
+ });
213
+ const learningUsage = collectLearningUsageReport({
214
+ learningFilePath: resolvedLearningFile,
215
+ learningUsagePath: resolvedLearningUsagePath,
216
+ learningUsageStatsProvider,
217
+ });
218
+ const learningEval = collectLearningEvalReport({
219
+ learningFilePath: resolvedLearningFile,
220
+ learningEvalPath: resolvedLearningEvalPath,
221
+ learningEvalReportProvider,
222
+ });
223
+ const learningRestoreBackups = collectLearningRestoreBackupsReport({
224
+ learningFilePath: resolvedLearningFile,
225
+ learningRestoreBackupsProvider,
226
+ });
227
+ const learningEvalWithFreshness = learningEval
228
+ ? {
229
+ ...learningEval,
230
+ freshness: assessLearningEvalFreshness({ learning, learningEval }),
231
+ }
232
+ : null;
233
+ const retrievalIndex = collectRetrievalIndexReport({
234
+ sourceRoot: resolvedSourceRoot,
235
+ learningFilePath: resolvedLearningFile,
236
+ });
237
+ const release = collectReleaseScriptReport({ sourceRoot: resolvedSourceRoot });
238
+ const repository = collectRepositoryReport({ sourceRoot: resolvedSourceRoot, git });
239
+ const nextActions = buildWorkspaceNextActions({
240
+ git,
241
+ repository,
242
+ learning,
243
+ learningUsage,
244
+ learningEval: learningEvalWithFreshness,
245
+ learningRestoreBackups,
246
+ retrievalIndex,
247
+ release,
248
+ });
249
+
250
+ return {
251
+ context: {
252
+ cwd: process.cwd(),
253
+ root: resolvedRoot,
254
+ sourceRoot: resolvedSourceRoot,
255
+ packageName: release.packageName,
256
+ version: release.version,
257
+ },
258
+ git,
259
+ repository,
260
+ learning,
261
+ learningUsage,
262
+ learningEval: learningEvalWithFreshness,
263
+ learningRestoreBackups,
264
+ retrievalIndex,
265
+ release,
266
+ nextActions,
267
+ };
268
+ }
269
+
270
+ export function formatWorkspaceJson(report) {
271
+ return JSON.stringify({
272
+ context: report.context,
273
+ git: report.git,
274
+ repository: report.repository,
275
+ learning: report.learning,
276
+ learningUsage: report.learningUsage || null,
277
+ learningEval: report.learningEval || null,
278
+ learningRestoreBackups: report.learningRestoreBackups || null,
279
+ retrievalIndex: report.retrievalIndex || null,
280
+ release: report.release,
281
+ nextActions: report.nextActions,
282
+ }, null, 2);
283
+ }
@@ -0,0 +1,99 @@
1
+ // Shared test helpers for the workspace.* test suite.
2
+
3
+ import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
4
+ import { tmpdir } from "node:os";
5
+ import path from "node:path";
6
+
7
+ export function ok(stdout = "") {
8
+ return { ok: true, status: 0, stdout, stderr: "", error: "" };
9
+ }
10
+
11
+
12
+ export function fail(stderr = "") {
13
+ return { ok: false, status: 1, stdout: "", stderr, error: "" };
14
+ }
15
+
16
+
17
+ export function fakeGit(responses) {
18
+ return (args) => responses[args.join(" ")] || fail("unexpected git command");
19
+ }
20
+
21
+
22
+ export function withTempDir(fn) {
23
+ const dir = mkdtempSync(path.join(tmpdir(), "design-ai-workspace-"));
24
+ const cleanup = () => rmSync(dir, { recursive: true, force: true });
25
+ try {
26
+ const result = fn(dir);
27
+ if (result && typeof result.then === "function") {
28
+ return result.finally(cleanup);
29
+ }
30
+ cleanup();
31
+ return result;
32
+ } catch (error) {
33
+ cleanup();
34
+ throw error;
35
+ }
36
+ }
37
+
38
+ export function writeSourceMetadata(sourceRoot, scripts = {}) {
39
+ mkdirSync(path.join(sourceRoot, ".claude-plugin"), { recursive: true });
40
+ writeFileSync(
41
+ path.join(sourceRoot, "package.json"),
42
+ JSON.stringify({
43
+ name: "@design-ai/cli",
44
+ version: "4.13.0",
45
+ repository: { type: "git", url: "git+https://github.com/sungjin9288/design-ai.git" },
46
+ homepage: "https://github.com/sungjin9288/design-ai#readme",
47
+ bugs: { url: "https://github.com/sungjin9288/design-ai/issues" },
48
+ scripts,
49
+ }),
50
+ "utf8",
51
+ );
52
+ writeFileSync(
53
+ path.join(sourceRoot, ".claude-plugin", "plugin.json"),
54
+ JSON.stringify({
55
+ version: "4.13.0",
56
+ homepage: "https://github.com/sungjin9288/design-ai",
57
+ repository: "https://github.com/sungjin9288/design-ai",
58
+ }),
59
+ "utf8",
60
+ );
61
+ }
62
+
63
+
64
+ export function fullReleaseScripts() {
65
+ return {
66
+ test: "node --test cli/lib/*.test.mjs",
67
+ "audit:strict": "python3 -B tools/audit/run-all.py --strict",
68
+ "release:metadata": "python3 -B tools/audit/release-metadata.py",
69
+ "release:self-test": "npm run smoke:assertions:self-test",
70
+ "package:smoke": "python3 -B tools/audit/package-smoke.py --pack",
71
+ "release:check": "npm test && npm run audit:strict",
72
+ "ci:local": "python3 -B tools/audit/local-ci.py",
73
+ };
74
+ }
75
+
76
+ export async function captureConsole(fn) {
77
+ const lines = [];
78
+ const originalLog = console.log;
79
+ const originalExitCode = process.exitCode;
80
+ console.log = (...args) => {
81
+ lines.push(args.join(" "));
82
+ };
83
+ process.exitCode = undefined;
84
+ try {
85
+ await fn();
86
+ return {
87
+ stdout: lines.join("\n"),
88
+ exitCode: process.exitCode,
89
+ };
90
+ } finally {
91
+ console.log = originalLog;
92
+ process.exitCode = originalExitCode;
93
+ }
94
+ }
95
+
96
+ export async function captureStdout(fn) {
97
+ const { stdout } = await captureConsole(fn);
98
+ return stdout;
99
+ }