@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,361 @@
1
+ // Agent development backlog actions and action plan derived from local learning signals.
2
+
3
+ import {
4
+ buildAgentBacklogExecutionQueue,
5
+ classifyAgentBacklogCommand,
6
+ summarizeAgentBacklogCommandSafety,
7
+ } from "./signals-backlog-commands.mjs";
8
+ import {
9
+ defaultLearningEvalReportPath,
10
+ evalReportPathForTemplate,
11
+ } from "./signals-eval.mjs";
12
+ import { commandFromArgs, commandSpec } from "./signals-shared.mjs";
13
+ import { defaultLearningEvalPath } from "./workspace.mjs";
14
+
15
+ export function agentAction({
16
+ id,
17
+ priority,
18
+ category,
19
+ title,
20
+ rationale,
21
+ command = "",
22
+ commandArgs = [],
23
+ applyCommand = "",
24
+ applyCommandArgs = [],
25
+ evidence = {},
26
+ }) {
27
+ const normalizedCommandArgs = Array.isArray(commandArgs) ? commandArgs.map((item) => String(item)) : [];
28
+ const normalizedApplyCommandArgs = Array.isArray(applyCommandArgs) ? applyCommandArgs.map((item) => String(item)) : [];
29
+ return {
30
+ id,
31
+ priority,
32
+ category,
33
+ title,
34
+ rationale,
35
+ command: command || (normalizedCommandArgs.length > 0 ? commandFromArgs(normalizedCommandArgs) : ""),
36
+ commandArgs: normalizedCommandArgs,
37
+ applyCommand: applyCommand || (normalizedApplyCommandArgs.length > 0 ? commandFromArgs(normalizedApplyCommandArgs) : ""),
38
+ applyCommandArgs: normalizedApplyCommandArgs,
39
+ evidence,
40
+ };
41
+ }
42
+
43
+ export function agentDevelopmentStatus(actions) {
44
+ if ((actions || []).some((item) => item.priority === "p0")) return "fail";
45
+ if ((actions || []).some((item) => item.priority === "p1")) return "warn";
46
+ return "pass";
47
+ }
48
+
49
+ export function buildAgentBacklogActionPlan({ actions = [], commands = {}, privacy = {} } = {}) {
50
+ const agentBacklogJsonArgs = Array.isArray(commands.agentBacklogJsonArgs) && commands.agentBacklogJsonArgs.length > 0
51
+ ? commands.agentBacklogJsonArgs
52
+ : ["design-ai", "learn", "--agent-backlog", "--strict", "--json"];
53
+ const steps = actions.map((action, index) => {
54
+ const commandArgs = Array.isArray(action.commandArgs) ? action.commandArgs.map((item) => String(item)) : [];
55
+ const command = String(action.command || (commandArgs.length > 0 ? commandFromArgs(commandArgs) : ""));
56
+ const applyCommandArgs = Array.isArray(action.applyCommandArgs) ? action.applyCommandArgs.map((item) => String(item)) : [];
57
+ const applyCommand = String(action.applyCommand || (applyCommandArgs.length > 0 ? commandFromArgs(applyCommandArgs) : ""));
58
+ const commandSafety = classifyAgentBacklogCommand(command);
59
+ const applyCommandSafety = applyCommand ? classifyAgentBacklogCommand(applyCommand) : null;
60
+ return {
61
+ rank: action.rank ?? index + 1,
62
+ actionId: action.id || "",
63
+ priority: action.priority || "p3",
64
+ category: action.category || "other",
65
+ title: action.title || "",
66
+ command,
67
+ commandArgs,
68
+ applyCommand,
69
+ applyCommandArgs,
70
+ applyCommandSafety,
71
+ applyRequiresReviewBeforeMutation: Boolean(applyCommandSafety?.requiresCleanWorkspace),
72
+ expectedOutcome: action.rationale || "",
73
+ verification: command
74
+ ? applyCommand
75
+ ? [
76
+ "Run the preview command first and inspect the proposed local learning profile changes.",
77
+ "Run the apply command only after operator review confirms the target profile path and mutation scope.",
78
+ "Re-run `design-ai learn --agent-backlog --strict --json` after the apply step to confirm the backlog status improved.",
79
+ ]
80
+ : commandSafety.requiresCleanWorkspace
81
+ ? [
82
+ "Run the command in a clean working tree or disposable workspace, then inspect generated or changed files before committing.",
83
+ "Re-run `design-ai learn --agent-backlog --strict --json` after the step to confirm the backlog status improved.",
84
+ ]
85
+ : [
86
+ "Run the command and inspect the preview/report output before applying any follow-up changes.",
87
+ "Re-run `design-ai learn --agent-backlog --strict --json` after the step to confirm the backlog status improved.",
88
+ ]
89
+ : [
90
+ "Review the action manually, then refresh the agent backlog report.",
91
+ ],
92
+ requiresReviewBeforeMutation: commandSafety.requiresCleanWorkspace,
93
+ commandSafety,
94
+ };
95
+ });
96
+ const safetySummary = summarizeAgentBacklogCommandSafety(steps);
97
+ const executionQueue = buildAgentBacklogExecutionQueue(steps, {
98
+ refreshCommandArgs: agentBacklogJsonArgs,
99
+ });
100
+ const verification = [
101
+ commands.signalsJson
102
+ ? {
103
+ label: "Refresh signal registry JSON",
104
+ command: commands.signalsJson,
105
+ commandArgs: Array.isArray(commands.signalsJsonArgs) ? commands.signalsJsonArgs : [],
106
+ }
107
+ : null,
108
+ commands.signalsReport
109
+ ? {
110
+ label: "Save signal registry Markdown handoff",
111
+ command: commands.signalsReport,
112
+ commandArgs: Array.isArray(commands.signalsReportArgs) ? commands.signalsReportArgs : [],
113
+ }
114
+ : null,
115
+ {
116
+ label: "Gate focused agent backlog",
117
+ ...commandSpec(agentBacklogJsonArgs),
118
+ },
119
+ ].filter(Boolean);
120
+
121
+ return {
122
+ version: 1,
123
+ stepCount: steps.length,
124
+ nextStep: steps[0] || null,
125
+ steps,
126
+ safetySummary,
127
+ executionQueue,
128
+ verification,
129
+ boundaries: {
130
+ reportMutatesProfile: Boolean(privacy.mutatesProfile),
131
+ reportMutatesSkillFiles: Boolean(privacy.mutatesSkillFiles),
132
+ reportCallsExternalAiApis: Boolean(privacy.callsExternalAiApis),
133
+ generatedFromLocalSignals: true,
134
+ },
135
+ };
136
+ }
137
+
138
+ export function buildAgentDevelopmentBacklog({
139
+ audit,
140
+ usage,
141
+ evals,
142
+ checkCapture,
143
+ workspace,
144
+ filePath,
145
+ usageFile,
146
+ signalSource,
147
+ }) {
148
+ const actions = [];
149
+
150
+ if (!audit.exists) {
151
+ const previewCommand = commandSpec(["design-ai", "learn", "--init", "--dry-run", "--file", filePath]);
152
+ const applyCommand = commandSpec(["design-ai", "learn", "--init", "--yes", "--file", filePath]);
153
+ actions.push(agentAction({
154
+ id: "agent-learning-profile-init",
155
+ priority: "p1",
156
+ category: "learning-profile",
157
+ title: "Initialize the local learning profile before agent development review.",
158
+ rationale: "Signal registry output is incomplete until a profile exists.",
159
+ ...previewCommand,
160
+ applyCommand: applyCommand.command,
161
+ applyCommandArgs: applyCommand.commandArgs,
162
+ evidence: {
163
+ profileExists: false,
164
+ },
165
+ }));
166
+ } else if (audit.summary.failures > 0) {
167
+ actions.push(agentAction({
168
+ id: "agent-learning-profile-audit-fix",
169
+ priority: "p0",
170
+ category: "learning-profile",
171
+ title: "Fix learning profile audit failures before using signals as a development gate.",
172
+ rationale: "Audit failures can make learned context selection and downstream signal summaries unreliable.",
173
+ ...commandSpec(["design-ai", "learn", "--audit", "--file", filePath]),
174
+ evidence: {
175
+ failures: audit.summary.failures,
176
+ warnings: audit.summary.warnings,
177
+ },
178
+ }));
179
+ } else if (audit.summary.warnings > 0) {
180
+ actions.push(agentAction({
181
+ id: "agent-learning-profile-audit-review",
182
+ priority: "p1",
183
+ category: "learning-profile",
184
+ title: "Review learning profile audit warnings before promoting agent behavior.",
185
+ rationale: "Warnings are not blockers, but they should be resolved before treating local learning as release evidence.",
186
+ ...commandSpec(["design-ai", "learn", "--audit", "--file", filePath]),
187
+ evidence: {
188
+ warnings: audit.summary.warnings,
189
+ },
190
+ }));
191
+ }
192
+
193
+ if (!usage.exists || usage.eventCount === 0) {
194
+ actions.push(agentAction({
195
+ id: "agent-learning-usage-record",
196
+ priority: "p2",
197
+ category: "usage-signals",
198
+ title: "Record prompt or pack usage with learning enabled.",
199
+ rationale: "Usage sidecar events show which learned entries actually affect agent prompts without storing raw brief text.",
200
+ ...commandSpec([
201
+ "env",
202
+ `DESIGN_AI_LEARNING_FILE=${filePath}`,
203
+ `DESIGN_AI_LEARNING_USAGE_FILE=${usageFile}`,
204
+ "design-ai",
205
+ "prompt",
206
+ "audit a design artifact",
207
+ "--with-learning",
208
+ "--json",
209
+ ]),
210
+ evidence: {
211
+ usageExists: Boolean(usage.exists),
212
+ eventCount: usage.eventCount || 0,
213
+ },
214
+ }));
215
+ }
216
+ if (usage.staleSelectedEntryCount > 0) {
217
+ actions.push(agentAction({
218
+ id: "agent-learning-usage-stale-review",
219
+ priority: "p1",
220
+ category: "usage-signals",
221
+ title: "Review stale selected learning ids in the usage sidecar.",
222
+ rationale: "Stale ids indicate usage evidence is no longer aligned with the active profile.",
223
+ ...commandSpec(["design-ai", "learn", "--usage", "--file", filePath, "--usage-file", usageFile]),
224
+ evidence: {
225
+ staleSelectedEntryCount: usage.staleSelectedEntryCount,
226
+ },
227
+ }));
228
+ }
229
+
230
+ if (evals.files.length === 0) {
231
+ const evalOutputFile = defaultLearningEvalPath(filePath);
232
+ actions.push(agentAction({
233
+ id: "agent-eval-checkpoint-generate",
234
+ priority: "p1",
235
+ category: "eval-harness",
236
+ title: "Generate and run route, prompt, pack, or learning eval checkpoints.",
237
+ rationale: "Agent development needs replayable checkpoints before signal registry output can act as a gate.",
238
+ ...commandSpec(["design-ai", "learn", "--eval-template", "--file", filePath, "--json", "--out", evalOutputFile]),
239
+ evidence: {
240
+ evalSignalCount: 0,
241
+ evalOutputFile,
242
+ },
243
+ }));
244
+ }
245
+ if (evals.failed > 0) {
246
+ actions.push(agentAction({
247
+ id: "agent-eval-failure-review",
248
+ priority: "p0",
249
+ category: "eval-harness",
250
+ title: "Fix failing eval signal reports before continuing agent development.",
251
+ rationale: "Failed checkpoints are the strongest deterministic signal that route, prompt, pack, or learning behavior drifted.",
252
+ ...commandSpec(["design-ai", "learn", "--signals", "--from-file", signalSource || ".", "--file", filePath, "--usage-file", usageFile, "--json"]),
253
+ evidence: {
254
+ failed: evals.failed,
255
+ warned: evals.warned,
256
+ },
257
+ }));
258
+ } else if (evals.templates > 0) {
259
+ const templateFile = Array.isArray(evals.templateFiles) && evals.templateFiles.length > 0
260
+ ? evals.templateFiles[0].file
261
+ : "";
262
+ const evalReportFile = templateFile ? evalReportPathForTemplate(templateFile) : defaultLearningEvalReportPath(filePath);
263
+ actions.push(agentAction({
264
+ id: "agent-eval-template-replay",
265
+ priority: "p1",
266
+ category: "eval-harness",
267
+ title: "Replay template-only eval signal files as executed reports.",
268
+ rationale: "Templates are useful setup artifacts, but executed reports provide stronger evidence for agent behavior.",
269
+ ...commandSpec([
270
+ "design-ai",
271
+ "learn",
272
+ "--eval",
273
+ "--from-file",
274
+ templateFile || defaultLearningEvalPath(filePath),
275
+ "--file",
276
+ filePath,
277
+ "--strict",
278
+ "--json",
279
+ "--out",
280
+ evalReportFile,
281
+ ]),
282
+ evidence: {
283
+ templates: evals.templates,
284
+ templateFile: templateFile || defaultLearningEvalPath(filePath),
285
+ evalReportFile,
286
+ },
287
+ }));
288
+ }
289
+
290
+ if (checkCapture.count > 0) {
291
+ actions.push(agentAction({
292
+ id: "agent-skill-proposal-preview",
293
+ priority: "p2",
294
+ category: "skill-evolution",
295
+ title: "Preview skill instruction deltas from repeated check-capture signals.",
296
+ rationale: "Captured warn/fail check results can become deterministic skill improvements without mutating skill files automatically.",
297
+ ...commandSpec(["design-ai", "learn", "--propose-skills", "--from-file", signalSource || ".", "--file", filePath, "--usage-file", usageFile, "--json"]),
298
+ evidence: {
299
+ checkCaptureCount: checkCapture.count,
300
+ categoryCounts: checkCapture.categoryCounts,
301
+ },
302
+ }));
303
+ }
304
+
305
+ if (workspace.nextActionCounts.fail > 0 || workspace.nextActionCounts.warn > 0) {
306
+ actions.push(agentAction({
307
+ id: "agent-workspace-readiness-review",
308
+ priority: workspace.nextActionCounts.fail > 0 ? "p0" : "p1",
309
+ category: "workspace-readiness",
310
+ title: "Resolve workspace readiness actions before treating agent development evidence as complete.",
311
+ rationale: "Workspace readiness joins git, repository metadata, learning, usage, and eval freshness into the operator handoff gate.",
312
+ ...commandSpec(["design-ai", "workspace", "--learning-file", filePath, "--learning-usage", usageFile, "--strict", "--json"]),
313
+ evidence: {
314
+ fail: workspace.nextActionCounts.fail || 0,
315
+ warn: workspace.nextActionCounts.warn || 0,
316
+ nextActionCount: workspace.nextActionCount || 0,
317
+ },
318
+ }));
319
+ }
320
+
321
+ const priorityOrder = { p0: 0, p1: 1, p2: 2, p3: 3 };
322
+ const actionOrder = {
323
+ "agent-learning-profile-audit-fix": 0,
324
+ "agent-learning-profile-init": 1,
325
+ "agent-learning-profile-audit-review": 2,
326
+ "agent-learning-usage-stale-review": 3,
327
+ "agent-workspace-readiness-review": 4,
328
+ "agent-eval-failure-review": 5,
329
+ "agent-eval-checkpoint-generate": 6,
330
+ "agent-eval-template-replay": 7,
331
+ "agent-learning-usage-record": 8,
332
+ "agent-skill-proposal-preview": 9,
333
+ };
334
+ const sortedActions = actions
335
+ .sort((a, b) => (
336
+ priorityOrder[a.priority] - priorityOrder[b.priority]
337
+ || (actionOrder[a.id] ?? 100) - (actionOrder[b.id] ?? 100)
338
+ || a.category.localeCompare(b.category)
339
+ || a.id.localeCompare(b.id)
340
+ ))
341
+ .map((item, index) => ({
342
+ rank: index + 1,
343
+ ...item,
344
+ }));
345
+
346
+ return {
347
+ status: agentDevelopmentStatus(sortedActions),
348
+ actionCount: sortedActions.length,
349
+ p0Count: sortedActions.filter((item) => item.priority === "p0").length,
350
+ p1Count: sortedActions.filter((item) => item.priority === "p1").length,
351
+ p2Count: sortedActions.filter((item) => item.priority === "p2").length,
352
+ p3Count: sortedActions.filter((item) => item.priority === "p3").length,
353
+ actions: sortedActions,
354
+ privacy: {
355
+ mutatesProfile: false,
356
+ mutatesSkillFiles: false,
357
+ callsExternalAiApis: false,
358
+ storesRawBriefText: false,
359
+ },
360
+ };
361
+ }
@@ -0,0 +1,176 @@
1
+ // Eval signal file discovery and summarization for the learning signal registry.
2
+
3
+ import { existsSync, readFileSync, statSync } from "node:fs";
4
+ import path from "node:path";
5
+
6
+ import { defaultLearningFile } from "./learn.mjs";
7
+ import { worstStatus } from "./signals-shared.mjs";
8
+ import { defaultLearningEvalPath } from "./workspace.mjs";
9
+
10
+ export const DEFAULT_SIGNAL_EVAL_FILES = [
11
+ "route-eval.json",
12
+ "route-eval-report.json",
13
+ "prompt-eval.json",
14
+ "prompt-eval-report.json",
15
+ "pack-eval.json",
16
+ "pack-eval-report.json",
17
+ "learning-eval.json",
18
+ "learning-eval-report.json",
19
+ ];
20
+ export function inferSignalKind(payload, filePath = "") {
21
+ const sourceName = path.basename(filePath).toLowerCase();
22
+ const cases = Array.isArray(payload?.cases) ? payload.cases : [];
23
+
24
+ if (sourceName.includes("route")) return "route-eval";
25
+ if (sourceName.includes("prompt")) return "prompt-eval";
26
+ if (sourceName.includes("pack")) return "pack-eval";
27
+ if (sourceName.includes("learning")) return "learning-eval";
28
+
29
+ if (payload?.sourceRouteVersion) return "route-eval";
30
+ if (payload?.sourcePromptVersion) return "prompt-eval";
31
+ if (payload?.sourcePackVersion) return "pack-eval";
32
+ if (payload?.sourceProfile) return "learning-eval";
33
+ if (cases.some((item) => item && typeof item === "object" && "topRouteId" in item)) return "route-eval";
34
+ if (cases.some((item) => item && typeof item === "object" && "missingPromptFragments" in item)) return "prompt-eval";
35
+ if (cases.some((item) => item && typeof item === "object" && ("contextStatus" in item || "pack" in item))) return "pack-eval";
36
+ if (cases.some((item) => item && typeof item === "object" && ("selectedEntryIds" in item || "expectedSelectedIds" in item))) return "learning-eval";
37
+
38
+ return "unknown-eval";
39
+ }
40
+
41
+ export function summarizeCaseCounts(payload) {
42
+ const summary = payload?.summary && typeof payload.summary === "object" ? payload.summary : null;
43
+ const cases = Array.isArray(payload?.cases) ? payload.cases : [];
44
+ if (summary) {
45
+ return {
46
+ caseCount: Number.isInteger(summary.total) ? summary.total : cases.length,
47
+ passed: Number.isInteger(summary.pass) ? summary.pass : 0,
48
+ warned: Number.isInteger(summary.warn) ? summary.warn : 0,
49
+ failed: Number.isInteger(summary.fail) ? summary.fail : 0,
50
+ };
51
+ }
52
+
53
+ return {
54
+ caseCount: cases.length,
55
+ passed: cases.filter((item) => item?.status === "pass").length,
56
+ warned: cases.filter((item) => item?.status === "warn").length,
57
+ failed: cases.filter((item) => item?.status === "fail").length,
58
+ };
59
+ }
60
+
61
+ export function summarizeSignalEvalFile(filePath) {
62
+ const resolvedFile = path.resolve(filePath);
63
+ if (!existsSync(resolvedFile)) {
64
+ return {
65
+ file: resolvedFile,
66
+ exists: false,
67
+ kind: inferSignalKind(null, resolvedFile),
68
+ shape: "missing",
69
+ status: "missing",
70
+ caseCount: 0,
71
+ passed: 0,
72
+ warned: 0,
73
+ failed: 0,
74
+ generatedAt: "",
75
+ error: "",
76
+ };
77
+ }
78
+
79
+ let payload = null;
80
+ try {
81
+ payload = JSON.parse(readFileSync(resolvedFile, "utf8"));
82
+ } catch {
83
+ return {
84
+ file: resolvedFile,
85
+ exists: true,
86
+ kind: inferSignalKind(null, resolvedFile),
87
+ shape: "invalid-json",
88
+ status: "fail",
89
+ caseCount: 0,
90
+ passed: 0,
91
+ warned: 0,
92
+ failed: 0,
93
+ generatedAt: "",
94
+ error: "Signal eval file is not valid JSON.",
95
+ };
96
+ }
97
+
98
+ const hasCases = Array.isArray(payload?.cases);
99
+ const isReport = typeof payload?.status === "string" || Boolean(payload?.summary);
100
+ const counts = summarizeCaseCounts(payload);
101
+ const status = isReport
102
+ ? String(payload.status || worstStatus([
103
+ counts.failed > 0 ? "fail" : "",
104
+ counts.warned > 0 ? "warn" : "",
105
+ "pass",
106
+ ])).trim()
107
+ : "template";
108
+
109
+ return {
110
+ file: resolvedFile,
111
+ exists: true,
112
+ kind: inferSignalKind(payload, resolvedFile),
113
+ shape: isReport ? "report" : "template",
114
+ status: hasCases ? status : "fail",
115
+ caseCount: counts.caseCount,
116
+ passed: counts.passed,
117
+ warned: counts.warned,
118
+ failed: counts.failed,
119
+ generatedAt: String(payload?.generatedAt || ""),
120
+ error: hasCases ? "" : "Signal eval file must include a cases array.",
121
+ };
122
+ }
123
+
124
+ export function resolveSignalFiles({ signalSource = "", root = process.cwd(), extraFiles = [] } = {}) {
125
+ const resolvedSource = signalSource ? path.resolve(signalSource) : path.resolve(root);
126
+ const resolvedExtraFiles = (Array.isArray(extraFiles) ? extraFiles : [])
127
+ .filter(Boolean)
128
+ .map((filePath) => path.resolve(filePath))
129
+ .filter((filePath) => existsSync(filePath));
130
+ const uniqueFiles = (files = []) => [...new Set([...files, ...resolvedExtraFiles])];
131
+ if (existsSync(resolvedSource) && statSync(resolvedSource).isDirectory()) {
132
+ return uniqueFiles(DEFAULT_SIGNAL_EVAL_FILES
133
+ .map((fileName) => path.join(resolvedSource, fileName))
134
+ .filter((filePath) => existsSync(filePath)));
135
+ }
136
+ if (signalSource) return uniqueFiles([resolvedSource]);
137
+ return uniqueFiles([]);
138
+ }
139
+
140
+ export function evalReportPathForTemplate(filePath = "") {
141
+ const resolvedFile = path.resolve(filePath);
142
+ const dir = path.dirname(resolvedFile);
143
+ const ext = path.extname(resolvedFile);
144
+ const base = path.basename(resolvedFile, ext);
145
+ if (base.endsWith("-report")) return resolvedFile;
146
+ if (base.endsWith("-eval")) return path.join(dir, `${base}-report${ext || ".json"}`);
147
+ return path.join(dir, `${base}-report${ext || ".json"}`);
148
+ }
149
+
150
+ export function defaultLearningEvalReportPath(filePath = defaultLearningFile()) {
151
+ return evalReportPathForTemplate(defaultLearningEvalPath(filePath));
152
+ }
153
+
154
+ export function evalSignalEvidenceKey(file = {}) {
155
+ return `${file.kind || "unknown-eval"}\n${path.dirname(file.file || "")}`;
156
+ }
157
+
158
+ export function summarizeEvalSignals(evalFiles = []) {
159
+ const reportKeys = new Set(
160
+ evalFiles
161
+ .filter((item) => item.shape === "report")
162
+ .map((item) => evalSignalEvidenceKey(item)),
163
+ );
164
+ const unresolvedTemplates = evalFiles.filter((item) => (
165
+ item.shape === "template" && !reportKeys.has(evalSignalEvidenceKey(item))
166
+ ));
167
+ return {
168
+ reports: evalFiles.filter((item) => item.shape === "report").length,
169
+ templates: unresolvedTemplates.length,
170
+ rawTemplates: evalFiles.filter((item) => item.shape === "template").length,
171
+ templateFiles: unresolvedTemplates,
172
+ failed: evalFiles.filter((item) => item.status === "fail").length,
173
+ warned: evalFiles.filter((item) => item.status === "warn").length,
174
+ passed: evalFiles.filter((item) => item.status === "pass").length,
175
+ };
176
+ }